2012-08-29 104 views
20

我有100,000個圖像不在我的控制之下。其中一些圖像非常出色,圖像伸展到邊界,而有些圖像具有過多的空白區域。使用jQuery自動裁剪圖像空白空間

當存在過多的空白時,它會使頁面看起來很糟糕,並且意味着屏幕上的圖像看起來都是不同的大小。

你可以明白我的意思是:

http://www.fitness-saver.com/uk/shop/mountain-bikes/

我一直在追捕的裁剪圖像,並自動刪除空格的jQuery的方法。

1)每個圖像中的空白量不同 2)圖像比例不同 3)我想使用javascript而不是預處理圖像。

我希望你能幫忙!

編輯:這是一個示例圖像 - http://images.productserve.com/preview/3395/128554505.jpg。請注意,圖片來自不同的聯屬網站,絕對來自不同的域名。

+1

我可以建議您實際添加問題發生在您的問題的圖像之一。它會阻止投票結束,因爲您有效地宣傳您的網站。 –

+1

您可以在畫布上繪製圖像並移除整個空白列和行,並將圖像重新縮放至「相同」尺寸(保持寬高比)。 – Prusse

+2

「我想使用JavaScript而不是預處理圖像」 - 爲什麼?爲什麼要讓用戶瀏覽器在每次加載圖片時都必須刪除空白區域,而不是僅僅在服務器端執行一次,然後將圖片保留爲未使用白色間距以供將來使用? – h2ooooooo

回答

41

分析圖像中的空白,我知道唯一的辦法就是到圖像加載到canvas

var img = new Image(), 
    $canvas = $("<canvas>"), // create an offscreen canvas 
    canvas = $canvas[0], 
    context = canvas.getContext("2d"); 

img.onload = function() { 
    context.drawImage(this, 0, 0); // put the image in the canvas 
    $("body").append($canvas); 
    removeBlanks(this.width, this.height); 
}; 

// test image 
img.src = 'http://images.productserve.com/preview/1302/218680281.jpg'; 

接下來,使用getImageData()方法。此方法返回可用於檢查每個像素數據(顏色)的ImageData對象。

var removeBlanks = function (imgWidth, imgHeight) { 
    var imageData = context.getImageData(0, 0, canvas.width, canvas.height), 
      data = imageData.data, 
      getRBG = function(x, y) { 
         return { 
         red: data[(imgWidth*y + x) * 4], 
         green: data[(imgWidth*y + x) * 4 + 1], 
         blue: data[(imgWidth*y + x) * 4 + 2] 
         }; 
        }, 
      isWhite = function (rgb) { 
         return rgb.red == 255 && rgb.green == 255 && rgb.blue == 255; 
        }, 
      scanY = function (fromTop) { 
         var offset = fromTop ? 1 : -1; 

         // loop through each row 
         for(var y = fromTop ? 0 : imgHeight - 1; fromTop ? (y < imgHeight) : (y > -1); y += offset) { 

         // loop through each column 
         for(var x = 0; x < imgWidth; x++) { 
          if (!isWhite(getRBG(x, y))) { 
           return y;       
          }  
         } 
        } 
        return null; // all image is white 
       }, 
      scanX = function (fromLeft) { 
         var offset = fromLeft? 1 : -1; 

         // loop through each column 
         for(var x = fromLeft ? 0 : imgWidth - 1; fromLeft ? (x < imgWidth) : (x > -1); x += offset) { 

         // loop through each row 
         for(var y = 0; y < imgHeight; y++) { 
          if (!isWhite(getRBG(x, y))) { 
           return x;       
          }  
         } 
        } 
        return null; // all image is white 
       }; 


     var cropTop = scanY(true), 
      cropBottom = scanY(false), 
      cropLeft = scanX(true), 
      cropRight = scanX(false); 
    // cropTop is the last topmost white row. Above this row all is white 
    // cropBottom is the last bottommost white row. Below this row all is white 
    // cropLeft is the last leftmost white column. 
    // cropRight is the last rightmost white column. 
}; 

坦白說,我無法測試此代碼一個很好的理由:我穿過了臭名昭著的「 無法因爲帆布已經被跨域數據污點從畫布上獲得的圖像數據」安全例外來。

這不是一個錯誤,它是一個預期的功能。從specs

的toDataURL(),toDataURLHD(),toBlob(),getImageData(),和 getImageDataHD()方法檢查標誌,並會拋出一個SecurityError 例外,而不是泄漏跨域數據。

這種情況發生時從外部域,這會導致畫布起源清潔標誌drawImage()將文件加載到被設置爲false,防止進一步的數據操作。

我怕你會遇到同樣的問題,但無論如何,here is the code.

即使這個工作在客戶端,我可以想像是多麼痛苦將是性能代價。所以,正如Jan所說,如果你可以下載圖像並在服務器端對它們進行預處理,那會更好。


編輯:我很好奇,看看我的代碼真的裁剪圖像,而事實上它。 enter image description here

你可以檢查出來here

它僅適用於從您的域的圖像,如前所述。您可以選擇使用白色背景自己的形象,改變最後一行:

// define here an image from your domain 
img.src = 'http://localhost/strawberry2.jpg'; 

顯然,你需要從你的域中運行的代碼,而不是從的jsfiddle。


EDIT2:如果您想裁剪和擴大保持相同的縱橫比,然後改變這種

var $croppedCanvas = $("<canvas>").attr({ width: cropWidth, height: cropHeight }); 

// finally crop the guy 
$croppedCanvas[0].getContext("2d").drawImage(canvas, 
    cropLeft, cropTop, cropWidth, cropHeight, 
    0, 0, cropWidth, cropHeight); 

var $croppedCanvas = $("<canvas>").attr({ width: imgWidth, height: imgHeight }); 

// finally crop the guy 
$croppedCanvas[0].getContext("2d").drawImage(canvas, 
    cropLeft, cropTop, cropWidth, cropHeight, 
    0, 0, imgWidth, imgHeight); 

EDIT3:在瀏覽器上裁剪圖像的一種快速方法r是通過使用Web Workers來並行化工作負載,如excellent article所解釋的那樣。

+0

感謝您的詳細回覆。是否沒有像Javascript和JSONP一樣的安全解決方案? –

+0

如果會員網站將圖像URL作爲JSON返回,您可以嘗試將JSON響應封裝到javascript函數中,請參閱http://en.wikipedia.org/wiki/JSONP。另外,我不是新的HTML5'crossorigin'屬性,它允許從外部域讀取圖像,但服務器(您的分支機構)必須通過具有「Access-Control-Allow-Origin」標頭來允許,請參閱https://developer.mozilla.org/en-US/docs/CORS_Enabled_Image我介紹了jsFiddle代碼中的新屬性 –

+0

@DavidHilditch查看我的更新回答 –

10

基於Jose Rui Santos提供的很好的答案,我已經改變了他的代碼,只用image沒有jQuery庫的對象加載。

該函數的返回值是在圖像元素中直接使用的裁剪圖像數據URL。

/* 
    Source: http://jsfiddle.net/ruisoftware/ddZfV/7/ 
    Updated by: Mohammad M. AlBanna 
    Website: MBanna.info 
    Facebook: FB.com/MBanna.info 
*/ 

var myImage = new Image(); 
myImage.crossOrigin = "Anonymous"; 
myImage.onload = function(){ 
    var imageData = removeImageBlanks(myImage); //Will return cropped image data 
} 
myImage.src = "IMAGE SOURCE"; 



//-----------------------------------------// 
function removeImageBlanks(imageObject) { 
    imgWidth = imageObject.width; 
    imgHeight = imageObject.height; 
    var canvas = document.createElement('canvas'); 
    canvas.setAttribute("width", imgWidth); 
    canvas.setAttribute("height", imgHeight); 
    var context = canvas.getContext('2d'); 
    context.drawImage(imageObject, 0, 0); 

    var imageData = context.getImageData(0, 0, imgWidth, imgHeight), 
     data = imageData.data, 
     getRBG = function(x, y) { 
      var offset = imgWidth * y + x; 
      return { 
       red:  data[offset * 4], 
       green: data[offset * 4 + 1], 
       blue: data[offset * 4 + 2], 
       opacity: data[offset * 4 + 3] 
      }; 
     }, 
     isWhite = function (rgb) { 
      // many images contain noise, as the white is not a pure #fff white 
      return rgb.red > 200 && rgb.green > 200 && rgb.blue > 200; 
     }, 
       scanY = function (fromTop) { 
     var offset = fromTop ? 1 : -1; 

     // loop through each row 
     for(var y = fromTop ? 0 : imgHeight - 1; fromTop ? (y < imgHeight) : (y > -1); y += offset) { 

      // loop through each column 
      for(var x = 0; x < imgWidth; x++) { 
       var rgb = getRBG(x, y); 
       if (!isWhite(rgb)) { 
        if (fromTop) { 
         return y; 
        } else { 
         return Math.min(y + 1, imgHeight - 1); 
        } 
       } 
      } 
     } 
     return null; // all image is white 
    }, 
    scanX = function (fromLeft) { 
     var offset = fromLeft? 1 : -1; 

     // loop through each column 
     for(var x = fromLeft ? 0 : imgWidth - 1; fromLeft ? (x < imgWidth) : (x > -1); x += offset) { 

      // loop through each row 
      for(var y = 0; y < imgHeight; y++) { 
       var rgb = getRBG(x, y); 
       if (!isWhite(rgb)) { 
        if (fromLeft) { 
         return x; 
        } else { 
         return Math.min(x + 1, imgWidth - 1); 
        } 
       }  
      } 
     } 
     return null; // all image is white 
    }; 

    var cropTop = scanY(true), 
     cropBottom = scanY(false), 
     cropLeft = scanX(true), 
     cropRight = scanX(false), 
     cropWidth = cropRight - cropLeft, 
     cropHeight = cropBottom - cropTop; 

    canvas.setAttribute("width", cropWidth); 
    canvas.setAttribute("height", cropHeight); 
    // finally crop the guy 
    canvas.getContext("2d").drawImage(imageObject, 
     cropLeft, cropTop, cropWidth, cropHeight, 
     0, 0, cropWidth, cropHeight); 

    return canvas.toDataURL(); 
} 
+1

你爲什麼加上+10;沒有意義,你讓我失去了很多時間試圖找到bug –

+0

@IvanCastellanos感謝您的編輯!我在代碼中添加了它,因爲裁剪後的圖像有文字,我需要在底部和右側留出一點空間。對不起。 – Mohammad