2017-08-29 85 views
2

我正在尝试使用Javascript来查找图像的最暗区域。 到目前为止,这是我所: https://jsfiddle.net/brampower/bv78rmz8/Javascript - 查找图像的最暗区域

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

可能重复[需要单击图像中最暗点](https://stackoverflow.com/questions/37739177/need-to- click-darkest-spot-in-image) –

+0

谢谢你,但是这个问题中的代码似乎没有帮助。我从头开始,因此提出了新的问题。我觉得我非常接近,但似乎无法弄清楚为什么返回给我的坐标不是我正在寻找的坐标。 – user1828398

回答

0

没有的jsfiddle工作,我最好的猜测是,在

var y = Math.floor(darkest_pixel_location/200); 
var x = darkest_pixel_location-(y*200); 

逻辑是不正确的原因有两个。

1)的图像是在宽度/高度为300像素,而不是200

2)图象 - 由x一阶和y第二

为了得到正确的x和y坐标,我认为下面的代码将工作:

var x = Math.floor(darkest_pixel_location/imageWidth); 
var y = darkest_pixel_location % imageWidth; 
+0

你是对的;将它们硬编码为200x200是我在对其他图像进行测试后留下的错误。在上面的Emestas的帮助下,我来到了这个更新的JSFiddle https://jsfiddle.net/brampower/tw08fdhf 不幸的是,这仍然不是我期待的结果。我试图获得图像较暗框的x和y坐标;我想到最黑暗的像素会在那个盒子里面,但它看起来并不那样。我可能需要考虑测试一个区域并平均该区域的像素亮度,虽然我承认没有经验 – user1828398

0

一个很好的时间来实现硬编码变量的讨厌的后果......

你正在创建的300×300和drawi画布与它相同的尺寸的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)。

因此,我希望你现在可以看到我的暗示图像中暗暗像素的目的。您可以一次将您试图调试的问题数量减少到一个。在这种情况下 - “我可以将索引转换回2d坐标吗?”。一旦完成,“我是否真的有一个黑点,我认为它是什么?“

在你的情况 - 在回答这两个问题是没有不完全是最有利的,从中开始调试的地方......


一些评论意见,并更好地理解!手头的问题得到了


好了,按在评论中讨论 - 的任务是找到一个黑暗区域的左上角的图像中 - 这样的搜索应该平均业绩较一(到目前为止)未知的地区,如禄最小值或最大值(暗或像素点)不会对确定的感兴趣区域产生不利影响。

理想情况下,可以迭代运行代码 - 尝试块大小为1,然后块大小为2等等,增加块大小,直到两次运行的结果相同或在有一定的限制。

也就是说,如果我搜索块大小为9并获取位置93,211,然后获得相同的块大小为10(而所有以前的块大小的值返回不同的结果),那么我可能会感觉相当有信心我正确地确定了感兴趣的领域。

下面是一些代码咀嚼。你会注意到我已经离开了你的功能并创建了另一个非常相似的功能。我希望你会觉得它合适。 :)

"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) 
 
{ 
 
\t var ajax = new XMLHttpRequest(); 
 
\t ajax.onload = function(){onLoad(this);} 
 
\t ajax.onerror = function(){console.log("ajax request failed to: "+url);onError(this);} 
 
\t ajax.open("GET",url,true); 
 
\t ajax.send(); 
 
} 
 

 
function ajaxPost(url, phpPostVarName, data, onSucess, onError) 
 
{ 
 
\t var ajax = new XMLHttpRequest(); 
 
\t ajax.onload = function(){ onSucess(this);} 
 
\t ajax.onerror = function() {console.log("ajax request failed to: "+url);onError(this);} 
 
\t ajax.open("POST", url, true); 
 
\t ajax.setRequestHeader("Content-type","application/x-www-form-urlencoded"); 
 
\t ajax.send(phpPostVarName+"=" + encodeURI(data)); 
 
} 
 

 
function ajaxPostForm(url, formElem, onSuccess, onError) 
 
{ 
 
\t var formData = new FormData(formElem); 
 
\t ajaxPostFormData(url, formData, onSuccess, onError) 
 
} 
 

 
function ajaxPostFormData(url, formData, onSuccess, onError) 
 
{ 
 
\t var ajax = new XMLHttpRequest(); 
 
\t ajax.onload = function(){onSuccess(this);} 
 
\t ajax.onerror = function(){onError(this);} 
 
\t ajax.open("POST",url,true); 
 
\t ajax.send(formData); 
 
} 
 

 
function getTheStyle(tgtElement) 
 
{ 
 
\t var result = {}, properties = window.getComputedStyle(tgtElement, null); 
 
\t forEach(properties, function(prop){result[prop] = properties.getPropertyValue(prop);}); 
 
\t return result; 
 
} 
 

 
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 
 
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 
 
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 
 
window.addEventListener('load', onDocLoaded, false); 
 

 
function onDocLoaded(evt) 
 
{ 
 
// \t var image_url = 'http://i.imgur.com/j6oJO8s.png'; 
 
// \t var image_url = 'onePixel.png'; 
 
\t var image_url = 'j6oJO8s.png'; 
 
\t 
 
\t byId('theImage').src = image_url; 
 
\t solve_darkest(image_url, function(x,y){alert('x: '+x+' y: '+y);}); 
 
\t 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) 
 
{ 
 
\t var image = new Image(); 
 
\t image.src = url; 
 

 
\t image.onload = function() 
 
\t { 
 
\t \t var canvas = document.createElement('canvas'); 
 
\t \t canvas.width = 300; 
 
\t \t canvas.height = 300; 
 

 
\t \t var context = canvas.getContext("2d"); 
 
\t \t context.drawImage(image, 0, 0); 
 

 
\t \t var imgData = context.getImageData(0, 0, 300, 300); 
 

 
\t \t var pixel = 0; 
 
\t \t var darkest_pixel_lightness = 100; 
 
\t \t var darkest_pixel_location = 0; 
 
\t \t for (var i = 0; i < imgData.data.length; i += 4) 
 
\t \t { 
 
\t \t \t var red = imgData.data[i + 0]; 
 
\t \t \t var green = imgData.data[i + 1]; 
 
\t \t \t var blue = imgData.data[i + 2]; 
 
\t \t \t var alpha = imgData.data[i + 3]; 
 

 
\t \t \t var hsl = rgbToHsl(red, green, blue); 
 
\t \t \t var lightness = hsl.l; 
 

 
\t \t \t if (lightness < darkest_pixel_lightness) 
 
\t \t \t { 
 
\t \t \t \t darkest_pixel_lightness = lightness; 
 
\t \t \t \t darkest_pixel_location = pixel; 
 
\t \t \t \t console.log("Darkest found at index: " + pixel); 
 
\t \t \t } 
 

 
\t \t \t pixel++; 
 
\t \t } 
 

 
\t // var y = Math.floor(darkest_pixel_location/200); 
 
\t // var x = darkest_pixel_location-(y*200); 
 
\t \t var y = Math.floor(darkest_pixel_location/this.width); 
 
\t \t var x = darkest_pixel_location-(y*this.width); 
 
\t \t callback(x,y); 
 
\t }; 
 
} 
 

 

 

 
function solve_darkest_2(url, callback) 
 
{ 
 
\t var image = new Image(); 
 
\t image.src = url; 
 

 
\t image.onload = function() 
 
\t { 
 
\t \t var canvas = document.createElement('canvas'); 
 
\t \t canvas.width = 300; 
 
\t \t canvas.height = 300; 
 
\t \t var context = canvas.getContext("2d"); 
 
\t \t context.drawImage(image, 0, 0); 
 
\t \t var imgData = context.getImageData(0,0, canvas.width, canvas.height); 
 

 
\t \t var darkest_pixel_luminance = 100; 
 
\t \t var darkest_pixel_xPos = 0; 
 
\t \t var darkest_pixel_yPos = 0; 
 
\t \t for (var y=0; y<canvas.height; y++) 
 
\t \t { 
 
\t \t \t for (var x=0; x<canvas.width; x++) 
 
\t \t \t { 
 
\t \t \t \t var luminance = averagePixels(imgData, x, y, 10); 
 
\t \t \t \t if (luminance < darkest_pixel_luminance) 
 
\t \t \t \t { 
 
\t \t \t \t \t darkest_pixel_luminance = luminance; 
 
\t \t \t \t \t darkest_pixel_xPos = x; 
 
\t \t \t \t \t darkest_pixel_yPos = y; 
 
\t \t \t \t } 
 
\t \t \t } 
 
\t \t } 
 
\t \t callback(darkest_pixel_xPos,darkest_pixel_yPos); 
 
\t }; 
 
} 
 

 
function averagePixels(imgData, xPos, yPos, averagingBlockSize) 
 
{ 
 
// \t var ctx = canvas.getContext("2d"); 
 
// \t var imgData = ctx.getImageData(0, 0, canvas.width, canvas.height); 
 
// \t imgData 
 
\t 
 
\t // we average pixels found in a square region, we need to know how many pixels 
 
\t // are in the region to divide the accumalated totals by the number of samples (pixels) in the 
 
\t // averaging square 
 
\t var numPixelsMax = averagingBlockSize * averagingBlockSize; 
 
\t var numPixelsActual = 0; 
 
\t 
 
\t var red, green, blue; 
 
\t red = green = blue = 0; 
 
\t 
 
\t var rowStride = imgData.width * 4; \t \t // add this to an index into the canvas's data to get the pixel 
 
\t \t \t \t \t \t \t \t \t \t // immediatelly below it. 
 
\t var x, y; 
 
\t var initialIndex = ((yPos * imgData.width) + xPos) * 4; 
 
\t var index = initialIndex; 
 
\t 
 
\t var pixel = 0; 
 
\t var darkest_pixel_lightness = 100; 
 
\t var darkest_pixel_location = 0; 
 
\t 
 
\t for (y=0; y<averagingBlockSize; y++) 
 
\t { 
 
\t \t index = initialIndex + y * rowStride; 
 
\t \t 
 
\t \t for (x=0; x<averagingBlockSize; x++) 
 
\t \t { 
 
\t \t \t if ((x+xPos < imgData.width) && (y+yPos < imgData.height)) 
 
\t \t \t { 
 
\t \t \t \t red += imgData.data[index+0]; 
 
\t \t \t \t green += imgData.data[index+1]; 
 
\t \t \t \t blue += imgData.data[index+2]; 
 
\t \t \t \t numPixelsActual++; 
 
\t \t \t } 
 
\t \t \t index += 4; 
 
\t \t } 
 
\t } 
 
\t red /= numPixelsActual; 
 
\t green /= numPixelsActual; 
 
\t blue /= numPixelsActual; 
 
\t 
 
\t var hsl = rgbToHsl(red, green, blue); 
 
\t var luminance = hsl.l; 
 
\t return luminance; 
 
}
img 
 
{ 
 
\t border: solid 1px red; 
 
}
<h1>300px</h1> 
 
<img id='theImage'/>

+0

你是对的;将它们硬编码为200x200是我在对其他图像进行测试后留下的错误。在上面的Emestas的帮助下,我来到了这个更新的JSFiddle jsfiddle.net/brampower/tw08fdhf。不幸的是,这仍然不是我期待的结果。我试图获得图像较暗框的x和y坐标;我想到最黑暗的像素会在那个盒子里面,但它看起来并不那样。我可能需要考虑测试区域并平均区域中像素的亮度。你能引导我走向正确的方向吗? – user1828398

+0

当然。那么,你可以取(例如)3x3像素的平均值。我会用x,x + 1,x + 2和y,y + 1,y + 2。我会添加所有的r值,所有的g值,然后是所有的b值。然后,我将这个累积值除以该区域的像素数(3x3 = 9),然后将这个单一的rgb三元组转换为hsl,并检查最黑暗的样本,就像你现在一样。您必须或者(0)处理有少于9个有效像素的样本进行采样,或者(1)通过循环而不是img_side_len迭代执行img_side_len - region_side_len迭代。 :) – enhzflep

+0

哇,这个在我头上的方式。看起来,当谈到Javascript时,我有很多摆弄的事情。你能写出一个JSFiddle吗?我意识到我问了很多,所以如果你没有时间这样做,请不要感到压力。我完全理解:) – user1828398

1

好吧,我只是测试你的jsfiddle。

对于被污染的帆布​​只是改变crossOrigin属性:

var image = new Image(); 
image.crossOrigin = "Anonymous"; 

对于不正确的像素,有几个问题。

  1. 不正确的画布大小。如果图像小于画布大小,算法会测试不在图像中但在画布中的像素。由于您不会丢弃透明的像素,因此您还要测试应该是黑色的0,0(RGB)像素#000000。

  2. 不正确的1维数组到2维变换。您使用的公式不正确,因为您将宽度和高度设置为300,但在公式中使用200。我建议创建一个变量并将其用作参考。

如果您怀疑像素正好在那里,请创建一个小图片,例如5x5像素大小,并检查算法是否会返回您所期望的。

我更新了jsfiddle,我认为这是正确的。此外,删除HTML中的img元素,并将画布附加到主体:https://jsfiddle.net/Draznel/597u5h0c/1/

+0

这太好了。我有一个更新的JSF,在那里你可以看到我还没有得到我正在寻找的结果;最终目标是找到图像中暗框的坐标或最暗区域(如果您愿意):https://jsfiddle.net/brampower/tw08fdhf/ 当前结果为x = 290,y = 138。该特定像素不落在黑暗的正方形区域。也许用getImageData()对一个区域进行采样并且平均该区域像素的亮度会更容易一些,尽管我承认我没有这方面的经验。你认为这会是一个更好的解决方案吗? – user1828398

+0

我认为问题可能在于rgbToHsl函数。试用不同的功能。我认为,问题是,最黑暗的像素是在290:138。即使您在较暗的矩形上绘制,如果图像中任何位置的单个像素的亮度较低,它都会变暗。例如,你有一个矩形,其亮度为0.1左右,但如果图像中的某个地方存在一个完整的黑色像素(#000000),它将成为图像中最暗的像素。 –

+0

我认为情况就是这样。我添加了这行'console.log(红色,绿色,蓝色);'当找到一个新的较暗的像素时。找到的最暗的像素是#060206,这比它之前的任何像素都暗。所以我认为该功能正常工作。试试看不同的图像,在互联网上找到一个小的光照图像,如蓝色的天空图像,编辑它并添加一个黑暗的像素,看看这个算法是否找到它。 –