2017-08-29 108 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,這比它之前的任何像素都暗。所以我認爲該功能正常工作。試試看不同的圖像,在互聯網上找到一個小的光照圖像,如藍色的天空圖像,編輯它並添加一個黑暗的像素,看看這個算法是否找到它。 –