Image Dialogue Removal And Repair Tool For Manga Panels
(Free & Supports Bulk Upload)
The result will appear here...
JavaScript Code (For Advanced Users)
You can edit the below JavaScript code to customize the image tool.
/**
* Removes dialogue from a manga panel by identifying color-based regions,
* blanking them out (including text inside), and then inpainting the area
* with surrounding background pixels.
*
* @param {Image} originalImg The original Image object of the manga panel.
* @param {string} targetColorStr A comma-separated RGB string for the bubble color to target, e.g., "255,255,255" for white.
* @param {number} colorThreshold The tolerance for color matching (0-765). A higher value is more lenient. 60 is a good starting point.
* @returns {HTMLCanvasElement} A new canvas element with the dialogue removed and repaired.
*/
function processImage(originalImg, targetColorStr = '255,255,255', colorThreshold = 60) {
// 1. --- Parameter Parsing and Canvas Setup ---
const targetColor = targetColorStr.split(',').map(Number);
if (targetColor.length !== 3 || targetColor.some(isNaN)) {
console.error("Invalid targetColorStr. Using default '255,255,255'.");
targetColor = [255, 255, 255];
}
const [targetR, targetG, targetB] = targetColor;
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d', { willReadFrequently: true });
const { width, height } = originalImg;
canvas.width = width;
canvas.height = height;
ctx.drawImage(originalImg, 0, 0);
const imageData = ctx.getImageData(0, 0, width, height);
const data = imageData.data;
// 2. --- Mask Creation: Identify potential bubble regions based on color ---
// Mask states: 0 = source pixel, 1 = hole pixel (to be filled)
const initialMask = new Uint8Array(width * height);
for (let i = 0; i < data.length; i += 4) {
const r = data[i];
const g = data[i+1];
const b = data[i+2];
const diff = Math.abs(r - targetR) + Math.abs(g - targetG) + Math.abs(b - targetB);
if (diff < colorThreshold) {
initialMask[i / 4] = 1;
}
}
// 3. --- Mask Refinement: Fill bounding boxes of connected components to include text ---
const finalMask = new Uint8Array(width * height);
const visited = new Uint8Array(width * height);
for (let y = 0; y < height; y++) {
for (let x = 0; x < width; x++) {
const idx = y * width + x;
if (initialMask[idx] === 1 && !visited[idx]) {
// Found a new unvisited component, start a Breadth-First Search (BFS)
const queue = [{x, y}];
visited[idx] = 1;
let head = 0;
let minX = x, minY = y, maxX = x, maxY = y;
while (head < queue.length) {
const current = queue[head++];
minX = Math.min(minX, current.x);
minY = Math.min(minY, current.y);
maxX = Math.max(maxX, current.x);
maxY = Math.max(maxY, current.y);
// Check 8 neighbors
for (let dy = -1; dy <= 1; dy++) {
for (let dx = -1; dx <= 1; dx++) {
if (dx === 0 && dy === 0) continue;
const nx = current.x + dx;
const ny = current.y + dy;
const nIdx = ny * width + nx;
if (nx >= 0 && nx < width && ny >= 0 && ny < height && initialMask[nIdx] === 1 && !visited[nIdx]) {
visited[nIdx] = 1;
queue.push({x: nx, y: ny});
}
}
}
}
// If the component is large enough, assume it's a dialogue bubble
// and fill its entire bounding box in the final mask.
if ((maxX - minX > 10) && (maxY - minY > 10)) {
for (let by = minY; by <= maxY; by++) {
for (let bx = minX; bx <= maxX; bx++) {
finalMask[by * width + bx] = 1;
}
}
}
}
}
}
// 4. --- Inpainting: Fill the masked region using a diffusion method ---
const workData = new Uint8ClampedArray(data);
const frontier = [];
// Final mask states: 0=source, 1=hole, 2=frontier
for (let i = 0; i < finalMask.length; i++) {
if (finalMask[i] === 1) { // If it's a hole pixel
const y = Math.floor(i / width);
const x = i % width;
let isBoundary = false;
// Check its 8 neighbors to see if it's on the edge of the hole
for (let dy = -1; dy <= 1; dy++) {
for (let dx = -1; dx <= 1; dx++) {
if (dx === 0 && dy === 0) continue;
const nx = x + dx, ny = y + dy;
if (nx >= 0 && nx < width && ny >= 0 && ny < height && finalMask[ny * width + nx] === 0) {
isBoundary = true;
break;
}
}
if (isBoundary) break;
}
if (isBoundary) {
finalMask[i] = 2; // Mark as on the frontier
frontier.push({x, y});
}
}
}
let head = 0;
while (head < frontier.length) {
const p = frontier[head++];
const idx = p.y * width + p.x;
// Calculate average color of surrounding known pixels
let r_sum = 0, g_sum = 0, b_sum = 0, count = 0;
for (let dy = -1; dy <= 1; dy++) {
for (let dx = -1; dx <= 1; dx++) {
const nx = p.x + dx, ny = p.y + dy;
if (nx >= 0 && nx < width && ny >= 0 && ny < height) {
const nIdx = ny * width + nx;
if (finalMask[nIdx] === 0) { // If neighbor is a source pixel
const dataIdx = nIdx * 4;
r_sum += workData[dataIdx];
g_sum += workData[dataIdx+1];
b_sum += workData[dataIdx+2];
count++;
}
}
}
}
// Fill the pixel with the average color
if (count > 0) {
const dataIdx = idx * 4;
workData[dataIdx] = r_sum / count;
workData[dataIdx+1] = g_sum / count;
workData[dataIdx+2] = b_sum / count;
}
finalMask[idx] = 0; // Mark this pixel as filled (now a source)
// Add its unprocessed neighbors to the frontier
for (let dy = -1; dy <= 1; dy++) {
for (let dx = -1; dx <= 1; dx++) {
if (dx === 0 && dy === 0) continue;
const nx = p.x + dx, ny = p.y + dy;
if (nx >= 0 && nx < width && ny >= 0 && ny < height) {
const nIdx = ny * width + nx;
if (finalMask[nIdx] === 1) { // If neighbor is an untouched hole pixel
finalMask[nIdx] = 2; // Mark it as the new frontier
frontier.push({x: nx, y: ny});
}
}
}
}
}
// 5. --- Final Output ---
const resultCanvas = document.createElement('canvas');
resultCanvas.width = width;
resultCanvas.height = height;
const resultCtx = resultCanvas.getContext('2d');
resultCtx.putImageData(new ImageData(workData, width, height), 0, 0);
return resultCanvas;
}
Free Image Tool Creator
Can't find the image tool you're looking for? Create one based on your own needs now!
The Image Dialogue Removal and Repair Tool for Manga Panels is designed to efficiently remove dialogue bubbles and text from manga panels. By identifying specific color regions associated with dialogue, this tool blanks them out and inpaints the surrounding areas to seamlessly blend the background, restoring the original artwork. This tool is particularly useful for editors, artists, or fans who want to create clean versions of manga panels, whether for restoration, redrawing, or creating fan edits without dialogue.