我正在尝试使用Javascript来查找图像的最暗区域。 到目前为止,这就是我所拥有的: https://jsfiddle.net/brampower/bv78rmz8/
function rgbToHsl(r, g, b) {
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: h,
s: s,
l: l,
})
}
function solve_darkest(url, callback) {
var image = new Image();
image.src = url;
image.onload = function(){
var canvas = document.createElement('canvas');
canvas.width = 300;
canvas.height = 300;
var context = canvas.getContext("2d");
context.drawImage(image, 0, 0);
var imgData = context.getImageData(0, 0, 300, 300);
var pixel = 0;
var darkest_pixel_lightness = 100;
var darkest_pixel_location = 0;
for (var i = 0; i < imgData.data.length; i += 4) {
red = imgData.data[i + 0];
green = imgData.data[i + 1];
blue = imgData.data[i + 2];
alpha = imgData.data[i + 3];
var hsl = rgbToHsl(red, green, blue);
var lightness = hsl.l;
if (lightness < darkest_pixel_lightness) {
darkest_pixel_lightness = lightness;
darkest_pixel_location = pixel;
}
pixel++;
}
var y = Math.floor(darkest_pixel_location/200);
var x = darkest_pixel_location-(y*200);
callback(x,y);
};
}
image_url = 'http://i.imgur.com/j6oJO8s.png';
solve_darkest(image_url, function(x, y) {
alert('x: '+x+' y: '+y);
});
由于受到污染的画布,它在JSFiddle中无法工作,但希望这会给你一个想法。对于示例图像,我的JS当前返回以下坐标: x:140 y:117
这些不是正确的坐标。此图像的最暗像素应围绕以下坐标: x:95 y:204
我无法弄清楚为什么坐标如此偏离。这里的任何人都愿意对我做错的事情有所了解吗?
答案 0 :(得分:1)
好的,我刚刚测试了你的jsfiddle。
对于受污染的画布,只需更改crossOrigin属性:
var image = new Image();
image.crossOrigin = "Anonymous";
对于不正确的像素,有几个问题。
画布尺寸不正确。如果图像小于画布大小,则算法会测试不在图像中但在画布中的像素。由于您不会丢弃透明像素,因此您还要测试0,0,0(RGB)像素,该像素应为黑色#000000。
不正确的1维数组到2维转换。您使用的公式不正确,因为您将宽度和高度设置为300,但在公式中使用200。我建议创建一个变量并将其作为参考。
如果您怀疑像素是否完全存在,请创建一个小图片,如5x5像素大小,并检查算法是否返回您期望的结果。
我更新了jsfiddle,我认为现在这是正确的。另外,删除了HTML中的img元素,并将画布附加到正文:https://jsfiddle.net/Draznel/597u5h0c/1/
答案 1 :(得分:0)
如果没有JSFiddle工作,我最好的猜测是
中的逻辑var y = Math.floor(darkest_pixel_location/200);
var x = darkest_pixel_location-(y*200);
由于两个原因,不正确。
1)图像的宽度/高度为300像素,而不是200
2)imagedata按x排序第一个和第二个
要获得正确的x和y坐标,我认为以下代码可以正常工作:
var x = Math.floor(darkest_pixel_location / imageWidth);
var y = darkest_pixel_location % imageWidth;
答案 2 :(得分:0)
实现硬编码变量的恶劣后果的好时机......
您正在创建一个300x300的画布,并为其绘制相同尺寸的png。不幸的是,您使用200来确定给定所选像素的索引的x,y pos。提示:制作300x300的白色图像。将单个像素设置为(95,204)为黑色。运行未经修改的代码,您将获得95,306。更改为与图像大小相同,您将获得(95,204)作为答案。
所以,你很难抱怨300x300图像的索引在以适合索引成200x200图像的方式进行操作时会返回错误的位置。
就个人而言,我会替换:
var y = Math.floor(darkest_pixel_location/200);
var x = darkest_pixel_location-(y*200);
与
var y = Math.floor(darkest_pixel_location/this.width);
var x = darkest_pixel_location-(y*this.width);
然后将索引转换回正确的 x,y坐标。
也就是说,您提供的图像实际上并没有在指定位置显示它的最暗点。 95,204处的像素值为#37614e,或rgb(55,97,78)。
因此,我希望您现在可以看到我建议在其他光线图像中使用单个暗像素的目的。您可以减少一次尝试调试的问题数量。在这种情况下 - &#34;我可以将索引转换回2d坐标吗?&#34;。一旦完成,&#34;我确实在我认为的地方有一个黑点?&#34;
在您的情况下 - 这两个问题的答案都是没有!不完全是最有利于开始调试的地方......
<小时/> 做了一些评论,更好地理解了手头的问题。
好的,根据评论中的讨论 - 任务是找到图像中暗区域的左上角 - 这样的搜索应该在(至今)未知区域上的平均结果,使得局部最小值或最大值(暗像素或亮像素)不会对所识别的感兴趣区域产生不利影响。
理想情况下,可以迭代地运行代码 - 尝试使用块大小为1,然后尝试块大小为2等,增加块大小,直到两次运行的结果相同或在一定的限制。
即如果我使用9的块大小搜索并获得位置93,211然后获得相同的块大小为10(而所有先前的块大小值返回不同的结果)那么我&#39; d我可能会对我正确识别感兴趣的领域感到相当自信。
这里有一些可以咀嚼的代码。您会注意到我已经离开了您的功能并创建了另一个非常相似的功能。我希望你会发现它合适。 :)
"use strict";
function newEl(tag){return document.createElement(tag)}
function newTxt(txt){return document.createTextNode(txt)}
function byId(id){return document.getElementById(id)}
function allByClass(clss,parent){return (parent==undefined?document:parent).getElementsByClassName(clss)}
function allByTag(tag,parent){return (parent==undefined?document:parent).getElementsByTagName(tag)}
function toggleClass(elem,clss){elem.classList.toggle(clss)}
function addClass(elem,clss){elem.classList.add(clss)}
function removeClass(elem,clss){elem.classList.remove(clss)}
function hasClass(elem,clss){elem.classList.contains(clss)}
// useful for HtmlCollection, NodeList, String types
function forEach(array, callback, scope){for (var i=0,n=array.length; i<n; i++)callback.call(scope, array[i], i, array);} // passes back stuff we need
// callback gets data via the .target.result field of the param passed to it.
function loadFileObject(fileObj, loadedCallback){var a = new FileReader();a.onload = loadedCallback;a.readAsDataURL( fileObj );}
function ajaxGet(url, onLoad, onError)
{
var ajax = new XMLHttpRequest();
ajax.onload = function(){onLoad(this);}
ajax.onerror = function(){console.log("ajax request failed to: "+url);onError(this);}
ajax.open("GET",url,true);
ajax.send();
}
function ajaxPost(url, phpPostVarName, data, onSucess, onError)
{
var ajax = new XMLHttpRequest();
ajax.onload = function(){ onSucess(this);}
ajax.onerror = function() {console.log("ajax request failed to: "+url);onError(this);}
ajax.open("POST", url, true);
ajax.setRequestHeader("Content-type","application/x-www-form-urlencoded");
ajax.send(phpPostVarName+"=" + encodeURI(data) );
}
function ajaxPostForm(url, formElem, onSuccess, onError)
{
var formData = new FormData(formElem);
ajaxPostFormData(url, formData, onSuccess, onError)
}
function ajaxPostFormData(url, formData, onSuccess, onError)
{
var ajax = new XMLHttpRequest();
ajax.onload = function(){onSuccess(this);}
ajax.onerror = function(){onError(this);}
ajax.open("POST",url,true);
ajax.send(formData);
}
function getTheStyle(tgtElement)
{
var result = {}, properties = window.getComputedStyle(tgtElement, null);
forEach(properties, function(prop){result[prop] = properties.getPropertyValue(prop);});
return result;
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
window.addEventListener('load', onDocLoaded, false);
function onDocLoaded(evt)
{
// var image_url = 'http://i.imgur.com/j6oJO8s.png';
// var image_url = 'onePixel.png';
var image_url = 'j6oJO8s.png';
byId('theImage').src = image_url;
solve_darkest(image_url, function(x,y){alert('x: '+x+' y: '+y);} );
solve_darkest_2(image_url, function(x,y){alert('x: '+x+' y: '+y);} );
}
function rgbToHsl(r, g, b)
{
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: h,
s: s,
l: l,
})
}
function solve_darkest(url, callback)
{
var image = new Image();
image.src = url;
image.onload = function()
{
var canvas = document.createElement('canvas');
canvas.width = 300;
canvas.height = 300;
var context = canvas.getContext("2d");
context.drawImage(image, 0, 0);
var imgData = context.getImageData(0, 0, 300, 300);
var pixel = 0;
var darkest_pixel_lightness = 100;
var darkest_pixel_location = 0;
for (var i = 0; i < imgData.data.length; i += 4)
{
var red = imgData.data[i + 0];
var green = imgData.data[i + 1];
var blue = imgData.data[i + 2];
var alpha = imgData.data[i + 3];
var hsl = rgbToHsl(red, green, blue);
var lightness = hsl.l;
if (lightness < darkest_pixel_lightness)
{
darkest_pixel_lightness = lightness;
darkest_pixel_location = pixel;
console.log("Darkest found at index: " + pixel);
}
pixel++;
}
// var y = Math.floor(darkest_pixel_location/200);
// var x = darkest_pixel_location-(y*200);
var y = Math.floor(darkest_pixel_location/this.width);
var x = darkest_pixel_location-(y*this.width);
callback(x,y);
};
}
function solve_darkest_2(url, callback)
{
var image = new Image();
image.src = url;
image.onload = function()
{
var canvas = document.createElement('canvas');
canvas.width = 300;
canvas.height = 300;
var context = canvas.getContext("2d");
context.drawImage(image, 0, 0);
var imgData = context.getImageData(0,0, canvas.width, canvas.height);
var darkest_pixel_luminance = 100;
var darkest_pixel_xPos = 0;
var darkest_pixel_yPos = 0;
for (var y=0; y<canvas.height; y++)
{
for (var x=0; x<canvas.width; x++)
{
var luminance = averagePixels(imgData, x, y, 10);
if (luminance < darkest_pixel_luminance)
{
darkest_pixel_luminance = luminance;
darkest_pixel_xPos = x;
darkest_pixel_yPos = y;
}
}
}
callback(darkest_pixel_xPos,darkest_pixel_yPos);
};
}
function averagePixels(imgData, xPos, yPos, averagingBlockSize)
{
// var ctx = canvas.getContext("2d");
// var imgData = ctx.getImageData( 0, 0, canvas.width, canvas.height );
// imgData
// we average pixels found in a square region, we need to know how many pixels
// are in the region to divide the accumalated totals by the number of samples (pixels) in the
// averaging square
var numPixelsMax = averagingBlockSize * averagingBlockSize;
var numPixelsActual = 0;
var red, green, blue;
red = green = blue = 0;
var rowStride = imgData.width * 4; // add this to an index into the canvas's data to get the pixel
// immediatelly below it.
var x, y;
var initialIndex = ((yPos * imgData.width) + xPos) * 4;
var index = initialIndex;
var pixel = 0;
var darkest_pixel_lightness = 100;
var darkest_pixel_location = 0;
for (y=0; y<averagingBlockSize; y++)
{
index = initialIndex + y * rowStride;
for (x=0; x<averagingBlockSize; x++)
{
if ((x+xPos < imgData.width) && (y+yPos < imgData.height))
{
red += imgData.data[index+0];
green += imgData.data[index+1];
blue += imgData.data[index+2];
numPixelsActual++;
}
index += 4;
}
}
red /= numPixelsActual;
green /= numPixelsActual;
blue /= numPixelsActual;
var hsl = rgbToHsl(red, green, blue);
var luminance = hsl.l;
return luminance;
}
&#13;
img
{
border: solid 1px red;
}
&#13;
<h1>300px</h1>
<img id='theImage'/>
&#13;