• 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 canvas
        • npm i 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; // lebar panel
    	  var dh = 439; // tinggi panel
    	  var xl = 4;   // faktor pengali utk memperbesar image
    
          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*xl, dh*xl);
    	  var x2 = c2.getContext('2d');
    	  //x2.filter = 'grayscale(1) invert(1)';
    	  x2.drawImage(img, 447,207,dw,dh, 0,0,dw*xl,dh*xl);
    
    	  // 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*xl, dh*xl);
    	  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.

    Detail ukuran image untuk dipotong-potong.

    Baris 27 mendownload file image.

    Baris 28 callback sesudah image di download yang isinya membaca file image hasil download.

    Kaya’nya selebihnya bisa dibaca sendiri di source-codenya.

    Ini saya kasih contoh hasilnya, memang agak kurang bagus, mungkin karena 1 panel ukuran tulisannya beda-beda.

    Downloading http://localhost/infografis.png
    
    
    SITUASI VAKSINASI COVID-19
    6 Maret 2021
    Update 14:00 wlB
    
    
    
    TargetSasaranVaksinasi
    40.349.051
    Tahapl
    SDM Kesehatan 1 .468.764
    Tahap II
    Petugas Publik 17.327.169
    Lansia 21.553.118
    
    
    
    Status Vaksinasi
    
    Vaksinasi-1 Vaksinasi-2
    2,552,265 l,v3o,52q
    
    Tahapl 1 2
    
    SDM Kesehatan 2.042.267 1.135.369
    Sudah Divaksin 1.741.784 1.126.353
    Tunda 300.483 9.016
    Tahapll 1 2
    
    PetugasPublik 629.157 4.179
    Sudah Divaksin 604.456 4.169
    Tunda 24.701 10
    Lansia 211.350 2
    Sudah Divaksin 206.025 2
    _unda 5.325 o
    
    
    
    CakupanVaksinasi
    (sudan d_aksinttarget sasaran vaksinasi)
    Vaksinasi-1 Vaksinasi-2
    6.33°o 2.80°/
    / o
    
    Tahapl 1 2
    
    SDMKesehatan 118.59% 76.69°/
    o
    
    Tahap II 1 2
    
    Petugas Publik 3.49% o.oo°/
    o
    Lansia 0.96% O.OO°/
    o
    

    Udah ah. That’s all folks.