Konan
Konan

Reputation: 1

How to optimize canvas rendering performance for edge detection in images?

I am working on a project that renders the edge points of an image on an HTML canvas using JavaScript. Despite several optimizations, the performance is not ideal, especially when dealing with larger images or dynamic updates.

My approach involves scaling down the image, applying a grayscale filter, and using a brightness threshold to detect edge points. These points are then dynamically rendered on the canvas. Here are the basics:

const canvas = document.getElementById("canvas");
const ctx = canvas.getContext("2d");

canvas.width = window.innerWidth;
canvas.height = window.innerHeight;

const img = new Image();
img.crossOrigin = "anonymous";
img.src = 'https://example.com/image.jpg';

img.onload = () => {
    console.log("Image loaded successfully. Starting processing...");
    processSingleImage(img);
};

function processSingleImage(img) {
    console.log("Processing single image with grayscale optimization...");
    const smallCanvas = document.createElement('canvas');
    const smallCtx = smallCanvas.getContext('2d');
    smallCanvas.width = img.width * scale;
    smallCanvas.height = img.height * scale;
    smallCtx.filter = 'grayscale(1)';
    smallCtx.drawImage(img, 0, 0, smallCanvas.width, smallCanvas.height);
}

// Function to preload the text in an offscreen container 
function preloadText() {
    offscreenContainer.text(typewriterText);
}

// Preload the text into the offscreen container
preloadText();

// Start typewriter effect
typeEffect();

const imgData = smallCtx.getImageData(0, 0, smallCanvas.width, smallCanvas.height).data;

const points = [];

// Function to preload the text in an offscreen container 
function preloadText() {
    offscreenContainer.text(typewriterText);
}

// Preload the text into the offscreen container
preloadText();

// Start typewriter effect
typeEffect();

// Function to preload the text in an offscreen container 
function preloadText() {
    offscreenContainer.text(typewriterText);
}

// Preload the text into the offscreen container
preloadText();

// Start typewriter effect
typeEffect();

for (let i = 0; i < smallCanvas.width * smallCanvas.height; i++) {
    const brightness = imgData[i * 4]; // Single channel (grayscale)
    const brightness = imgData[pixelIndex]; // Any single channel value works
    if (brightness < brightnessThreshold) {
        const x = (i % smallCanvas.width) / smallCanvas.width * canvas.width;
        const y = Math.floor(i / smallCanvas.width) / smallCanvas.height * canvas.height;
        points.push({ x, y });
    }
}

console.log(`Processed image. Found ${points.length} edge points.`);
edgeData.push(points);

ready = true;
console.log("Image processed. Starting animation...");
animate();
}

function draw() {
    if (!ready) return;
    ctx.fillStyle = "rgba(0, 0, 0, 0.1)";
    ctx.fillRect(0, 0, canvas.width, canvas.height);

    edgeData.forEach((points) => {
        ctx.beginPath();
        points.forEach((point, index) => {
            if (index % 1 === 0) { 
                ctx.fillStyle = `hsl(${Math.random() * 360}, 50%, 70%)`;
                ctx.fillText("*", point.x, point.y); // set points
            }
        });
        ctx.closePath();
    });
}

function animate() {
    draw();
    let lastTime = 0;
    const fps = 35; // set framerate
    const fpsInterval = 1000 / fps; // set fps-interval
}

function animate(currentTime) {
    requestAnimationFrame(animate);
    const elapsed = currentTime - lastTime;
    if (elapsed > fpsInterval) {
        lastTime = currentTime - (elapsed % fpsInterval);
        // animation logic check
        console.log("Frame drawn at:", currentTime);
    }
}

requestAnimationFrame(animate); 

// Create an offscreen container for preloading the text
const offscreenContainer = $('<div>').css({
    position: 'absolute',
    visibility: 'hidden',
    top: 0,
    left: 0
}).appendTo('body');

// Typewriter text
const typewriterText = `[...]`;

// Function to preload the text in an offscreen container 
function preloadText() {
    offscreenContainer.text(typewriterText);
}

// Preload the text into the offscreen container
preloadText();

// Start typewriter effect
typeEffect();

// Resize handler (optional)

window.addEventListener('resize', () => {
    canvas.width = window.innerWidth;
    canvas.height = window.innerHeight;
});

The edge detection and rendering process becomes slower with larger images or high-density edge points, despite optimizations like scaling and grayscale filters. Also, the project includes additional animations (e.g., a typewriter effect for text overlay), which further strain the rendering pipeline.

What techniques or optimizations can improve canvas rendering performance for edge detection?

Are there algorithmic adjustments or GPU-accelerated approaches (e.g., WebGL) that could be integrated into this workflow for better performance?

I’m looking for technical insights or best practices, rather than recommendations for libraries or external tools.

I hope this fits the guidelines.

Regards, Konan

EDIT: I added now more of the animation logic for a better understanding of how it is working, I hope.

What after some research last night really made a change after some different approaches, has been the better animation performance with:

function animate(currentTime) {
    requestAnimationFrame(animate);

    const elapsed = currentTime - lastTime;

    if (elapsed > fpsInterval) {
        lastTime = currentTime - (elapsed % fpsInterval);

        // animation logic check
        console.log("Frame drawn at:", currentTime);
    }
}

as well as adjusted framerate settings and the preloading per offscreen container (especially for the typewriterText):

// Create offscreen container for preloading the text
const offscreenContainer = $('<div>').css({
    position: 'absolute',
    visibility: 'hidden',
    top: 0,
    left: 0
}).appendTo('body');

const typewriterText = `[...]`;

// Function to preload the text in an offscreen container 
function preloadText() {
    offscreenContainer.text(typewriterText);
}

// Preload the text into the offscreen container
preloadText();

// Start typewriter effect
typeEffect();

It should be now better readable, depending on the draw() and animation() functions.

I also think I'm progressing too many pixels here, but to reduce them made it just look way more chopped in most cases.

Still thanks for the answers - and I hope the edge detection is now better visible.

I'll check out, what is possible to implement here, especially in terms of ImageData.

Upvotes: 0

Views: 71

Answers (0)

Related Questions