2012-08-29 65 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