Please bookmark this page to avoid losing your image tool!

Image Logo Recognition For Music

(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.
async function processImage(originalImg, threshold = 0.8) {

    /**
     * Helper function to load an an image from a Base64 string.
     * @param {string} base64 - The Base64 encoded image data.
     * @returns {Promise<Image>} A promise that resolves with the loaded Image object.
     */
    const loadImageFromBase64 = (base64) => {
        return new Promise((resolve, reject) => {
            const img = new Image();
            img.onload = () => resolve(img);
            img.onerror = (err) => reject(err);
            img.src = base64;
        });
    };

    /**
     * Helper function to convert ImageData to a grayscale representation.
     * This simplifies the matching process and makes it color-independent.
     * @param {ImageData} imageData - The pixel data from a canvas.
     * @returns {{data: Uint8ClampedArray, width: number, height: number}}
     */
    const convertToGrayscale = (imageData) => {
        const grayData = new Uint8ClampedArray(imageData.width * imageData.height);
        for (let i = 0; i < imageData.data.length; i += 4) {
            const r = imageData.data[i];
            const g = imageData.data[i + 1];
            const b = imageData.data[i + 2];
            // Using the luminosity method for grayscale conversion
            const gray = 0.299 * r + 0.587 * g + 0.114 * b;
            grayData[i / 4] = gray;
        }
        return {
            data: grayData,
            width: imageData.width,
            height: imageData.height
        };
    };

    /**
     * Performs template matching using Normalized Cross-Correlation (NCC).
     * This is computationally intensive and may be slow on large images.
     * @param {{data: Uint8ClampedArray, width: number, height: number}} source - The source image (grayscale).
     * @param {{data: Uint8ClampedArray, width: number, height: number}} template - The template image (grayscale).
     * @returns {{x: number, y: number, score: number}} The best match position and its score.
     */
    const findBestMatch = (source, template) => {
        let bestMatch = { x: 0, y: 0, score: -1 };
        const { data: sourceData, width: sw, height: sh } = source;
        const { data: templateData, width: tw, height: th } = template;

        // Pre-calculate template mean and standard deviation
        let templateSum = 0;
        let templateSumSq = 0;
        for (let i = 0; i < templateData.length; i++) {
            templateSum += templateData[i];
            templateSumSq += templateData[i] * templateData[i];
        }
        const templateMean = templateSum / templateData.length;
        const templateStdDev = Math.sqrt(templateSumSq / templateData.length - templateMean * templateMean);

        // Iterate over the source image
        for (let y = 0; y <= sh - th; y++) {
            for (let x = 0; x <= sw - tw; x++) {
                let windowSum = 0;
                let windowSumSq = 0;
                let crossCorrelationSum = 0;

                // Iterate over the template/window
                for (let j = 0; j < th; j++) {
                    for (let i = 0; i < tw; i++) {
                        const sourceIdx = (y + j) * sw + (x + i);
                        const templateIdx = j * tw + i;
                        const sourcePixel = sourceData[sourceIdx];
                        const templatePixel = templateData[templateIdx];
                        
                        windowSum += sourcePixel;
                        windowSumSq += sourcePixel * sourcePixel;
                        crossCorrelationSum += sourcePixel * templatePixel;
                    }
                }

                const windowMean = windowSum / templateData.length;
                const windowStdDev = Math.sqrt(windowSumSq / templateData.length - windowMean * windowMean);

                let score = -1;
                if (windowStdDev > 0 && templateStdDev > 0) {
                     const numerator = (crossCorrelationSum / templateData.length) - (windowMean * templateMean);
                     const denominator = windowStdDev * templateStdDev;
                     score = numerator / denominator;
                }
                
                if (score > bestMatch.score) {
                    bestMatch = { x, y, score };
                }
            }
        }
        return bestMatch;
    };


    // --- Main Function Logic ---

    // Define logo templates using Base64 to avoid external URLs
    const templates = [
        { name: 'Spotify', base64: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACgAAAAoCAYAAACM/rhtAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAARRSURBVFhHzZg/aBRXFMf/O+9eN4wKxoWIKIKdhaAoklARLKyNVNroG/gHsbQJlqxttGlWCiEpaGGsiIZGUBBCRFAqKnYpEYsWgoKIiLh3d3eH5d178e7e3by684Hhzt2d9/1f7t337j15+Pjx/9cQmCQSgZlJJAKzP2kEMn0xI1g/ySAY+F8JAsGQxJ9T0UqgT0rEBAwGg0dY3P7qRQC32z1m45t+A8DT05PRKxT63zQagVwuR6vVkqZptNYYjUaEw+EHyxJFEf1+v8FgENlsljRNQ9M0xGIxTCYTQqEQyWSSbrf7H2+1WqHrOhqNBrLZbM+SgEKhQMViEcvlkul0ir7vY7FYEAwGEM/niyljJpMhFoshHA4jGAzI5/OkadrzJGA0GkGv1xMNh7DZvEVsNpmYTCakUqnPY7PZen5iYgF8Pi+mpKTgfN43pVL5B/f7fSSSiZjb2cGc3BxmpycB1Go1pNNpDAZD8vk8er2eTCbT/TckEgkYDAby+TzS6TSGwyEqlQqJRIJgMKDfS1OT/P/N7u4uZmdn0ev1yOfzxGazUavVUKlUcDgceDye53sS0ev1sNls+Hy+p0mkcW4P523b0Gq1sNlsyGaz5HI5vN5bL7RaLSQSCVKpFO12m8lkQqVSITs7O+mB8ff3B9/3kc1mYTabkcvlaLVa2Gw2hMNhvN7bL6hUKvR6PbxeL4lEAmVlpT0h/x83mQyKxSJpNBqpVApt24amabjdbvh8PhqNJtLpNN1u9x/e7/eZnp4mkUjA6XTC7/ej1+vh9/vx+/0YDAZkMhkyMjKQTqeJSqXCaDRisVhQqVTQarWQy+VQaDRC67V/I5VKUb/fR6fTYTAYNDv3eTwerFarX+x2u4hEIgSDQdTrdZRKJcRiMSQSCfJm/tXjR9jtduRyOQwGA7w5+fPsnQ6Hwz1+eXlpL3BxcQGz2YxCoYBgMIhKpUKlUqFarQaDwQCv14uPj49/8Pl85OXlIRaLIRwOIxqNIhaLhW3L93uMMZLJJDKZDLlcLg/Ue2l3t9uN3W7HZrNB0zSMxWIIBAKEw2EkEglUKhVCoRA+ny+I//8uFovl/1B0XcdutzsQk/l8jlwux+l0wuVykclkcDgceDwebNuGpunHqNVq0Gq1UKvVUKvVUCoVVCqV2b8f+7yVUi2Vyh3b7fajvVwuYzabcbvdUKvVqNfrqNfrqNVq/B+j3Q65nE5kNhtyuRx0XUev10Ov10Ou1mIymZCfn4+7uzupVKqH/4tGo0Enk0Gj0SAWi+HzeRCJRBAOh1Gr1RAOh/l83q/j5uYGfr8fTqeTD38uFApxOBzi9/txOByiKEohzOfzUSqV+P3+p8g9cTgcUavVMBgM4Pf70Wq1NDMzmZ+Xh1KpxGq1wuPx4PV6SSTS3/Ue/9U3IqNfKBCIZPqjRsB/AVKJZCDWSvUAAAAASUVORK5CYII=' },
        { name: 'Apple Music', base64: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACgAAAAoCAYAAACM/rhtAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAANdSURBVFhH7ZfvS1NRGMYfExOzkQyCQqXyD1gkWYmERkUihYpgH6gUFQn6Y0hRkS5FEYIgVqKICIhIlqYy2R8l0eJ+03i+33vu4T33nnPu7u5zL3Qfzof7ed/nvd899x7SNC3/C8MhM0M+N4Mpm/wZ2AgyaAIGwNf2PzZg0s8NWAZz3XG96cM5gR04jXNnOQMbMA3+2/4cBj8N413B6p+Zp/b+uT2f8Boc06lO/Xo1Vj8i/z4/Pz8/bVbrl5lO56Kjo8O+v/r1Qdd1zM/Pw/u1u+0vjUaDRCLx4mKx2M/m5+cRj8cxDAO/34+fnx8UCgXk83kkEgncbjeys7NxcnKiM3d0dODs7Iz0ejQawWq1wmq1otfrMTExgbGxMezbtw+Dg4MYGBjYy+3Zsyc2NjbQ6/VQKBRwOBxobm5mamrKWvLz8+Hz+XBzc4O1tTXa29sxPz+PxMTEfV6Xl5fR3NzM+Pi4VpIkEQ8PD6murqampgb9/f1wuVxISEjA5uYmhoaGEAgEkM/nOX/w/h+Xy8WsLCy8tLurqwurq6sIBAIIhUJoaGjAysqKyuvr6wvj4+OYnp5GKBSCxWJBpVKhoKBANo9JSUlITU1FRkYGpqamUCgUcDgcGBgYwNbWFvr7+/H19YVOp4NarbZ8w+fzITs7m5SUlDsajUbSajUcDgcGBgbUajUajYbExcVheXkZg4ODMDo6qr21pKQkFhYW1Gq1a2RkRN3d3ePj43i9nq/Ly8szMDDArKzsb3d1dTnEx8djcHDQZrPZkZCQAK/XazQa8PT0dHZ2FqWlpUQiEcTjccTjcaxtbcHpdGJgYAAOhwMvv//6nZGRERoaGjA0NITW1lbKysowNzeHalZWVtTX18d8gYGBaGlpQSaTITY2FoVCgVwuR0NDA7Kzs9HpdDAYDDA7O4vS0tJdLS0t8fv9yMvLQ319PZWVlTh9+jQ+/vhjHB0dYXZ2FtXV1bS0tEBVVZXGxka0trZibGws4/P19SVeXl74xRdfEAgElEol7Ha7VqslnU7z9PQkLy8vtra2UFlZiVAoRCAQQDqdJpPJEA6HicViBAIB9Pf3j46OVqvV3N/fhx84uP3g4CCLzc1NNDc3t7e3IxaLEQwGsf/+/aRSKVwu1wMCA+LxuclkMp/Ph1KpNDQ0gcvlwu1243a74XA4VCoVlUoFrVZLMBikUqnk83k0Gi3kUqlUKpRKJeRS+fVCoRAulwuPxwOPx4PP54NarYbb7QbvGvP5PFwuFzqdDpvNBq/XS6VSIReLxWJRKhVyqVQKhVwuuVT+w1wupVJJpVIu5HK5VCq5XO55+vSpnJwc9PX1IRqNYrFYEIlEkEqlcLlcaDQaEokEIpHIof+O2z6hKxQK8fl8cDgc0HUdhmEgm83i8/kwGo1wu93o9XpUKhWZTAbpdJparYbfyVQqRbFYRFVVFYqKiv5n9uDgIGw2G+vr6zExMYGlpSWsra3hH3PjO1RUVODg4AAbGxvkWkNDQ9jY2MD09DS++OILRkdHMSYnJzEyMoKysrJ/sC0rKyvF9PT07u4uLBYL+vr6MDY2JpvNDgaDyM/Ph0QiAV3XsbKyAgMDAxgfHyf+X59bLpdhsVhwdnZGKpW6uzsfU1NTkEql8Pl8+Pj4QCQSQSAQQDAYJC4uDjabDScnJzAYDHTv+r9+wzA1Mib4b2EwBf+8gYnBNhD3HwAAAABJRU5ErkJggg==' },
        { name: 'SoundCloud', base64: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACgAAAAoCAYAAACM/rhtAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAHPSURBVFhH7ZhNTsNAEIWn0cQq6GmpCBQ8QpAKeAI4AnxKvgCIBJ8ATxAmSiKgpYgHqIgKRFyBfSExm2l3N6Q80p70VtrWvBwZ2bnPNz/r7J3l5eX/D1F/Rz/343U86/L90oDqM5zQ8O04b5f4y0+7uC7jK6/h3oXoWJ+o82k86qG7nQe9Kveq3p3t2VXu9XpcrcfUe92/z/t9fTq3u/t80qPzYy+O53v6Qn055c/R9f2+h+sCqo/h/vxe1+K8lqf1+qXyut7F9Hh9P6yud2n83J/3+XQ86mH2uJ+m8s411K/R6d73E4X1er2p5f6e8vKk69qX74fX5Wp9H8/ruC+b6hpc12/E/XgYvSrbg9l1cO1k9J1uQe+qg97V6+F8D7f24LqcR/V+3Wl1z/t53C7X3u06j/f33u19t8XqV239oN6nqL9h/o7XW1Wv2vptvj5f8A1Vb3R+r/u6jHuv4a+v+V7G4j2sOqwO98t+Hqv7N3r+D9e16Dpc+uA3oPq3N0j97y+hHl3Iuup+9A3aH+R77o/3Vn9j0v0Vuv/S6P7dK6/h2/f8E6h+jQ2k/lU2UP2cbKD67WQD1efKBqqfKhsoflM2UPyMbKD42dkA8UPKBopHlQ0Uj4gNFAdq+X+mH5R9HwcAAAAASUVORK5CYII=' },
        { name: 'YouTube Music', base64: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACgAAAAoCAYAAACM/rhtAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAIBSURBVFhH7ZfNTsMwEIWnJSUq6GmpCBQ8QpAKeAI4AnxKvgCIBJ8AT5AnSiKgpYgHqIgKRBS8QxITaTe3hJQn2pM+hbb178iZnXkmn1l3ZntmZma/Yag+4f772e3a1t5zHjG8g+uG8n4S8nU9D2PwfD0Pw+H5eh6Gw+s+h4PxfkSjX1G4T9f7eR8N/g/9c7i/D8fh9L3H/Xk9H4+n6911W3c/Hw/D/w+h4XWcw/P5l8uH4f45rGv2M2E/2d5/L3rfx4N48Ho9DMP78Xg/D8f7vN+nw/v+08v6fTgcqPsd9v99Ph+u38d9vQyH8bF7nwx++R7P5+N5GA7D43kYjufrbTgc7u/DYTgMh+H34/j7MBw+79M+l1h/x+F8Hw7H4/0w+l2Pw/v1PByH9+t5/z6cP5B78I7f4bS7B/m939Hwf/kM+v4d8v9Qd1sft6O653A/h4+g/3S+P2c4jN+3oO71o77Wv3g8qPtd9/0xGo/3YzgcDMN/03043P/3YTi+P4fD4X4/3I+n4T7cDu+D42X4uP9s/P0/j/9EUPm4xP9E+d1i/J/cZ/rP7zR/S/U5d3fXv3q/g+t5GA7h/g+v52F4vR/fT3j4q/t1w687O00AAAAASUVORK5CYII=' },
    ];
    
    // Setup the output canvas
    const canvas = document.createElement('canvas');
    const ctx = canvas.getContext('2d');

    // For performance, cap the image size.
    const MAX_WIDTH = 800;
    const scale = originalImg.width > MAX_WIDTH ? MAX_WIDTH / originalImg.width : 1;
    canvas.width = originalImg.width * scale;
    canvas.height = originalImg.height * scale;

    ctx.drawImage(originalImg, 0, 0, canvas.width, canvas.height);
    
    // Get grayscale data of the source image
    const sourceImageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
    const sourceGrayscale = convertToGrayscale(sourceImageData);

    const foundLogos = [];

    // Process each template
    for (const template of templates) {
        const templateImg = await loadImageFromBase64(template.base64);

        const tempCanvas = document.createElement('canvas');
        tempCanvas.width = templateImg.width;
        tempCanvas.height = templateImg.height;
        const tempCtx = tempCanvas.getContext('2d');
        tempCtx.drawImage(templateImg, 0, 0);
        const templateImageData = tempCtx.getImageData(0, 0, tempCanvas.width, tempCanvas.height);
        const templateGrayscale = convertToGrayscale(templateImageData);
        
        const match = findBestMatch(sourceGrayscale, templateGrayscale);

        if (match.score > threshold) {
             foundLogos.push({
                name: template.name,
                x: match.x,
                y: match.y,
                width: templateImg.width,
                height: templateImg.height,
                score: match.score
            });
        }
    }

    // Draw the results on the canvas
    ctx.font = '16px Arial';
    ctx.lineWidth = 2;

    for (const logo of foundLogos) {
        ctx.strokeStyle = '#00FF00'; // Bright green for the box
        ctx.strokeRect(logo.x, logo.y, logo.width, logo.height);
        
        const text = `${logo.name} (${(logo.score * 100).toFixed(1)}%)`;
        const textMetrics = ctx.measureText(text);
        const textX = logo.x;
        const textY = logo.y > 20 ? logo.y - 5 : logo.y + logo.height + 15;

        ctx.fillStyle = 'rgba(0, 0, 0, 0.7)';
        ctx.fillRect(textX - 2, textY - 14, textMetrics.width + 4, 18);
        
        ctx.fillStyle = '#00FF00'; // Bright green for the text
        ctx.fillText(text, textX, textY);
    }
    
    return canvas;
}

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 Logo Recognition for Music tool is designed to identify and highlight logo images from various music streaming services in a provided image. This tool utilizes advanced image processing techniques to detect logos such as Spotify, Apple Music, SoundCloud, and YouTube Music within user-uploaded images. Use cases include analyzing promotional materials, verifying brand presence in photos, or enhancing social media content by showcasing music-related imagery. The tool outputs a processed image with detected logos annotated for easy recognition.

Leave a Reply

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