简体   繁体   中英

Iterating pixels vertically from HTML5 Canvas

I am trying to find the first vertical and horizontal row in in image that has data in it.

I have got the first horizontal row easily, as when calling getImageData , the resulting array of pixel data is stored horizontally row-by-row. I have this data like so:

var data = context.getImageData(0, 0, width, height).data;
var heightFirst = 0;

for (var r = 0; r <= data.length; r += 4) {
    if (data[r + 3] > 0) {
        var y = (r / width) / 4;
        heightFirst = y;
        break;
    }
}

I now need to iterate the data vertically. I realise this is probably done using a nested for loop and doing something like this:

for (var r = 0; r <= data.length; r += 4) {
    for (var c = 0; c < canvasHeight; c++) {    
        if (data[(r + 3) + (c * width)] > 0) {

        }
    }
}

I'm not sure of the exact implementation / calculation can anyone help me out??

EDIT

As @bobbybee and @danielpolencic have mentioned, a loop that increments by 4 * width will increment a vertical column:

for (var r = 0; r <= data.length; r += (4 * width)) {
  if (data[r + 3] > 0) {
    // do whatever
  }
}

However, this only gets information for the very first column of pixels. The next step I suppose is to repeat the loop above, but increment it by the number of columns (multiplied by 4 due to the colour data) with a loop like this:

for (var c = 0; c < canvas.width; c ++) {
    for (var r = 0; r <= data.length; r += ((4 * canvas.width) + (c * 4))) {
        if (data[r + 3] > 0) {
            firstX = c;
        }
    }
}

This doesn't seem to be quite right. I've stuck this on this fiddle , as you can see it should be returning 10, as this is the first column from the left, but it is writing 99 to the log. Feel like I am so close!

You can do it all in a single loop without having to nest at all.

Live Demo

var minX = canvas.width,
    minY = canvas.height;

for (var p = 0; p <= data.length; p += 4) {
     var curX = (p / 4) % canvas.width,
         curY = ((p / 4) - curX) / canvas.width;

    /* if what we're checking is higher than our recorded minX and 
      minY skip to the next row */

    if(curX > minX && curY > minY){
        // if minY is greater than 0 continue, if its 0 it can be no less so break.
        if(minY > 0){
            p=(curY+1)*(canvas.width*4); 
        }else{
            break;
        }
    }

    if (data[p + 3] > 0) {
        if (curX < minX) {
            minX = curX;
        };

        if (curY < minY) {
            minY = curY;
        };
    }
}

The following is the formula for getting the x and y from a single dimension array

X = Index % Width

Y = (Index - x) / Width

In our case since we are working with pixel data that is seperated into 4 component values (r/g/b/alpha) we need to divide our index by 4 like I do in the demo above.

Next I use minX and minY set to the canvas width and height. Whenever we hit a pixel we check if its x or y are lower than our lowest x and y stored. If they are lower, then we keep the value and move on.

However its easier to read, and faster to use more than one loop to read the data.

for (var x = 0; x <= canvas.width; x++) {
  for (var y = 0; y <= canvas.height; y++) {
    if (minY < y) {
      break;
    }
    if (data[((y * canvas.width + x) * 4) + 3] > 0) {
      if (x < minX) {
        minX = x;
      }
      if (y < minY) {
        minY = y;
      }
    }
  }
}

Demo to get the lastX and lastY

for (var x = 0; x <= canvas.width; x++) {
    for (var y = 0; y <= canvas.height; y++) {
        if (data[((y * canvas.width + x) * 4) + 3] > 0) {
            if (x < firstX) {
                firstX = x;
            }
            if (y < firstY) {
                firstY = y;
            }

            if (x > lastX) {
                lastX = x;
            }
            if (y > lastY) {
                lastY = y;
            }
        }
    }
}

Like @bobbybee correctly pointed out, the step for your loop is 4 * image_width .

for (var r = 0; r <= data.length; r += (4 * width)) {
    for (var c = 0; c < canvasHeight; c++) {
        if (data[r + 3] > 0) {

        }
    }
}

This can be accomplished by a single loop (no need to nest loops here):

for(var y = 0; y < data.length; y += canvas.width * 4) {
    if (data[y + 3] > 0 ) { /*...*/ }
}

or to squeeze out a little more speed:

var y = 0,
    length = data.length,     /// cache length
    width = canvas.width * 4; /// cache width

while(y < length) {
    if (data[y + 3] > 0 ) { /*...*/ }
    y += width;
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM