You can edit the below JavaScript code to customize the image tool.
Apply Changes
async function processImage(originalImg, confidenceThreshold = "30") {
// Create canvas to process and return the result
const canvas = document.createElement('canvas');
canvas.width = originalImg.width;
canvas.height = originalImg.height;
const ctx = canvas.getContext('2d');
// Fill with white background to handle transaprent background images safely
ctx.fillStyle = '#ffffff';
ctx.fillRect(0, 0, canvas.width, canvas.height);
// Draw original image on top
ctx.drawImage(originalImg, 0, 0);
// Dynamically load Tesseract.js if not available
if (!window.Tesseract) {
await new Promise((resolve, reject) => {
const script = document.createElement('script');
script.src = 'https://cdn.jsdelivr.net/npm/tesseract.js@5/dist/tesseract.min.js';
script.onload = resolve;
script.onerror = reject;
document.head.appendChild(script);
});
}
// Initialize Tesseract worker for Russian text recognition
const worker = await window.Tesseract.createWorker('rus');
const ret = await worker.recognize(canvas);
await worker.terminate();
// Standard Russian-to-Latin transliteration dictionary
const ru2lat = {
'А': 'A', 'Б': 'B', 'В': 'V', 'Г': 'G', 'Д': 'D', 'Е': 'E', 'Ё': 'Yo', 'Ж': 'Zh', 'З': 'Z', 'И': 'I', 'Й': 'Y', 'К': 'K', 'Л': 'L', 'М': 'M', 'Н': 'N', 'О': 'O', 'П': 'P', 'Р': 'R', 'С': 'S', 'Т': 'T', 'У': 'U', 'Ф': 'F', 'Х': 'Kh', 'Ц': 'Ts', 'Ч': 'Ch', 'Ш': 'Sh', 'Щ': 'Shch', 'Ъ': '', 'Ы': 'Y', 'Ь': '', 'Э': 'E', 'Ю': 'Yu', 'Я': 'Ya',
'а': 'a', 'б': 'b', 'в': 'v', 'г': 'g', 'д': 'd', 'е': 'e', 'ё': 'yo', 'ж': 'zh', 'з': 'z', 'и': 'i', 'й': 'y', 'к': 'k', 'л': 'l', 'м': 'm', 'н': 'n', 'о': 'o', 'п': 'p', 'р': 'r', 'с': 's', 'т': 't', 'у': 'u', 'ф': 'f', 'х': 'kh', 'ц': 'ts', 'ч': 'ch', 'ш': 'sh', 'щ': 'shch', 'ъ': '', 'ы': 'y', 'ь': '', 'э': 'e', 'ю': 'yu', 'я': 'ya'
};
const transliterate = (word) => {
return word.split('').map(char => ru2lat[char] !== undefined ? ru2lat[char] : char).join('');
};
const threshold = parseInt(confidenceThreshold, 10) || 30;
for (const word of ret.data.words) {
// Skip low-confidence words
if (word.confidence < threshold) continue;
// Skip words that don't contain any Russian cyrillic letters
const hasRussian = /[А-Яа-яЁё]/.test(word.text);
if (!hasRussian) continue;
const transText = transliterate(word.text).trim();
if (!transText) continue;
const { x0, y0, x1, y1 } = word.bbox;
const w = x1 - x0;
const h = y1 - y0;
// Pad the bounding box slightly to override the entire original wording robustly
const padX = Math.max(1, w * 0.05);
const padY = Math.max(1, h * 0.05);
const bx = Math.floor(Math.max(0, x0 - padX));
const by = Math.floor(Math.max(0, y0 - padY));
const bw = Math.ceil(Math.min(canvas.width - bx, w + padX * 2));
const bh = Math.ceil(Math.min(canvas.height - by, h + padY * 2));
if (bw <= 0 || bh <= 0) continue;
// Find the approximate text background color by checking colors at the top and bottom edge pixels
const imgData = ctx.getImageData(bx, by, bw, bh).data;
let r = 0, g = 0, b = 0, count = 0;
for (let i = 0; i < bw; i++) {
// Check top border pixels
let idxTop = i * 4;
r += imgData[idxTop]; g += imgData[idxTop + 1]; b += imgData[idxTop + 2]; count++;
// Check bottom border pixels
let idxBottom = ((bh - 1) * bw + i) * 4;
if (idxBottom < imgData.length - 3) {
r += imgData[idxBottom]; g += imgData[idxBottom + 1]; b += imgData[idxBottom + 2]; count++;
}
}
if (count > 0) {
r = Math.round(r / count);
g = Math.round(g / count);
b = Math.round(b / count);
} else {
r = 255; g = 255; b = 255;
}
// Calculate background brightness and establish an appropriate overlay text color
const brightness = (r * 299 + g * 587 + b * 114) / 1000;
const textColor = brightness > 128 ? '#000000' : '#ffffff';
// Draw an opaque background over the original Russian text box
ctx.fillStyle = `rgb(${r}, ${g}, ${b})`;
ctx.fillRect(bx, by, bw, bh);
// Render the transliterated Latin text
ctx.fillStyle = textColor;
let fontSize = Math.floor(h * 0.85);
ctx.font = `bold ${fontSize}px sans-serif`;
ctx.textBaseline = 'middle';
// Output text inside the bounding box, respecting the max width
ctx.fillText(transText, bx, by + bh / 2, bw);
}
return canvas;
}
Apply Changes