的node.js
所以,我试图实现hough变换,这个版本是基于次要属性的1维(其所有变暗减至1个暗淡优化)版本。 封闭的是我的代码,带有示例图像...输入和输出。
明显的问题是我做错了什么。 我翻了三倍检查我的逻辑和代码,它看起来也很好,我的参数。 但显然我错过了某些东西。
注意,红色像素应该是椭圆中心,而蓝色像素是要去除的边缘(属于符合数学方程的椭圆)。
另外,我不感兴趣openCV / matlab / ocatve /等..使用(没有任何反对他们)。 非常感谢你!
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]
}
}
看来你试图实现谢永红的算法; 强记(2002)。 一种新的高效椭圆检测方法2. p。 957。
椭圆删除遭受几个错误
在您的代码中,通过将坐标重置为{-1, -1}
来执行找到的椭圆的移除(原始纸张算法的步骤12)。
您需要添加:
`if (arr_edges[x1y1].x === -1) break;`
在x2y2块的末尾。 否则,循环会将-1,-1视为白点。
更重要的是,你的算法在于擦除到中心的距离小于b
每个点。 b
应该是短轴半长度(按照原始算法)。 但是在你的代码中,变量b
实际上是最新的 (而不是最常见的)半长,并且你擦除的距离小于b(而不是更大,因为它是次轴)。 换句话说,清除距离最近计算轴的距离的圆内的所有点。
您的样本图像实际上可以通过清除圆圈内所有点的距离低于所选长轴的方式进行处理:
max_minor = arr_accum[max_votes.index].d;
事实上,你没有重叠的椭圆,并且它们已经足够传播了。 请考虑更好的重叠算法或更接近的省略号。
该算法混合主轴和副轴
该文件的第6步说明:
对于每个第三个像素(x,y),如果(x,y)和(x0,y0)之间的距离大于要考虑的一对像素所需的最小距离,则执行以下步骤(7)到(9)。
这显然是一个近似值。 如果这样做,最终将考虑比短轴半长度更远的点,并最终在长轴上(轴交换)。 您应该确保考虑点与测试椭圆中心之间的距离小于当前考虑的长轴半长度(条件应该是d <= a
)。 这将有助于算法的椭圆擦除部分。
此外,如果您还将一对像素的最小距离与原始纸张进行比较,则40对于图片中较小的椭圆太大。 你的代码中的注释是错误的,它应该是最小椭圆短轴半长的一半。
LEAST_REQUIRED_ELLIPSES太小
该参数也是错误的。 这是椭圆应被视为有效的最小投票数。 每个投票对应一个像素。 所以6的值意味着只有6 + 2像素形成一个椭圆。 由于像素坐标是整数,并且图片中有多于1个椭圆,因此该算法可能会检测到不是椭圆,并且最终会清除边缘(特别是与错误的椭圆擦除算法结合使用时)。 基于测试,100的值将会找到图片的五个椭圆中的四个,而80会全部找到它们。 较小的值不会找到椭圆的正确中心。
示例图像不是黑色和白色
尽管有评论,示例图像并不完全是黑色和白色。 您应该转换它或应用一些阈值(例如,RGB值大于10而不是简单的不同形式0)。
最小变化的差异,使其工作可在这里:https://gist.github.com/pguyot/26149fec29ffa47f0cfb/revisions
最后,请注意parseInt(x.toFixed(0))
可能会被重写为Math.floor(x)
,并且您可能不希望像这样截断所有的浮点数,而是将它们四舍五入,然后在需要的地方继续:要擦除的算法图片中的椭圆将受益于中心坐标的非截断值。 这个代码绝对可以进一步改进,例如它目前计算两次点x1y1
和x2y2
之间的距离。
上一篇: node.js
下一篇: Removing consecutive occurrences from end of list python