API Nhận dạng chữ viết tay cho phép bạn nhận dạng văn bản từ dữ liệu đầu vào viết tay ngay khi dữ liệu đó xuất hiện.
Handwriting Recognition API (API Nhận dạng chữ viết tay) là gì?
API Nhận dạng chữ viết tay cho phép bạn chuyển đổi chữ viết tay (mực) của người dùng thành văn bản. Một số hệ điều hành từ lâu đã có các API như vậy và với khả năng mới này, cuối cùng ứng dụng web của bạn cũng có thể sử dụng chức năng này. Quá trình chuyển đổi diễn ra ngay trên thiết bị của người dùng, hoạt động ngay cả khi ở chế độ ngoại tuyến, mà không cần thêm bất kỳ thư viện hoặc dịch vụ nào của bên thứ ba.
API này triển khai tính năng nhận dạng được gọi là "trực tuyến" hoặc gần như theo thời gian thực. Điều này có nghĩa là dữ liệu đầu vào viết tay được nhận dạng trong khi người dùng đang vẽ bằng cách chụp và phân tích từng nét vẽ. Trái ngược với các quy trình "ngoại tuyến" như Nhận dạng ký tự quang học (OCR), trong đó chỉ biết sản phẩm cuối cùng, các thuật toán trực tuyến có thể cung cấp độ chính xác cao hơn do có thêm các tín hiệu như trình tự thời gian và áp lực của từng nét mực.
Các trường hợp sử dụng được đề xuất cho API Nhận dạng chữ viết tay
Sau đây là một số ví dụ về cách sử dụng:
- Ứng dụng ghi chú mà người dùng muốn chụp ảnh ghi chú viết tay và dịch chúng thành văn bản.
- Các ứng dụng biểu mẫu mà người dùng có thể sử dụng bút hoặc nhập bằng ngón tay do hạn chế về thời gian.
- Trò chơi yêu cầu điền chữ cái hoặc số, chẳng hạn như ô chữ, trò chơi treo cổ hoặc sudoku.
Trạng thái hiện tại
Handwriting Recognition API (API Nhận dạng chữ viết tay) có trong (Chromium 99).
Cách sử dụng API Nhận dạng chữ viết tay
Phát hiện tính năng
Phát hiện tính năng hỗ trợ trình duyệt bằng cách kiểm tra sự tồn tại của phương thức createHandwritingRecognizer()
trên đối tượng trình điều hướng:
if ('createHandwritingRecognizer' in navigator) {
// 🎉 The Handwriting Recognition API is supported!
}
Các khái niệm cốt lõi
API Nhận dạng chữ viết tay chuyển đổi dữ liệu đầu vào viết tay thành văn bản, bất kể phương thức nhập (chuột, cảm ứng, bút). API này có 4 thực thể chính:
- Điểm đại diện cho vị trí con trỏ tại một thời điểm cụ thể.
- Một nét vẽ bao gồm một hoặc nhiều điểm. Quá trình ghi lại một nét vẽ bắt đầu khi người dùng đặt con trỏ xuống (tức là nhấp vào nút chuột chính hoặc chạm vào màn hình bằng bút hoặc ngón tay) và kết thúc khi họ nâng con trỏ lên.
- Một bản vẽ bao gồm một hoặc nhiều nét vẽ. Quá trình nhận dạng thực tế diễn ra ở cấp này.
- Trình nhận dạng được định cấu hình bằng ngôn ngữ nhập dự kiến. Phương thức này dùng để tạo một thực thể của bản vẽ có áp dụng cấu hình trình nhận dạng.
Các khái niệm này được triển khai dưới dạng giao diện và từ điển cụ thể. Tôi sẽ trình bày về các khái niệm này trong phần sau.
Tạo trình nhận dạng
Để nhận dạng văn bản từ dữ liệu đầu vào viết tay, bạn cần lấy một thực thể của HandwritingRecognizer
bằng cách gọi navigator.createHandwritingRecognizer()
và truyền các quy tắc ràng buộc vào thực thể đó. Các quy tắc ràng buộc xác định mô hình nhận dạng chữ viết tay cần sử dụng. Hiện tại, bạn có thể chỉ định danh sách ngôn ngữ theo thứ tự ưu tiên:
const recognizer = await navigator.createHandwritingRecognizer({
languages: ['en'],
});
Phương thức này trả về một lời hứa phân giải bằng một thực thể của HandwritingRecognizer
khi trình duyệt có thể thực hiện yêu cầu của bạn. Nếu không, hệ thống sẽ từ chối lời hứa kèm theo lỗi và tính năng nhận dạng chữ viết tay sẽ không hoạt động. Vì lý do này, trước tiên, bạn nên truy vấn tính năng hỗ trợ của trình nhận dạng cho các tính năng nhận dạng cụ thể.
Truy vấn tính năng hỗ trợ trình nhận dạng
Bằng cách gọi navigator.queryHandwritingRecognizerSupport()
, bạn có thể kiểm tra xem nền tảng mục tiêu có hỗ trợ các tính năng nhận dạng chữ viết tay mà bạn dự định sử dụng hay không. Trong ví dụ sau, nhà phát triển:
- muốn phát hiện văn bản bằng tiếng Anh
- nhận được các dự đoán thay thế, ít có khả năng xảy ra hơn (nếu có)
- có quyền truy cập vào kết quả phân đoạn, tức là các ký tự được nhận dạng, bao gồm cả các điểm và nét tạo nên các ký tự đó
const { languages, alternatives, segmentationResults } =
await navigator.queryHandwritingRecognizerSupport({
languages: ['en'],
alternatives: true,
segmentationResult: true,
});
console.log(languages); // true or false
console.log(alternatives); // true or false
console.log(segmentationResult); // true or false
Phương thức này trả về một lời hứa phân giải bằng đối tượng kết quả. Nếu trình duyệt hỗ trợ tính năng mà nhà phát triển chỉ định, thì giá trị của tính năng đó sẽ được đặt thành true
. Nếu không, giá trị này sẽ được đặt thành false
.
Bạn có thể sử dụng thông tin này để bật hoặc tắt một số tính năng nhất định trong ứng dụng, hoặc để điều chỉnh truy vấn và gửi một truy vấn mới.
Bắt đầu vẽ
Trong ứng dụng, bạn nên cung cấp một khu vực nhập dữ liệu để người dùng nhập dữ liệu bằng chữ viết tay. Vì lý do hiệu suất, bạn nên triển khai tính năng này bằng cách sử dụng đối tượng canvas. Việc triển khai chính xác phần này nằm ngoài phạm vi của bài viết này, nhưng bạn có thể tham khảo bản minh hoạ để xem cách thực hiện.
Để bắt đầu một bản vẽ mới, hãy gọi phương thức startDrawing()
trên trình nhận dạng. Phương thức này lấy một đối tượng chứa nhiều gợi ý để tinh chỉnh thuật toán nhận dạng. Tất cả gợi ý đều không bắt buộc:
- Loại văn bản đang được nhập: văn bản, địa chỉ email, số hoặc một ký tự riêng lẻ (
recognitionType
) - Loại thiết bị đầu vào: chuột, cảm ứng hoặc nhập bằng bút (
inputType
) - Văn bản trước đó (
textContext
) - Số lượng dự đoán thay thế ít có khả năng xảy ra hơn cần được trả về (
alternatives
) - Danh sách các ký tự mà người dùng có thể nhận dạng ("ký tự") mà người dùng có nhiều khả năng sẽ nhập
(
graphemeSet
)
API Nhận dạng chữ viết tay hoạt động tốt với Sự kiện con trỏ, cung cấp giao diện trừu tượng để sử dụng dữ liệu đầu vào từ bất kỳ thiết bị trỏ nào. Các đối số sự kiện con trỏ chứa loại con trỏ đang được sử dụng. Điều này có nghĩa là bạn có thể sử dụng các sự kiện con trỏ để tự động xác định loại dữ liệu đầu vào. Trong ví dụ sau, bản vẽ để nhận dạng chữ viết tay sẽ tự động được tạo khi sự kiện pointerdown
xảy ra lần đầu tiên trên khu vực viết tay. Vì pointerType
có thể trống hoặc được đặt thành một giá trị độc quyền, nên tôi đã giới thiệu một tính năng kiểm tra tính nhất quán để đảm bảo chỉ đặt các giá trị được hỗ trợ cho loại dữ liệu đầu vào của bản vẽ.
let drawing;
let activeStroke;
canvas.addEventListener('pointerdown', (event) => {
if (!drawing) {
drawing = recognizer.startDrawing({
recognitionType: 'text', // email, number, per-character
inputType: ['mouse', 'touch', 'pen'].find((type) => type === event.pointerType),
textContext: 'Hello, ',
alternatives: 2,
graphemeSet: ['f', 'i', 'z', 'b', 'u'], // for a fizz buzz entry form
});
}
startStroke(event);
});
Thêm nét vẽ
Sự kiện pointerdown
cũng là nơi thích hợp để bắt đầu một nét vẽ mới. Để làm như vậy, hãy tạo một bản sao mới của HandwritingStroke
. Ngoài ra, bạn nên lưu trữ thời gian hiện tại làm điểm tham chiếu cho các điểm tiếp theo được thêm vào:
function startStroke(event) {
activeStroke = {
stroke: new HandwritingStroke(),
startTime: Date.now(),
};
addPoint(event);
}
Thêm một điểm
Sau khi tạo nét vẽ, bạn nên trực tiếp thêm điểm đầu tiên vào nét vẽ đó. Vì bạn sẽ thêm nhiều điểm hơn sau này, nên bạn nên triển khai logic tạo điểm trong một phương thức riêng. Trong ví dụ sau, phương thức addPoint()
sẽ tính thời gian đã trôi qua từ dấu thời gian tham chiếu.
Thông tin về thời gian là không bắt buộc nhưng có thể cải thiện chất lượng nhận dạng. Sau đó, lớp này sẽ đọc toạ độ X và Y từ sự kiện con trỏ và thêm điểm vào nét vẽ hiện tại.
function addPoint(event) {
const timeElapsed = Date.now() - activeStroke.startTime;
activeStroke.stroke.addPoint({
x: event.offsetX,
y: event.offsetY,
t: timeElapsed,
});
}
Trình xử lý sự kiện pointermove
được gọi khi con trỏ di chuyển trên màn hình. Bạn cũng cần thêm các điểm đó vào nét vẽ. Sự kiện này cũng có thể được kích hoạt nếu con trỏ không ở trạng thái "nhấn xuống", chẳng hạn như khi di chuyển con trỏ trên màn hình mà không nhấn nút chuột. Trình xử lý sự kiện trong ví dụ sau sẽ kiểm tra xem có nét vẽ đang hoạt động hay không và thêm điểm mới vào nét vẽ đó.
canvas.addEventListener('pointermove', (event) => {
if (activeStroke) {
addPoint(event);
}
});
Nhận dạng văn bản
Khi người dùng nhấc con trỏ lên một lần nữa, bạn có thể thêm nét vẽ vào bản vẽ bằng cách gọi phương thức addStroke()
. Ví dụ sau đây cũng đặt lại activeStroke
, vì vậy, trình xử lý pointermove
sẽ không thêm điểm vào nét vẽ đã hoàn tất.
Tiếp theo, đã đến lúc nhận dạng dữ liệu đầu vào của người dùng bằng cách gọi phương thức getPrediction()
trên bản vẽ. Quá trình nhận dạng thường mất chưa đến vài trăm mili giây, vì vậy, bạn có thể chạy lại các dự đoán nếu cần. Ví dụ sau đây chạy một dự đoán mới sau mỗi nét vẽ hoàn tất.
canvas.addEventListener('pointerup', async (event) => {
drawing.addStroke(activeStroke.stroke);
activeStroke = null;
const [mostLikelyPrediction, ...lessLikelyAlternatives] = await drawing.getPrediction();
if (mostLikelyPrediction) {
console.log(mostLikelyPrediction.text);
}
lessLikelyAlternatives?.forEach((alternative) => console.log(alternative.text));
});
Phương thức này trả về một lời hứa sẽ phân giải bằng một mảng các dự đoán được sắp xếp theo khả năng xảy ra. Số lượng phần tử phụ thuộc vào giá trị bạn đã truyền vào gợi ý alternatives
. Bạn có thể sử dụng mảng này để cho người dùng lựa chọn các kết quả trùng khớp có thể có và yêu cầu họ chọn một lựa chọn. Ngoài ra, bạn chỉ cần sử dụng kết quả dự đoán có nhiều khả năng nhất, đó là những gì tôi làm trong ví dụ.
Đối tượng dự đoán chứa văn bản được nhận dạng và kết quả phân đoạn không bắt buộc. Tôi sẽ thảo luận về đối tượng này trong phần sau.
Thông tin chi tiết chi tiết kèm theo kết quả phân đoạn
Nếu được nền tảng mục tiêu hỗ trợ, đối tượng dự đoán cũng có thể chứa kết quả phân đoạn.
Đây là một mảng chứa tất cả các đoạn chữ viết tay được nhận dạng, một tổ hợp của ký tự được nhận dạng mà người dùng có thể xác định (grapheme
) cùng với vị trí của ký tự đó trong văn bản được nhận dạng (beginIndex
, endIndex
) và các nét vẽ và điểm tạo ra ký tự đó.
if (mostLikelyPrediction.segmentationResult) {
mostLikelyPrediction.segmentationResult.forEach(
({ grapheme, beginIndex, endIndex, drawingSegments }) => {
console.log(grapheme, beginIndex, endIndex);
drawingSegments.forEach(({ strokeIndex, beginPointIndex, endPointIndex }) => {
console.log(strokeIndex, beginPointIndex, endPointIndex);
});
},
);
}
Bạn có thể sử dụng thông tin này để theo dõi lại các ký tự được nhận dạng trên canvas.
Nhận dạng hoàn chỉnh
Sau khi quá trình nhận dạng hoàn tất, bạn có thể giải phóng tài nguyên bằng cách gọi phương thức clear()
trên HandwritingDrawing
và phương thức finish()
trên HandwritingRecognizer
:
drawing.clear();
recognizer.finish();
Bản minh hoạ
Thành phần web <handwriting-textarea>
triển khai một chế độ kiểm soát chỉnh sửa được nâng cao dần, có khả năng nhận dạng chữ viết tay. Bằng cách nhấp vào nút ở góc dưới bên phải của thành phần điều khiển chỉnh sửa, bạn sẽ kích hoạt
chế độ vẽ. Khi bạn hoàn tất bản vẽ, thành phần web sẽ tự động bắt đầu quá trình nhận dạng và thêm văn bản đã nhận dạng trở lại vào thành phần điều khiển chỉnh sửa. Nếu API Nhận dạng chữ viết tay không được hỗ trợ hoặc nền tảng không hỗ trợ các tính năng được yêu cầu, thì nút chỉnh sửa sẽ bị ẩn. Tuy nhiên, bạn vẫn có thể sử dụng thành phần điều khiển chỉnh sửa cơ bản dưới dạng <textarea>
.
Thành phần web cung cấp các thuộc tính và thuộc tính để xác định hành vi nhận dạng từ bên ngoài, bao gồm cả languages
và recognitiontype
. Bạn có thể đặt nội dung của thành phần điều khiển thông qua thuộc tính value
:
<handwriting-textarea languages="en" recognitiontype="text" value="Hello"></handwriting-textarea>
Để được thông báo về mọi thay đổi đối với giá trị, bạn có thể theo dõi sự kiện input
.
Bạn có thể thử thành phần này bằng cách sử dụng bản minh hoạ này trên Glitch. Ngoài ra, hãy nhớ xem mã nguồn. Để sử dụng thành phần điều khiển trong ứng dụng, hãy tải thành phần đó từ npm.
Tính bảo mật và quyền truy cập
Nhóm Chromium đã thiết kế và triển khai API Nhận dạng chữ viết tay bằng cách sử dụng các nguyên tắc cốt lõi được xác định trong bài viết Kiểm soát quyền truy cập vào các tính năng mạnh mẽ của nền tảng web, bao gồm cả quyền kiểm soát của người dùng, tính minh bạch và tính công thái học.
Quyền kiểm soát của người dùng
Người dùng không thể tắt API Nhận dạng chữ viết tay. Phương thức này chỉ dành cho các trang web được phân phối qua HTTPS và chỉ có thể được gọi từ ngữ cảnh duyệt web cấp cao nhất.
Sự minh bạch
Không có chỉ báo nào cho biết tính năng nhận dạng chữ viết tay có đang hoạt động hay không. Để ngăn chặn việc tạo vân tay số, trình duyệt sẽ triển khai các biện pháp đối phó, chẳng hạn như hiển thị lời nhắc cấp quyền cho người dùng khi phát hiện hành vi có thể bị lợi dụng.
Quyền ổn định
API Nhận dạng chữ viết tay hiện không hiển thị lời nhắc cấp quyền nào. Do đó, bạn không cần phải duy trì quyền theo bất kỳ cách nào.
Phản hồi
Nhóm Chromium muốn biết trải nghiệm của bạn với API Nhận dạng chữ viết tay.
Giới thiệu cho chúng tôi về thiết kế API
API có hoạt động như mong đợi không? Hay có phương thức hoặc thuộc tính nào bị thiếu mà bạn cần để triển khai ý tưởng của mình không? Bạn có câu hỏi hoặc nhận xét về mô hình bảo mật không? Gửi vấn đề về thông số kỹ thuật trên kho lưu trữ GitHub tương ứng hoặc thêm ý kiến của bạn vào một vấn đề hiện có.
Báo cáo vấn đề về việc triển khai
Bạn có tìm thấy lỗi khi triển khai Chromium không? Hay cách triển khai có khác với thông số kỹ thuật không?
Gửi lỗi tại new.crbug.com. Hãy nhớ cung cấp càng nhiều thông tin chi tiết càng tốt, hướng dẫn đơn giản để tái hiện lỗi và nhập Blink>Handwriting
vào hộp Components (Thành phần).
Glitch rất hữu ích để chia sẻ các bản tái hiện nhanh chóng và dễ dàng.
Hỗ trợ API
Bạn có dự định sử dụng API Nhận dạng chữ viết tay không? Sự ủng hộ công khai của bạn giúp nhóm Chromium ưu tiên các tính năng và cho các nhà cung cấp trình duyệt khác thấy tầm quan trọng của việc hỗ trợ các tính năng đó.
Hãy chia sẻ cách bạn dự định sử dụng công cụ này trên luồng thảo luận Discourse của WICG. Gửi một tweet đến@ChromiumDev bằng hashtag #HandwritingRecognition
và cho chúng tôi biết bạn đang sử dụng ở đâu và như thế nào.
Liên kết Hữu ích
- Giải thích
- Bản nháp thông số kỹ thuật
- Kho lưu trữ GitHub
- ChromeStatus
- Lỗi Chromium
- Xem xét thẻ
- Ý định tạo nguyên mẫu
- Luồng WebKit-Dev
- Quan điểm của Mozilla về tiêu chuẩn
Lời cảm ơn
Tài liệu này do Joe Medley, Honglin Yu và Jiewei Qian xem xét.