Weird results combining colours - javascript

I'm currently working on a horizontal blur algorithm in javascript, though I doubt the language matters.
I get the data from a canvas which is basically a huge array where every four (RGBA) values stand for one pixel. A value can contain an int ranging from 0 to 255.
When I blur the image, the area's between two different colours turn into strange colours! I've drawn a red rectangle on a black background. Using the algorithm below, I get the following result (4px size):
Though when a use a 1 or 2 pixel size, everything seems to work normally.
Please note this is somewhat messy build up. I'm planning to make this all OOP!
// s: size
// w: width
// h: height
function blur( s, w, h ) {
var src = ctx.getImageData( 0, 0, w, h ); // get imagedata from source
var dst = ctx.createImageData( w, h ); // create imagedata for dest
var x, y, xo, index, rgb; // predefine vars
// loop through y axis
for( y = 0; y < h; y++ ) {
// loop through x axis
for( x = 0; x < w; x++ ) {
rgb = 0; // set total to 0
// loop through area around current pixel
for( xo = 0 - s; xo <= s; xo++ ) {
// get specific index
index = getIndex( x + xo, y, w );
// add nothing if the value doesn't exist (borders)
// if( isNaN( src.data[index] ) ) continue;
if( typeof src.data[index] === 'undefined' ) continue;
// add the values to total
rgb += ( src.data[index] << 16 ) + ( src.data[index + 1] << 8 ) + src.data[index + 2];
}
// get the average of all pixels in that area
rgb = rgb / ( s * 2 + 1);
// get index of current pixel
index = getIndex( x, y, w );
// set pixel in dest
dst.data[index] = ( rgb & 0xff0000 ) >> 16; // red
dst.data[index + 1] = ( rgb & 0x00ff00 ) >> 8; // green
dst.data[index + 2] = ( rgb & 0x0000ff ); // blue
dst.data[index + 3] = 255; // alpha
}
}
// add the new image data
ctx.putImageData( dst, 0, 0 );
}
function getIndex( x, y, w ) {
// calculate the appropriate index, since every pixel has 4 array values
return ( y * ( w * 4 ) + ( x * 4 ) );
}
So what is wrong with my algorithm? I'm a bit lost. Please note that I'm not looking for existing objects/libraries/files for canvas blurring. I like to reinvent everything to educate myself.
Edit: I also like to add that the values I get back are truly the values that represent the colours shown on the canvas. That means that's definitely a miscalculation in my algorithm.

You should average your channels separately. Dividing a packed three-channel value is unlikely to keep each channel within its byte.

The average between 0x030000 (dark red) and 0x000000 (black) becomes 0x018000, which gets a lot of green (0x80)
You should average the channels separately.

Related

Find Hex Colors and X,Y in Images

I am trying to find all the hex colors in an image and if possible, circle or highlight the X, Y position of where the hex color(s) are. My current code is attempting to find all colors and almost crashes my browser and my attempt to find the X,Y coordinates of each image isn't going good either.
I have two functions doing different things, it's what I have tried to work with to give an example of what has been attempted... Any help would be great!
Any assistance would be amazing!
<canvas id="myCanvas" width="240" height="297" style="border:1px solid #d3d3d3;">
Your browser does not support the HTML5 canvas tag.
</canvas>
<img id="origImage" width="220" height="277" src="loggraph.PNG">
<script>
function getPixel(imgData, index) {
var i = index*4, d = imgData.data;
return [d[i],d[i+1],d[i+2],d[i+3]] // [R,G,B,A]
}
function getPixelXY(imgData, x, y) {
return getPixel(imgData, y*imgData.width+x);
}
function goCheck() {
var cvs = document.createElement('canvas'),
img = document.getElementsByTagName("img")[0];
cvs.width = img.width; cvs.height = img.height;
var ctx = cvs.getContext("2d");
ctx.drawImage(img,0,0,cvs.width,cvs.height);
var idt = ctx.getImageData(0,0,cvs.width,cvs.height);
console.log(getPixel(idt, 852)); // returns array [red, green, blue, alpha]
console.log(getPixelXY(idt,1,1)); // same pixel using x,y
}
function getColors(){
var canvas = document.getElementById("myCanvas");
var devices = canvas.getContext("2d");
var imageData = devices.getImageData(0, 0, canvas.width, canvas.height);
var data = imageData.data;
// iterate over all pixels
for(var i = 0, n = data.length; i < n; i += 4) {
var r = data[i];
var g = data[i + 1];
var b = data[i + 2];
var rgb = "("+r+","+g+","+b+")";
var incoming = i*4, d = imageData.data;
var bah = [d[incoming],d[incoming+1],d[incoming+2],d[incoming+3]];
$('#list').append("<li>"+rgb+"</li>");
colorList.push(rgb);
}
$('#list').append("<li>"+[d[incoming],d[incoming+1],d[incoming+2],d[incoming+3]]+"</li>");
}
}
Must check all pixels
To find a pixel that matches a color will require, in the worst case (pixel of that color not in image), that you step over every pixel in the image.
How not to do it
Converting every pixel to a DOM string is about the worst way to do it, as DOM string use a lot of memory and CPU overhead, especially if instantiated using jQuery (which has its own additional baggage)
Hex color to array
To find the pixel you need only check each pixels color data against the HEX value. You convert the hex value to an array of 3 Bytes.
The following function will convert from CSS Hex formats "#HHH" "#HHHH", "#HHHHHH" and "#HHHHHHHH" ignoring the alpha part if included, to an array of integers 0-255
const hex2RGB = h => {
if(h.length === 4 || h.length === 5) {
return [parseInt(h[1] + h[1], 16), parseInt(h[2] + h[2], 16), parseInt(h[3] + h[3], 16)];
}
return [parseInt(h[1] + h[2], 16), parseInt(h[3] + h[4], 16), parseInt(h[5] + h[6], 16)];
}
Finding the pixel
I do not know how you plan to use such a feature so the example below is a general purpose method that will help and can be modified as needed
It will always find a pixel if you let it even if there is no perfect match. It does this by finding the closest color to the color you are looking for.
The reason that of finds the closest match is that when you draw an image onto a 2D canvas the pixel values are modified slightly if the image has transparent pixels (pre-multiplied alpha)
The function finds the pixel by measuring the spacial distance between the pixel and the hex color (simple geometry Pythagoras). The closest color is the one that is the smallest distance.
It will return the object
{
x, // the x coordinate of the match
y, // the y coordinate of the match
distance, // how closely the color matches the requested color.
// 0 means a perfect match
// to 441 completely different eg black and white
// value is floored to an integer value
}
If the image is tainted (cross origin, local device storage), or you pass something that can not be converted to pixels the function will return undefined
The function keeps a canvas that it uses to get pixel data as it assumes that it will be use many times. If the image is tainted it will catch the error (add a warning to the console), cleanup the tainted canvas and be ready for another image.
Usage
To use the function add it to your code base, it will setup automatically.
Get an image and a hex value and call the function with the image, CSS hex color, and optionally the threshold distance for the color match.
Eg find exact match for #FF0000
const result = findPixel(origImage, "#FF0000", 0); // find exact match for red
if (result) { // only if found
console.log("Found color #FF0000 at pixel " + result.x + ", " + result.y);
} else {
console.log("The color #FF0000 is not in the image");
}
or find color close to
const result = findPixel(origImage, "#FF0000", 20); // find a match for red
// within 20 units.
// A unit is 1 of 256
if (result) { // only if found
console.log("Found closest color within " + result.distance + "units of #FF0000 at pixel " + result.x + ", " + result.y);
}
or find closest
// find the closest, no threshold ensures a result
const result = findPixel(origImage, "#FF0000");
console.log("Found closest color within " + result.distance + "units of #FF0000 at pixel " + result.x + ", " + result.y);
Code
The function is as follows.
const findPixel = (() => {
var can, ctx;
function createCanvas(w, h) {
if (can === undefined){
can = document.createElement("canvas");
ctx = can.getContext("2d");
}
can.width = w;
can.height = h;
}
function getPixels(img) {
const w = img.naturalWidth || img.width, h = img.naturalHeight || img.height;
createCanvas(w, h);
ctx.drawImage(img, 0, 0);
try {
const imgData = ctx.getImageData(0, 0, w, h);
can.width = can.height = 1; // make canvas as small as possible so it wont
// hold memory. Leave in place to avoid instantiation overheads
return imgData;
} catch(e) {
console.warn("Image is un-trusted and pixel access is blocked");
ctx = can = undefined; // canvas and context can no longer be used so dump them
}
return {width: 0, height: 0, data: []}; // return empty pixel data
}
const hex2RGB = h => { // Hex color to array of 3 values
if(h.length === 4 || h.length === 5) {
return [parseInt(h[1] + h[1], 16), parseInt(h[2] + h[2], 16), parseInt(h[3] + h[3], 16)];
}
return [parseInt(h[1] + h[2], 16), parseInt(h[3] + h[4], 16), parseInt(h[5] + h[6], 16)];
}
const idx2Coord = (idx, w) => ({x: idx % w, y: idx / w | 0});
return function (img, hex, minDist = Infinity) {
const [r, g, b] = hex2RGB(hex);
const {width, height, data} = getPixels(img);
var idx = 0, found;
while (idx < data.length) {
const R = data[idx] - r;
const G = data[idx + 1] - g;
const B = data[idx + 2] - b;
const d = R * R + G * G + B * B;
if (d === 0) { // found exact match
return {...idx2Coord(idx / 4, width), distance: 0};
}
if (d < minDist) {
minDist = d;
found = idx;
}
idx += 4;
}
return found ? {...idx2Coord(found / 4, width), distance: minDist ** 0.5 | 0 } : undefined;
}
})();
This function has been tested and works as described above.
Note Going by the code in the your question the alpha value of the image and CSS hex color is ignored.
Note that if you intend to find many colors from the same image this function is not the best suited for you needs. If this is the case let me know in the comment and I can make changes or instruct you how to optimism the code for such uses.
Note It is not well suited for single use only. However if this is the case change the line const findPixel = (() => { to var findPixel = (() => { and after you have used it remove the reference findpixel = undefined; and JS will clean up any resources it holds.
Note If you also want to get the actual color of the closest found color that is trivial to add as well. Ask in the comments.
Note It is reasonably quick (you will be hard pressed to get a quicker result) but be warned that for very large images 4K and above it may take a bit, and on very low end devices it may cause a out of memory error. If this is a problem then another solution is possible but is far slower.

How to make sure my object has their own x and y

I am guessing that there may be an answer about this on the internet somewhere but I can not find it. I am making a graphing calculator and I tried to make "plots" follow a certain function y = 2x. Though i can not seem to find out how to make the plots have their own x and y (their unique x and y).
function CreateDot() {
this.ix = 1; //this is the x value of the function and is also used in y = 2x
this.fy = this.ix * 2; //this is the y value of the funciton y = 2x
this.newDot = function() {
//this is defining the x value of the plot and scaling the value to have it follow the grid
this.x1 = spacing * this.ix;
// this is defining the y value of the plot and scaling the value to have it follow the grid
this.y1 = 500 - spacing * this.fy; //
//this is the code for creating the "plot" which is a dot on the screen and red in colour
stroke(255,0,0);
strokeWeight(25);
//this is defining the position of the point with the values above x1 and y1
point(this.x1,this.y1);
//this is supposed to allow the next value of x=2 in the function y = 2x and so allowing the next coordinates of the plot to be ( 2, 4 ) and the next dot (3, 6) and so on.
this.ix = this.ix + 1;
}
}
I what I noticed is that after I made this a constructor function and put new dots into an array and limited it to 5, I ran it and one dot flew all the way to the right. I printed each of the objects x and y and they had the same values..
So my question is how do I make sure Each object has their own unique x and y values?
Thanks in advance!
You have to put the function either on the prototype and call it that way, or you have to pass the dots as parameters to the update function:
// Prototype
function CreateDot( spacing ) {
this.ix = 1;
this.fy = this.ix * 2;
this.x1 = spacing * this.ix;
this.y1 = 500 - spacing * this.fy;
}
CreateDot.prototype.addOne = function() {
// this refers to: 'the dot you call addOne upon'.
this.ix = this.ix + 1;
};
var myDot = new CreateDot( 250 );
console.log( 'before myDot.addOne() : ' + myDot.ix );
myDot.addOne();
console.log( 'after myDot.addOne() : ' + myDot.ix );
// Seperate function
var addOne_f = function( dot ) {
// this refers to 'the window instance', so you need to use the 'dot' parameter to refer to the dot.
dot.ix = dot.ix + 1;
};
console.log( 'before addOne_f( myDot ) : ' + myDot.ix );
addOne_f( myDot );
console.log( 'after addOne_f( myDot ) : ' + myDot.ix );
// Inside an array:
var dots = [
new CreateDot( 200 ),
new CreateDot( 225 ),
new CreateDot( 250 )
];
// Update all dots
dots.forEach( dot => dot.addOne());
// Update the second dot again to show they're not linked
dots[ 1 ].addOne();
console.log( dots.map( dot => dot.ix ));

hough transform - javascript - node.js

So, i'm trying to implement hough transform, this version is 1-dimensional (its for all dims reduced to 1 dim optimization) version based on the minor properties.
Enclosed is my code, with a sample image... input and output.
Obvious question is what am i doing wrong. I've tripled check my logic and code and it looks good also my parameters. But obviously i'm missing on something.
Notice that the red pixels are supposed to be ellipses centers , while the blue pixels are edges to be removed (belong to the ellipse that conform to the mathematical equations).
also, i'm not interested in openCV / matlab / ocatve / etc.. usage (nothing against them).
Thank you very much!
var fs = require("fs"),
Canvas = require("canvas"),
Image = Canvas.Image;
var LEAST_REQUIRED_DISTANCE = 40, // LEAST required distance between 2 points , lets say smallest ellipse minor
LEAST_REQUIRED_ELLIPSES = 6, // number of found ellipse
arr_accum = [],
arr_edges = [],
edges_canvas,
xy,
x1y1,
x2y2,
x0,
y0,
a,
alpha,
d,
b,
max_votes,
cos_tau,
sin_tau_sqr,
f,
new_x0,
new_y0,
any_minor_dist,
max_minor,
i,
found_minor_in_accum,
arr_edges_len,
hough_file = 'sample_me2.jpg',
edges_canvas = drawImgToCanvasSync(hough_file); // make sure everything is black and white!
arr_edges = getEdgesArr(edges_canvas);
arr_edges_len = arr_edges.length;
var hough_canvas_img_data = edges_canvas.getContext('2d').getImageData(0, 0, edges_canvas.width,edges_canvas.height);
for(x1y1 = 0; x1y1 < arr_edges_len ; x1y1++){
if (arr_edges[x1y1].x === -1) { continue; }
for(x2y2 = 0 ; x2y2 < arr_edges_len; x2y2++){
if ((arr_edges[x2y2].x === -1) ||
(arr_edges[x2y2].x === arr_edges[x1y1].x && arr_edges[x2y2].y === arr_edges[x1y1].y)) { continue; }
if (distance(arr_edges[x1y1],arr_edges[x2y2]) > LEAST_REQUIRED_DISTANCE){
x0 = (arr_edges[x1y1].x + arr_edges[x2y2].x) / 2;
y0 = (arr_edges[x1y1].y + arr_edges[x2y2].y) / 2;
a = Math.sqrt((arr_edges[x1y1].x - arr_edges[x2y2].x) * (arr_edges[x1y1].x - arr_edges[x2y2].x) + (arr_edges[x1y1].y - arr_edges[x2y2].y) * (arr_edges[x1y1].y - arr_edges[x2y2].y)) / 2;
alpha = Math.atan((arr_edges[x2y2].y - arr_edges[x1y1].y) / (arr_edges[x2y2].x - arr_edges[x1y1].x));
for(xy = 0 ; xy < arr_edges_len; xy++){
if ((arr_edges[xy].x === -1) ||
(arr_edges[xy].x === arr_edges[x2y2].x && arr_edges[xy].y === arr_edges[x2y2].y) ||
(arr_edges[xy].x === arr_edges[x1y1].x && arr_edges[xy].y === arr_edges[x1y1].y)) { continue; }
d = distance({x: x0, y: y0},arr_edges[xy]);
if (d > LEAST_REQUIRED_DISTANCE){
f = distance(arr_edges[xy],arr_edges[x2y2]); // focus
cos_tau = (a * a + d * d - f * f) / (2 * a * d);
sin_tau_sqr = (1 - cos_tau * cos_tau);//Math.sqrt(1 - cos_tau * cos_tau); // getting sin out of cos
b = (a * a * d * d * sin_tau_sqr ) / (a * a - d * d * cos_tau * cos_tau);
b = Math.sqrt(b);
b = parseInt(b.toFixed(0));
d = parseInt(d.toFixed(0));
if (b > 0){
found_minor_in_accum = arr_accum.hasOwnProperty(b);
if (!found_minor_in_accum){
arr_accum[b] = {f: f, cos_tau: cos_tau, sin_tau_sqr: sin_tau_sqr, b: b, d: d, xy: xy, xy_point: JSON.stringify(arr_edges[xy]), x0: x0, y0: y0, accum: 0};
}
else{
arr_accum[b].accum++;
}
}// b
}// if2 - LEAST_REQUIRED_DISTANCE
}// for xy
max_votes = getMaxMinor(arr_accum);
// ONE ellipse has been detected
if (max_votes != null &&
(max_votes.max_votes > LEAST_REQUIRED_ELLIPSES)){
// output ellipse details
new_x0 = parseInt(arr_accum[max_votes.index].x0.toFixed(0)),
new_y0 = parseInt(arr_accum[max_votes.index].y0.toFixed(0));
setPixel(hough_canvas_img_data,new_x0,new_y0,255,0,0,255); // Red centers
// remove the pixels on the detected ellipse from edge pixel array
for (i=0; i < arr_edges.length; i++){
any_minor_dist = distance({x:new_x0, y: new_y0}, arr_edges[i]);
any_minor_dist = parseInt(any_minor_dist.toFixed(0));
max_minor = b;//Math.max(b,arr_accum[max_votes.index].d); // between the max and the min
// coloring in blue the edges we don't need
if (any_minor_dist <= max_minor){
setPixel(hough_canvas_img_data,arr_edges[i].x,arr_edges[i].y,0,0,255,255);
arr_edges[i] = {x: -1, y: -1};
}// if
}// for
}// if - LEAST_REQUIRED_ELLIPSES
// clear accumulated array
arr_accum = [];
}// if1 - LEAST_REQUIRED_DISTANCE
}// for x2y2
}// for xy
edges_canvas.getContext('2d').putImageData(hough_canvas_img_data, 0, 0);
writeCanvasToFile(edges_canvas, __dirname + '/hough.jpg', function() {
});
function getMaxMinor(accum_in){
var max_votes = -1,
max_votes_idx,
i,
accum_len = accum_in.length;
for(i in accum_in){
if (accum_in[i].accum > max_votes){
max_votes = accum_in[i].accum;
max_votes_idx = i;
} // if
}
if (max_votes > 0){
return {max_votes: max_votes, index: max_votes_idx};
}
return null;
}
function distance(point_a,point_b){
return Math.sqrt((point_a.x - point_b.x) * (point_a.x - point_b.x) + (point_a.y - point_b.y) * (point_a.y - point_b.y));
}
function getEdgesArr(canvas_in){
var x,
y,
width = canvas_in.width,
height = canvas_in.height,
pixel,
edges = [],
ctx = canvas_in.getContext('2d'),
img_data = ctx.getImageData(0, 0, width, height);
for(x = 0; x < width; x++){
for(y = 0; y < height; y++){
pixel = getPixel(img_data, x,y);
if (pixel.r !== 0 &&
pixel.g !== 0 &&
pixel.b !== 0 ){
edges.push({x: x, y: y});
}
} // for
}// for
return edges
} // getEdgesArr
function drawImgToCanvasSync(file) {
var data = fs.readFileSync(file)
var canvas = dataToCanvas(data);
return canvas;
}
function dataToCanvas(imagedata) {
img = new Canvas.Image();
img.src = new Buffer(imagedata, 'binary');
var canvas = new Canvas(img.width, img.height);
var ctx = canvas.getContext('2d');
ctx.patternQuality = "best";
ctx.drawImage(img, 0, 0, img.width, img.height,
0, 0, img.width, img.height);
return canvas;
}
function writeCanvasToFile(canvas, file, callback) {
var out = fs.createWriteStream(file)
var stream = canvas.createPNGStream();
stream.on('data', function(chunk) {
out.write(chunk);
});
stream.on('end', function() {
callback();
});
}
function setPixel(imageData, x, y, r, g, b, a) {
index = (x + y * imageData.width) * 4;
imageData.data[index+0] = r;
imageData.data[index+1] = g;
imageData.data[index+2] = b;
imageData.data[index+3] = a;
}
function getPixel(imageData, x, y) {
index = (x + y * imageData.width) * 4;
return {
r: imageData.data[index+0],
g: imageData.data[index+1],
b: imageData.data[index+2],
a: imageData.data[index+3]
}
}
It seems you try to implement the algorithm of Yonghong Xie; Qiang Ji (2002). A new efficient ellipse detection method 2. p. 957.
Ellipse removal suffers from several bugs
In your code, you perform the removal of found ellipse (step 12 of the original paper's algorithm) by resetting coordinates to {-1, -1}.
You need to add:
`if (arr_edges[x1y1].x === -1) break;`
at the end of the x2y2 block. Otherwise, the loop will consider -1, -1 as a white point.
More importantly, your algorithm consists in erasing every point which distance to the center is smaller than b. b supposedly is the minor axis half-length (per the original algorithm). But in your code, variable b actually is the latest (and not most frequent) half-length, and you erase points with a distance lower than b (instead of greater, since it's the minor axis). In other words, you clear all points inside a circle with a distance lower than latest computed axis.
Your sample image can actually be processed with a clearing of all points inside a circle with a distance lower than selected major axis with:
max_minor = arr_accum[max_votes.index].d;
Indeed, you don't have overlapping ellipses and they are spread enough. Please consider a better algorithm for overlapping or closer ellipses.
The algorithm mixes major and minor axes
Step 6 of the paper reads:
For each third pixel (x, y), if the distance between (x, y) and (x0,
y0) is greater than the required least distance for a pair of pixels
to be considered then carry out the following steps from (7) to (9).
This clearly is an approximation. If you do so, you will end up considering points further than the minor axis half length, and eventually on the major axis (with axes swapped). You should make sure the distance between the considered point and the tested ellipse center is smaller than currently considered major axis half-length (condition should be d <= a). This will help with the ellipse erasing part of the algorithm.
Also, if you also compare with the least distance for a pair of pixels, as per the original paper, 40 is too large for the smaller ellipse in your picture. The comment in your code is wrong, it should be at maximum half the smallest ellipse minor axis half-length.
LEAST_REQUIRED_ELLIPSES is too small
This parameter is also misnamed. It is the minimum number of votes an ellipse should get to be considered valid. Each vote corresponds to a pixel. So a value of 6 means that only 6+2 pixels make an ellipse. Since pixels coordinates are integers and you have more than 1 ellipse in your picture, the algorithm might detect ellipses that are not, and eventually clear edges (especially when combined with the buggy ellipse erasing algorithm). Based on tests, a value of 100 will find four of the five ellipses of your picture, while 80 will find them all. Smaller values will not find the proper centers of the ellipses.
Sample image is not black & white
Despite the comment, sample image is not exactly black and white. You should convert it or apply some threshold (e.g. RGB values greater than 10 instead of simply different form 0).
Diff of minimum changes to make it work is available here:
https://gist.github.com/pguyot/26149fec29ffa47f0cfb/revisions
Finally, please note that parseInt(x.toFixed(0)) could be rewritten Math.floor(x), and you probably want to not truncate all floats like this, but rather round them, and proceed where needed: the algorithm to erase the ellipse from the picture would benefit from non truncated values for the center coordinates. This code definitely could be improved further, for example it currently computes the distance between points x1y1 and x2y2 twice.

Canvas - Get position of colour if available

Is it possible to get the position of a colour on canvas.
I know you can get the colour at a position like this
context.getImageData( arguments ).data
But I would like to try and find a colour in canvas, so say I would have this colour black.
rgb(0, 0, 0);
I would like to get the position of that colour if it exists on canvas, I've asked Google but I only get the Get colour at position which is the opposite of what I need.
As mentioned already you need to iterate over the pixel buffer.
Here is one way you can do it:
(Online demo here)
function getPositionFromColor(ctx, color) {
var w = ctx.canvas.width,
h = ctx.canvas.height,
data = ctx.getImageData(0, 0, w, h), /// get image data
buffer = data.data, /// and its pixel buffer
len = buffer.length, /// cache length
x, y = 0, p, px; /// for iterating
/// iterating x/y instead of forward to get position the easy way
for(;y < h; y++) {
/// common value for all x
p = y * 4 * w;
for(x = 0; x < w; x++) {
/// next pixel (skipping 4 bytes as each pixel is RGBA bytes)
px = p + x * 4;
/// if red component match check the others
if (buffer[px] === color[0]) {
if (buffer[px + 1] === color[1] &&
buffer[px + 2] === color[2]) {
return [x, y];
}
}
}
}
return null;
}
This will return the x/y position of the first match of the color you give (color = [r, g, b]). If color is not found the function returns null.
(the code can be optimized in various ways by I haven't addressed that here).
You would need to iterate over the data property and check the RGB values. Basically you would iterate in groups of 4, as each pixel is stored as 4 indices, and compare the values accordingly.

Making a blur algorithm work with canvas image data?

I have the following simple blur algorithm:
for (y = 0; y < height; ++y) {
for (x = 0; x < width; ++x) {
total = 0
for (ky = -radius; ky <= radius; ++ky)
for (kx = -radius; kx <= radius; ++kx)
total += source(x + kx, y + ky)
dest(x, y) = total / (radius * 2 + 1) ^ 2
}
}
And I need to make this work with an array, generated by canvas with getImageData(). The problem is that the array from canvas is one dimentional, and the algorithm needs a two dimentional one, because "kx" and "ky" are distances from the current pixel, and in a two dimentional array you just change one of the indexes in order to move left or right on the grid, but in a one dimentional array (which is trying to represent a two-dim. one) you can't do that.
What do you think, how should I approach this problem? In my opinion, the entire problem is here:
total += source(x + kx, y + ky)
This line needs to get the correct pixels from the one-dimentional array and it should work fine.
P.S. I forgot to say that I'm trying to blur the image one channel at a time, like this:
for (var i=0; i<imgData.data.length; i+=4) {
red[index] = imgData.data[i];
green[index] = imgData.data[i+1];
blue[index] = imgData.data[i+2];
index++;
}
and I'm passing each array (red, green and blue) individually to the algorithm.
In order access a single dimensional array with two dimensional coordinates, you have the following formula:
var pixel = pixels[ ( ( y * width ) + x ) * 4 ];
The * 4 was added to account for the r,g,b,a values.
xResolution is the width if the image.
So source() would be:
function source( imageArray, x, y, imageWidth )
{
var pos = ( ( y * imageWidth ) + x ) * 4;
var val =
{
r : imageArray[ pos ],
g : imageArray[ pos + 1 ],
b : imageArray[ pos + 2 ],
a : imageArray[ pos + 3 ]
};
return val;
}

Categories

Resources