aba*_*haw 13 javascript image node.js
我一直在研究这个问题已经有一段时间没什么可喜的结果了.我试图将图像分割成相似颜色的连接区域.(基本上将所有像素的列表分成多个组(每个组包含属于它的像素的坐标并共享相似的颜色).
例如:http: //unsplash.com/photos/SoC1ex6sI4w/
在这张图片中,顶部的乌云可能会落入一组.山上的一些灰色岩石在另一个,另一些是橙色的草.雪会是另一个 - 背包的红色 - 等等.
我正在尝试设计一种既准确又高效的算法(它需要在中端笔记本电脑级硬件上运行几毫秒)
以下是我的尝试:
使用基于连通分量的算法从左上角扫描每个像素,从左到右扫描每行像素(并将当前像素与顶部像素和左像素进行比较).使用CIEDE2000色差公式,如果顶部或左侧的像素在一定范围内,那么它将被视为"相似"并且是该组的一部分.
这种方法有效 - 但问题在于它依赖于具有锐边的颜色区域 - 如果任何颜色组通过软渐变连接,它将沿着该渐变向下移动并继续"连接"像素,因为各个像素之间的差异是比较小到足以被认为是"相似的".
为了解决这个问题,我选择将每个访问过的像素的颜色设置为大多数"相似"相邻像素(顶部或左侧)的颜色.如果没有相似的像素,则保留其原始颜色.这在某种程度上解决了更加模糊的边界或柔化边缘的问题,因为随着算法的进展,新组的第一种颜色将被"携带",并且最终颜色与当前比较颜色之间的差异将超过"相似性"威胁和不再是该群体的一部分.
希望这是有道理的.问题是这些选项都没有真正起作用.在上面的图像上返回的不是干净的组,而是嘈杂的碎片组,这不是我想要的.
我不是专门寻找代码 - 而是关于如何构建算法以成功解决这个问题的更多想法.有没有人有这个想法?
谢谢!
您可以转换RGB为HSL更容易计算颜色之间的距离.我正在设置行中的色差容差:
if (color_distance(original_pixels[i], group_headers[j]) < 0.3) {...}
Run Code Online (Sandbox Code Playgroud)
如果更改0.3,您可以获得不同的结果.
看它工作.
请告诉我它是否有帮助.
function hsl_to_rgb(h, s, l) {
// from http://stackoverflow.com/questions/2353211/hsl-to-rgb-color-conversion
var r, g, b;
if (s == 0) {
r = g = b = l; // achromatic
} else {
var hue2rgb = function hue2rgb(p, q, t) {
if (t < 0) t += 1;
if (t > 1) t -= 1;
if (t < 1 / 6) return p + (q - p) * 6 * t;
if (t < 1 / 2) return q;
if (t < 2 / 3) return p + (q - p) * (2 / 3 - t) * 6;
return p;
}
var q = l < 0.5 ? l * (1 + s) : l + s - l * s;
var p = 2 * l - q;
r = hue2rgb(p, q, h + 1 / 3);
g = hue2rgb(p, q, h);
b = hue2rgb(p, q, h - 1 / 3);
}
return [Math.round(r * 255), Math.round(g * 255), Math.round(b * 255)];
}
function rgb_to_hsl(r, g, b) {
// from http://stackoverflow.com/questions/2353211/hsl-to-rgb-color-conversion
r /= 255, g /= 255, b /= 255;
var max = Math.max(r, g, b),
min = Math.min(r, g, b);
var h, s, l = (max + min) / 2;
if (max == min) {
h = s = 0; // achromatic
} else {
var d = max - min;
s = l > 0.5 ? d / (2 - max - min) : d / (max + min);
switch (max) {
case r:
h = (g - b) / d + (g < b ? 6 : 0);
break;
case g:
h = (b - r) / d + 2;
break;
case b:
h = (r - g) / d + 4;
break;
}
h /= 6;
}
return [h, s, l];
}
function color_distance(v1, v2) {
// from http://stackoverflow.com/a/13587077/1204332
var i,
d = 0;
for (i = 0; i < v1.length; i++) {
d += (v1[i] - v2[i]) * (v1[i] - v2[i]);
}
return Math.sqrt(d);
};
function round_to_groups(group_nr, x) {
var divisor = 255 / group_nr;
return Math.ceil(x / divisor) * divisor;
};
function pixel_data_to_key(pixel_data) {
return pixel_data[0].toString() + '-' + pixel_data[1].toString() + '-' + pixel_data[2].toString();
}
function posterize(context, image_data, palette) {
for (var i = 0; i < image_data.data.length; i += 4) {
rgb = image_data.data.slice(i, i + 3);
hsl = rgb_to_hsl(rgb[0], rgb[1], rgb[2]);
key = pixel_data_to_key(hsl);
if (key in palette) {
new_hsl = palette[key];
new_rgb = hsl_to_rgb(new_hsl[0], new_hsl[1], new_hsl[2]);
rgb = hsl_to_rgb(hsl);
image_data.data[i] = new_rgb[0];
image_data.data[i + 1] = new_rgb[1];
image_data.data[i + 2] = new_rgb[2];
}
}
context.putImageData(image_data, 0, 0);
}
function draw(img) {
var canvas = document.getElementById('canvas');
var context = canvas.getContext('2d');
context.drawImage(img, 0, 0, canvas.width, canvas.height);
img.style.display = 'none';
var image_data = context.getImageData(0, 0, canvas.width, canvas.height);
var data = image_data.data;
context.drawImage(target_image, 0, 0, canvas.width, canvas.height);
data = context.getImageData(0, 0, canvas.width, canvas.height).data;
original_pixels = [];
for (i = 0; i < data.length; i += 4) {
rgb = data.slice(i, i + 3);
hsl = rgb_to_hsl(rgb[0], rgb[1], rgb[2]);
original_pixels.push(hsl);
}
group_headers = [];
groups = {};
for (i = 0; i < original_pixels.length; i += 1) {
if (group_headers.length == 0) {
group_headers.push(original_pixels[i]);
}
group_found = false;
for (j = 0; j < group_headers.length; j += 1) {
// if a similar color was already observed
if (color_distance(original_pixels[i], group_headers[j]) < 0.3) {
group_found = true;
if (!(pixel_data_to_key(original_pixels[i]) in groups)) {
groups[pixel_data_to_key(original_pixels[i])] = group_headers[j];
}
}
if (group_found) {
break;
}
}
if (!group_found) {
if (group_headers.indexOf(original_pixels[i]) == -1) {
group_headers.push(original_pixels[i]);
}
if (!(pixel_data_to_key(original_pixels[i]) in groups)) {
groups[pixel_data_to_key(original_pixels[i])] = original_pixels[i];
}
}
}
posterize(context, image_data, groups)
}
var target_image = new Image();
target_image.crossOrigin = "";
target_image.onload = function() {
draw(target_image)
};
target_image.src = "http://i.imgur.com/zRzdADA.jpg";Run Code Online (Sandbox Code Playgroud)
canvas {
width: 300px;
height: 200px;
}Run Code Online (Sandbox Code Playgroud)
<canvas id="canvas"></canvas>Run Code Online (Sandbox Code Playgroud)