Bài viết này tôi xin trình bày kỹ thuật nhận dạng chữ viết tay bằng phương pháp phân tích đường đi của nét chữ(stroke based):
Bài viết này hy vọng sẽ giúp các bạn có được cách nhìn tích cực về cách thức nhận dạng chữ viết tay.
Một phương pháp khác là dùng PCA,các bạn xem topic sau
Thuật toán PCA trong vấn đề nhận dạng ảnh
NNLT tôi dùng là C# trên nền Console cho dễ minh họa,các bạn có thể áp dụng cho các nnlt khác 1 cách tương đương về ý tưởng.
Quá trình nhận dạng cần trải qua các bước sau đây:
1/Chuyển ảnh về ma trận nhị phân:
Điều này khá dễ dàng vì ảnh bitmap đều được phân tích ra 3 màu là RGB,tư đó các màu đậm đưa vào matrix là số 1,màu trắng là số 0
Chuyển về ma trận điểm ảnh nhị phân :
Mã:
img = new Bitmap("D:/test.png");for (int i = 0; i < img.Width; i++){ for (int j = 0; j < img.Height; j++) { if (img.GetPixel(j, i).A.ToString() == "255" && img.GetPixel(j, i).B.ToString() == "255" && img.GetPixel(j, i).G.ToString() == "255" && img.GetPixel(j, i).R.ToString() == "255") { bitstr = bitstr + "0"; } else { bitstr = bitstr + "1"; } } bitstr = bitstr + "
";}
bitstr chính là 1 chuỗi chứa toàn bộ các pixel của ảnh bitmap.Các bạn nên dùng các ảnh 50x50 là được.Vượt quá nên
resize lại.Sau đó chưa vào mảng M có size là 50x50
2/Khử nhiễu :
Bạn dùng thuật toán loang để xác định diện tích vùng lớn nhất,sau đó lấy điểm trung tâm của vùng diện tích lớn của khu vực:
Mã:
bool b=true;while (b){ if(A[foo, bar].ToString() == "0" || A[foo + level, bar].ToString() == "0" || A[foo - level, bar].ToString() == "0" || A[foo, bar + level].ToString() == "0" || A[foo, bar - level].ToString() == "0" || A[foo + level, bar + level].ToString() == "0" || A[foo - level, bar - level].ToString() == "0" || A[foo + level, bar - level].ToString() == "0" || A[foo - level, bar + level].ToString() == "0"){ break; // lọc các điểm là số 1 và có bao bọc } level++;}
Kết quả thu được như sau:
3/lọc và chuyển về dạng đỉnh đồ thị rời rạc :
Mã:
int l = 2;for (int foo = 1; foo <= 48; foo++){ for (int bar = 1; bar <= 48; bar++) { if (M[foo, bar] == 1) { int k = 1; while (k <= l) { M[foo + k, bar] = 0; M[foo, bar + k] = 0; M[foo - k, bar] = 0; M[foo, bar - k] = 0; M[foo + k, bar + k] = 0; M[foo + k, bar - k] = 0; M[foo - k, bar + k] = 0; M[foo - k, bar - k] = 0; k++; } } }}
kết quả thu được :
4/Đánh dấu các đỉnh để dễ phục vụ cho các bước sau :
5/Xây dựng các vectơ từ các node đến các node của đồ thị :
điểm này cần chú ý như sau :
1 đồ thị đã được phân tích ở bước 3 sẽ tạo ra các vecto có hướng đi đến các đỉnh còn lại.
Tùy vào đường đi của các đỉnh mà ta có được cách nhận dạng.
Dựa vào đó ta sẽ xác định giữa 2 nút với nhau tạo ra các vecto nào.
Phân tích các đỉnh kề liên thông trực tiếp với nhau theo vecto nào.Ở đây ta lấy đỉnh kề gần nhất.
Xác định điểm kề gần nhất:
Qua hình ta thấy nút số 1 sẽ gần số 4 nhất.
Vấn đề tiếp theo là xác định vecto từ đỉnh đến đỉnh là vectơ nào :
Dựa vào đó ta sẽ viết hàm lọc tiếp các đỉnh kề có cùng vecto (nếu không làm bước này thì kết quả phân tích có khác chút nhưng vẫn có thể làm được.Tuy nhiên làm thế này để đơn giản hóa vấn đề hơn)
VIết đầu ra tổng hợp
Cái này viết khá khổ vì bạn cần phải tổng hợp dữ liệu liên tục và viết 1 cách kỹ lưỡng và test đầy đủ các trường hợp :
Bạn sẽ thấy 1 điều rằng,các số 0 6 8 9 sẽ có hình thức gần như sau,các vecto 2,3,4,5 sẽ xuất hiện nhiều lần
,ta khoanh vùng lại để viết hàm.Nếu nằm trong 1 khoảng nào đó thì là số 8.Có thể thấy số 8 có 4 đường cong,ta
lại duyệt từ trái sang phải,từ trên xuống dưới nên vectơ 2,3,4 xuất hiện rất cao,thứ 2 là số 6 và 9,sau là
số 0 ...
các vectơ 1 8 sẽ xuất hiện nhiều trong các số 1 7 ..
từ đó ta sẽ viết hàm liên thuộc,hàm này trả về kết quả gần đúng,từ đó ta suy ra kết quả dự đoán.
Tham khảo : Handwritten Digits Recognition của tác giả Gaurav Jain, Jason Ko
View more random threads:
Thông qua phân tích dữ liệu Google từ 86 quốc gia, mới đây, một công ty tại Anh đã công bố bảng xếp hạng kích tấc "cậu nhỏ" của các nước trên thế giới. Kết quả, hầu hết các nước xếp ở nhóm đầu của...
"Chim" của chàng trai Việt thuộc...