Please bookmark this page to avoid losing your image tool!

Image Translator And Counter Tool

(Free & Supports Bulk Upload)

Drag & drop your images here or

The result will appear here...
You can edit the below JavaScript code to customize the image tool.
/**
 * Translates specified colors in an image and counts the frequency of all original colors.
 * This function processes an image pixel by pixel to replace colors based on a provided map
 * and also tallies the occurrences of every color found in the original image.
 *
 * @param {HTMLImageElement} originalImg The source image object whose pixel data will be processed.
 * @param {string} [colorMapString=''] A comma-separated string defining the color translations.
 *   The format for each pair is 'oldHexColor:newHexColor'.
 *   Example: '#ff0000:#00ff00,#0000ff:#ffff00' will replace all red pixels with green and all blue pixels with yellow.
 *   Hex codes can be 3 or 6 digits (e.g., #f00:#0f0).
 * @param {string} [displayMode='both'] Controls what is returned.
 *   - 'both': Returns a container with the translated image and the color count table.
 *   - 'translated': Returns only the canvas element of the translated image.
 *   - 'counts': Returns only the container element with the color count table.
 * @returns {HTMLElement} A single HTML element (a DIV or a CANVAS) that can be displayed on the page.
 */
function processImage(originalImg, colorMapString = '', displayMode = 'both') {

    /**
     * Helper function to parse a hex color string (#RRGGBB or #RGB) into an {r, g, b} object.
     * @param {string} hex The hex color string.
     * @returns {{r: number, g: number, b: number}|null} An object with r, g, b values or null if invalid.
     */
    const hexToRgb = (hex) => {
        if (!hex || typeof hex !== 'string') return null;
        let sanitizedHex = hex.trim().startsWith('#') ? hex.trim().slice(1) : hex.trim();

        if (sanitizedHex.length === 3) {
            sanitizedHex = sanitizedHex.split('').map(char => char + char).join('');
        }

        if (sanitizedHex.length !== 6) return null;

        const num = parseInt(sanitizedHex, 16);
        if (isNaN(num)) return null;

        return {
            r: (num >> 16) & 255,
            g: (num >> 8) & 255,
            b: num & 255
        };
    };

    // 1. Setup canvas to read original image data
    const canvas = document.createElement('canvas');
    const ctx = canvas.getContext('2d', {
        willReadFrequently: true
    });
    const width = originalImg.naturalWidth;
    const height = originalImg.naturalHeight;
    canvas.width = width;
    canvas.height = height;
    ctx.drawImage(originalImg, 0, 0);

    // 2. Read pixel data, with error handling for cross-origin images
    let imageData;
    try {
        imageData = ctx.getImageData(0, 0, width, height);
    } catch (e) {
        console.error("Canvas security error:", e);
        const errorDiv = document.createElement('div');
        errorDiv.textContent = 'Error: Could not process the image. This may be due to cross-origin security restrictions (CORS policy). Try using an image from the same domain.';
        errorDiv.style.color = 'red';
        errorDiv.style.padding = '10px';
        errorDiv.style.fontFamily = 'Arial, sans-serif';
        errorDiv.style.border = '1px solid red';
        return errorDiv;
    }
    const data = imageData.data;

    // 3. Parse the color map string into a usable Map object for fast lookups
    const colorMap = new Map();
    if (colorMapString && colorMapString.trim() !== '') {
        colorMapString.split(',').forEach(pair => {
            const parts = pair.split(':');
            if (parts.length === 2) {
                const oldRgb = hexToRgb(parts[0]);
                const newRgb = hexToRgb(parts[1]);
                if (oldRgb && newRgb) {
                    const oldRgbKey = `${oldRgb.r},${oldRgb.g},${oldRgb.b}`;
                    colorMap.set(oldRgbKey, newRgb);
                }
            }
        });
    }

    // 4. Prepare a new canvas and data array for the translated image
    const translatedCanvas = document.createElement('canvas');
    translatedCanvas.width = width;
    translatedCanvas.height = height;
    const translatedCtx = translatedCanvas.getContext('2d');
    const translatedImageData = translatedCtx.createImageData(width, height);
    const translatedData = translatedImageData.data;

    // 5. Iterate through all pixels to count original colors and create the translated image
    const colorCounts = new Map();
    for (let i = 0; i < data.length; i += 4) {
        const r = data[i];
        const g = data[i + 1];
        const b = data[i + 2];
        const a = data[i + 3];

        const originalRgbKey = `${r},${g},${b}`;

        // Count every unique color in the original image
        colorCounts.set(originalRgbKey, (colorCounts.get(originalRgbKey) || 0) + 1);

        // Check if the current pixel's color needs to be translated
        const newColor = colorMap.get(originalRgbKey);
        if (newColor) {
            translatedData[i] = newColor.r;
            translatedData[i + 1] = newColor.g;
            translatedData[i + 2] = newColor.b;
        } else {
            translatedData[i] = r;
            translatedData[i + 1] = g;
            translatedData[i + 2] = b;
        }
        translatedData[i + 3] = a; // Always preserve the original alpha channel
    }

    // Place the newly created pixel data onto the translated canvas
    translatedCtx.putImageData(translatedImageData, 0, 0);

    /**
     * Generates an HTML element containing a formatted table of color counts.
     * @returns {HTMLDivElement} A div containing the results table.
     */
    const createCountsElement = () => {
        const container = document.createElement('div');
        const header = document.createElement('h3');
        header.textContent = 'Original Image Color Counts';
        header.style.fontFamily = 'Arial, sans-serif';
        header.style.marginTop = '20px';
        container.appendChild(header);

        const table = document.createElement('table');
        table.style.width = '100%';
        table.style.borderCollapse = 'collapse';
        table.style.fontFamily = 'Arial, sans-serif';
        table.style.fontSize = '14px';

        const thead = document.createElement('thead');
        thead.innerHTML = `
            <tr style="text-align: left; background-color: #f2f2f2;">
                <th style="padding: 8px; border: 1px solid #ddd;">Color</th>
                <th style="padding: 8px; border: 1px solid #ddd;">RGB Value</th>
                <th style="padding: 8px; border: 1px solid #ddd;">Pixel Count</th>
            </tr>
        `;
        table.appendChild(thead);

        const tbody = document.createElement('tbody');
        const sortedCounts = [...colorCounts.entries()].sort((a, b) => b[1] - a[1]);

        for (const [rgbKey, count] of sortedCounts) {
            const tr = tbody.insertRow();
            tr.style.borderBottom = '1px solid #ddd';

            const swatchCell = tr.insertCell();
            swatchCell.style.padding = '8px';
            const swatch = document.createElement('div');
            swatch.style.width = '20px';
            swatch.style.height = '20px';
            swatch.style.backgroundColor = `rgb(${rgbKey})`;
            swatch.style.border = '1px solid #ccc';
            swatchCell.appendChild(swatch);

            const rgbCell = tr.insertCell();
            rgbCell.style.padding = '8px';
            rgbCell.textContent = `rgb(${rgbKey})`;

            const countCell = tr.insertCell();
            countCell.style.padding = '8px';
            countCell.style.textAlign = 'right';
            countCell.textContent = count.toLocaleString();
        }
        container.appendChild(table);
        return container;
    };

    // 6. Return the appropriate element(s) based on the displayMode parameter
    if (displayMode === 'translated') {
        return translatedCanvas;
    }

    if (displayMode === 'counts') {
        return createCountsElement();
    }

    // Default 'both' mode
    const mainContainer = document.createElement('div');
    const translatedHeader = document.createElement('h3');
    translatedHeader.textContent = 'Translated Image';
    translatedHeader.style.fontFamily = 'Arial, sans-serif';
    mainContainer.appendChild(translatedHeader);
    mainContainer.appendChild(translatedCanvas);
    mainContainer.appendChild(createCountsElement());

    return mainContainer;
}

Free Image Tool Creator

Can't find the image tool you're looking for?
Create one based on your own needs now!

Description

The Image Translator And Counter Tool allows users to process images by mapping and transforming specified colors according to a color translation map. It replaces defined colors in the image with new colors and counts the frequency of all the original colors present in the image. This tool is beneficial for artists, designers, and developers who want to create color-themed visuals, analyze color distribution in images, or prepare images for specific design requirements, such as branding and marketing projects. Users can choose to view the translated image, the color count data, or both.

Leave a Reply

Your email address will not be published. Required fields are marked *