Komputer,  Programming,  Software

OCR

Beberapa waktu lalu sempet ngasih komentar di twitter atas postingan orang yang isinya protes atas data dari website Kementrian Kesehatan tentang vaksinasi Covid-19. Protes karena data yang disajikan berupa image (gambar) dan menurut orang yg posting data yang disajikan tidak “properly consumable by the public?”

Saya pikir kalau orang gak suka data ditampilkan dalam bentuk image, berarti orang itu mau “mengambil” data itu lalu (mungkin) diolah dan disajikan dalam bentuk lain. Otomatis saya kasih tanggapan “Coba ambil gambarnya, split utk di-OCR utk dapet teksnya”. Jawaban khas “data scraper” 🙂

Eh ternyata orangnya gak suka jawaban saya, dia bales lagi “The point is that the public *don’t* have to do the things you mentioned”. Saya setuju, masyarakat umum (“the public”) gak mungkin harus nge-OCR data dalam bentuk image. “The public” cukup lihat saja langsung image-nya, dan harusnya gak peduli tentang bagaimana data ditampilkan, yang penting kelihatan pake mata. Intinya bagi saya, orang yang peduli dengan bagaimana data ditampilkan bukan bagian dari “the public”.

Terlepas dari perdebatan di atas, jiwa programmer saya terus tergelitik untuk nyoba bikin program yang melakukan OCR ke data vaksinasi covid-19 yang disebut di atas. Langkah pertama yg saya lakukan adalah: cari pake google dengan kata kunci “javascript ocr“.

Yang muncul paling atas tentu saja “Tesseract.js“, proyek tesseract memang ngerajain bidang OCR. Tapi saya mau nyari OCR yg “slim”, yang cukup dengan 1 file js dan sangat mudah digunakan. Dan ternyata apa yg saya mau muncul di halaman pertama juga, namanya Ocrad.js.

Ocrad.js sebenernya bukan benar2 implementasi teknik OCR pake javascript, karena menurut yang buat, Ocrad.js itu hasil konversi program Ocrad yang dibuat pake C++ ke WebAssembly pake Emscripten. Jadi jangan harap bisa “ngintip” cara OCR pake Javascript murni di Ocrad.js.

Oke, langsung aja, ini langkah2 bikin program OCR untuk nge-scrap data image dari Kemenkes tentang vaksinasi covid-19.

  1. Install Node.js
  2. Clone Ocrad.js dari github. Sebenernya yang dibutuhkan cuma 1 file, yaitu file ocrad.js, tapi ada baiknya kalo di-clone semua aja karena ada contoh2nya yang bisa dipelajari.
  3. Buat 1 folder untuk programnya, lalu buat dua file (yang saya sertakan di bawah) di dalam folder tsb. Sebenernya yang paling penting cuma file vaksinasi.js, file package.json cuma untuk mempermudah instalasi node-module yang dibutuhkan.
  4. Buka command prompt, ganti folder aktif ke folder program di atas, terus ketik perintah:
    • Kalau ada file package.json, cukup pake 1 perintah: npm install
    • Kalau tidak ada file package.json ada dua perintah:
      • npm i --save canvas
      • npm i --save request
  5. Sesudah itu tinggal jalankan script-nya: node vaksinasi.js

File: package.json

{
  "name": "vaksinasi-covid19",
  "version": "1.0.0",
  "description": "OCR image data vaksinasi covid-19 dari Kemenkes RI ",
  "main": "vaksinasi.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "author": "Ureh",
  "license": "ISC",
  "dependencies": {
    "canvas": "^2.7.0",
    "request": "^2.88.2"
  }
}

File: vaksinasi.js

var OCRAD = require('./ocrad.js');
var Canvas = require('canvas');
var fs = require('fs');
var request = require('request');

var Image = Canvas.Image;
var png = "https://www.kemkes.go.id/web/assets/images/infografis/vaksin/VAKSINASI-Infografis.png";
png = "http://localhost/infografis.png"; // for local testing

function downloadFile(url, dest, cb) {
  console.log('Downloading %s', url);
  var file = fs.createWriteStream(dest);

  var req = request.get(url);
  req.pipe(file).on('error', function(err) { // Handle errors
    fs.unlink(dest); // Delete the file async. (But we don't check the result)
    if (cb) cb(err.message);
  });

  file.on('finish', function() {
    file.close(cb);  // close() is async, call cb after close completes.
  });
}

console.log('Starting OCR');

downloadFile(png, 'infografis.png', function(){
	fs.readFile(__dirname + '/infografis.png', function(err, src) {
	  if (err) {
		throw err;
	  }

	  var dw = 383;
	  var dh = 439;
	  var xl = 4;
	  var xl2 = 4;

      var img = new Image();
	  img.src = src;

// JUDUL ATAS
	  console.log('\n');
	  var c0 = new Canvas.createCanvas(1280*xl, 195*xl);
	  var x0 = c0.getContext('2d');
	  x0.drawImage(img, 0,0,1280,195, 0,0,1280*xl,195*xl);
	  console.log(OCRAD(c0));

// PANEL KIRI
	  console.log('\n');
	  var c1 = new Canvas.createCanvas(dw*xl, dh*xl);
	  var x1 = c1.getContext('2d');
	  x1.drawImage(img, 35,207,dw,dh, 0,0,dw*xl,dh*xl);
	  console.log(OCRAD(c1));

// PANEL TENGAH
	  console.log('\n');
	  var c2 = new Canvas.createCanvas(dw*xl2, dh*xl2);
	  var x2 = c2.getContext('2d');
	  //x2.filter = 'grayscale(1) invert(1)';
	  x2.drawImage(img, 447,207,dw,dh, 0,0,dw*xl2,dh*xl2);

	  // konversi ke hitam-putih dan di invert
	  // karena text putih gak kebaca
	  // filter di canvas context gak jalan
	  const imgData = x2.getImageData(0, 0, dw*xl2, dh*xl2);
	  for (i = 0; i < imgData.data.length; i += 4) {
	  	let count = imgData.data[i] + imgData.data[i + 1] + imgData.data[i + 2];
		let colour = 255;
		if (count > 500) colour = 0;

		imgData.data[i] = colour;		// red
		imgData.data[i + 1] = colour;	// green
		imgData.data[i + 2] = colour;	// blue
		imgData.data[i + 3] = 255;		// alpha
	  }
	  x2.putImageData(imgData, 0,0);
	  console.log(OCRAD(c2));

// PANEL KANAN
	  console.log('\n');
	  var c3 = new Canvas.createCanvas(dw*xl, dh*xl);
	  var x3 = c3.getContext('2d');
	  x3.drawImage(img, 859,207,dw,dh, 0,0,dw*xl,dh*xl);

	  // konversi ke hitam-putih tdk mempengaruhi output OCR
	  /*
	  const imgData3 = x3.getImageData(0, 0, dw*xl, dh*xl);
	  for (i = 0; i < imgData3.data.length; i += 4) {
	  	let count = imgData3.data[i] + imgData3.data[i + 1] + imgData3.data[i + 2];
		let colour = 0;
		if (count > 300) colour = 255;

		imgData3.data[i] = colour;
		imgData3.data[i + 1] = colour;
		imgData3.data[i + 2] = colour;
		imgData3.data[i + 3] = 255;
	  }
	  x3.putImageData(imgData3, 0,0);
	  */
	  console.log(OCRAD(c3));

	});
});

Sekarang kita bahas programnya, program ini adalah hasil modifikasi dari contoh Ocrad.js.

Baris 1 s.d. 8 adalah inisialisasi variabel, dimana di baris ke-8 saya set ulang variabel png supaya program mengambil gambar dari localhost, karena saya akan mencoba mengulang-ulang program dengan image yang sama biar gak membebani servernya kemenkes.

Baris 10 s.d. 23 adalah fungsi untuk mengunduh file lalu menjalankan callback saat proses pengunduhan selesai. Fungsinya dapet dari codota.com.

Bahasannya segitu dulu aja yah. Lain kali disambung lagi.