Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Javascript - Image data processing and div rendering

So the challenge that I've created for myself is as such.

I have a source photo:

Source Photo

That I am mapping color values and creating a pixelated representation of it using divs

Here's the result:

Result Photo

The code that I'm accomplishing this with is:

'use strict';

var imageSource = 'images/unicorn.jpg';

var img = new Image();
img.src = imageSource;
var canvas = $('<canvas/>')[0];
canvas.width = img.width;
canvas.height = img.height;
canvas.getContext('2d').drawImage(img, 0, 0, img.width, img.height);
var context = canvas.getContext('2d');

console.log('img height: ' + img.height);
console.log('img width: ' + img.width);

var pixelDensity = 70;

var timerStart = new Date();


for (var i = pixelDensity/2; i < img.height; i += (img.height/pixelDensity) ) {
    $('.container').append($('<div class="row">'));
    for(var j = pixelDensity/2; j < img.width; j += img.height/pixelDensity) {
        var value = context.getImageData(j, i, 1, 1).data;
        var colorValue = 'rgb(' + value[0] + ', ' + value[1] + ', ' + value[2] + ')';
        $('.row:last').append($('<div class="block">').css({'background-color' : colorValue}));
    }
}

var timerStop = new Date();

console.log(timerStop - timerStart + ' ms');

The pixelDensity variable controls how close together the color samples are. The smaller the number, the fewer the samples, taking less time to produce the result. As you increase the number, the samples go up and the function slows down considerably.

I'm curious to know what is making this thing take such a long time. I've looked at slightly similar projects - most notably Jscii - which process the image data much faster, but I can't figure out what the difference is that's offering the added performance.

Thanks for your help!

like image 269
tylerdavis Avatar asked Jan 31 '13 19:01

tylerdavis


2 Answers

The main issue is that you append DOM elements to a page right in a loop. This would run much faster if you'll create wrapper element with all your data before actually adding it to the page.

Edit: I've also noticed you call context.getImageData for every pixel, this is what takes most of the time. Instead, you should cache image data in variable and get color values from it. You'll also need to cache loop conditions as @Travis J mentioned and round them:

var wrapper = $('<div class="container">');
var imgData = context.getImageData(0, 0, img.width, img.height).data;
var getRGB = function(i) { return [imgData[i], imgData[i+1], imgData[i+2]]; };
var start = Math.round(pixelDensity/2),
    inc = Math.round(img.height/pixelDensity);

for (var i = start; i < img.height; i += inc) {
    var row = $('<div class="row">');
    for(var j = start; j < img.width; j += inc) {
        var colorValue = getRGB((i * (img.width*4)) + (j*4));
        row.append($('<div class="block">').css({'background-color' : 'rgb('+(colorValue.join(','))+')'}));
    }
    wrapper.append(row);
}

$('body').append(wrapper);

This would reduce time from 6-9 seconds to 600-1000 milliseconds. You can also use plain javascript to manipulate DOM elements instead of jquery to make it even faster.

like image 89
dimusic Avatar answered Oct 16 '22 09:10

dimusic


Why don't you consider drawing the result on the canvas instead of creating so many divs? In theory it should be much faster...

like image 27
Martin Avatar answered Oct 16 '22 11:10

Martin