I> Giới thiệu:

Để viết một ứng dụng Android có khả năng nhận biết các ký tự trong văn bản theo bản thân mình tưởng tượng dường như rất khó khăn và có vẻ cao siêu. Tuy nhiên, sau khi tìm hiểu một vài thông tin, mình nhận thấy Tesseract hoàn toàn có thể đơn giản hóa ý tưởng của mình. Mình sẽ sử dụng thư viện Tess Two dành cho Android được tạo nên bởi Robert Theis trong bài viết này. Tess Two dựa trên công nghệ của Tesseract OCR Engine và thư viện xử lý hình ảnh Leptonica.

II> Các bước cấu hình

Bước 1: Đầu tiên, mình sẽ tải và cài đặt Android NDK vào đường dẫn là “C:”. Sau đó mình sẽ cấu hình biến môi trường cho máy trỏ tới thư mục NDK và khởi động lại PC để đảm bảo việc cấu hình được hoàn tất.

Bước 2: Tải “tess-two-master” từ đây sau đó giải nén và đổi tên là “tess” (mình sẽ đặt nó trong đường dẫn “C:tess”). Truy cập vào đường dẫn “C:tesstess-two” và mở terminal của hệ thống. Mình sẽ sử dụng lệnh “ndk-build” để cấu hình ndk cho Tesseract.

Bước 3: Truy cập vào đường dẫn “C:tess eyes-two” và tiếp tục mở terminal để cấu hình ndk bằng lệnh “ndk-build”. Tiếp đến, mình sẽ dùng lệnh: “android update project –target 1 –path C:tesstess-two

Bước 4: Tiếp tục dùng lệnh: “ant release” và đảm bảo biến môi trường “JAVAHOME” đã được cấu hình đúng. Sau khi quá trình hoàn tất, thư viện “tess-two” sẽ được sinh ra và mình sẽ tích hợp thư việc này vào project demo.

Bước 5: Sau khi đã cấu hình thành công, mình sẽ vào đây để lựa chọn ngôn ngữ muốn nhận dạng. File ngôn ngữ sẽ có đuôi “*.traineddata” và phải được đặt trong đường dẫn “~assetstessdata” của project. Done! Bây giờ mình sẽ đi code!

III> Viết ứng dụng demo

Đầu tiên, mình sẽ khai báo thông tin cho file ngôn ngữ cần nhận dạng:

public static final String DATA_PATH = Environment.getExternalStorageDirectory().toString() + "/DemoAndroid/";
…
String paths = new String { DATA_PATH, DATA_PATH + "tessdata/" };

		for (String path : paths) {
			File dir = new File(path);
			if (!dir.exists()) {
				if (!dir.mkdirs()) {
					Log.v(TAG, "ERROR: Creation of directory " + path + " on sdcard failed");
					return;
				} else {
					Log.v(TAG, "Created directory " + path + " on sdcard");
				}
			}

		}
…
if (!(new File(DATA_PATH + "tessdata/" + lang + ".traineddata")).exists()) {
			try {
				AssetManager assetManager = getAssets();
				InputStream in = assetManager.open("tessdata/" + lang + ".traineddata");
				OutputStream out = new FileOutputStream(DATA_PATH
						+ "tessdata/" + lang + ".traineddata");

				// Transfer bytes from in to out
				byte buf = new byte[1024];
				int len;
				//while ((lenf = gin.read(buff)) > 0) {
				while ((len = in.read(buf)) > 0) {
					out.write(buf, 0, len);
				}
				in.close();
				//gin.close();
				out.close();
				
				Log.v(TAG, "Copied " + lang + " traineddata");
			} catch (IOException e) {
				Log.e(TAG, "Was unable to copy " + lang + " traineddata " + e.toString());
			}
		}
...

Tiếp đến, mình sẽ chụp một tấm hình cần nhận dạng và chuyển qua dạng bitmap:

…
BitmapFactory.Options options = new BitmapFactory.Options();
		options.inSampleSize = 4;

		Bitmap bitmap = BitmapFactory.decodeFile(_path, options);

		try {
			ExifInterface exif = new ExifInterface(_path);
			int exifOrientation = exif.getAttributeInt(
					ExifInterface.TAG_ORIENTATION,
					ExifInterface.ORIENTATION_NORMAL);

			Log.v(TAG, "Orient: " + exifOrientation);

			int rotate = 0;

			switch (exifOrientation) {
			case ExifInterface.ORIENTATION_ROTATE_90:
				rotate = 90;
				break;
			case ExifInterface.ORIENTATION_ROTATE_180:
				rotate = 180;
				break;
			case ExifInterface.ORIENTATION_ROTATE_270:
				rotate = 270;
				break;
			}

			Log.v(TAG, "Rotation: " + rotate);

			if (rotate != 0) {

				// Getting width & height of the given image.
				int w = bitmap.getWidth();
				int h = bitmap.getHeight();

				// Setting pre rotate
				Matrix mtx = new Matrix();
				mtx.preRotate(rotate);

				// Rotating Bitmap
				bitmap = Bitmap.createBitmap(bitmap, 0, 0, w, h, mtx, false);
			}

			// Convert to ARGB_8888, required by tess
			bitmap = bitmap.copy(Bitmap.Config.ARGB_8888, true);

		} catch (IOException e) {
			Log.e(TAG, "Couldn't correct orientation: " + e.toString());
		}

Sau cùng, mình sẽ sử dụng Tess-two nhận dạng văn bản trong tấm hình đã chụp:

TessBaseAPI baseApi = new TessBaseAPI();
		baseApi.setDebug(true);
		baseApi.init(DATA_PATH, lang);
		baseApi.setImage(bitmap);
		
		String recognizedText = baseApi.getUTF8Text();
		
		baseApi.end();

		Log.v(TAG, "OCRED TEXT: " + recognizedText);

IV> Kết

Hy vọng thông qua bài viết này, các bạn có thể nắm bắt được cách cấu hình và áp dụng “tess-two” để làm một demo về nhận dạng văn bản trên Android.