使用jQuery自动裁剪图像空白区域

我有100,000张不受我控制的图像。 这些图像中的一些是优秀的,因为图像延伸到边界,而一些具有过量的白色空间。

当存在过多的空白区域时,它会使页面看起来很糟糕,并且意味着屏幕上的图像看起来都是不同的大小。

你可以在这看到我的意思:

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

我一直在寻找的是一种jQuery方法,用于裁剪图像并自动删除空白。

1)每个图像中的空白量是不同的2)图像的比例是不同的3)我想使用javascript而不是预处理图像。

希望你能帮忙!

编辑:这是一个示例图像 – http://sofzh.miximages.com/javascript/128554505.jpg 。 请注意,图像来自各个联盟网站,绝对来自不同的域。

要分析图像中的空白区域,我知道的唯一方法是将该图像加载到canvas

 var img = new Image(), $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. }; 

坦率地说,我无法测试此代码是有充分理由的:我遇到了臭名昭着的“ 无法从canvas中获取图像数据,因为canvas已被跨源数据污染。 ”安全例外。

这不是一个错误,它是一个预期的function。 从规格 :

toDataURL(),toDataURLHD(),toBlob(),getImageData()和getImageDataHD()方法检查标志并抛出SecurityErrorexception而不是泄漏跨源数据。

drawImage()从外部域加载文件时会发生这种情况,这会导致canvas的origin-clean标志设置为false,从而阻止进一步的数据操作。

我担心你会遇到同样的问题,但无论如何, 这是代码。

即使这在客户端起作用,我也可以想象在性能方面会有多么悲惨。 所以,正如Jan所说,如果您可以下载图像并在服务器端预处理它们,那就更好了。


编辑:我很想知道我的代码是否会真正裁剪图像,事实确实如此。 在此处输入图像描述

你可以在这里查看

它仅适用于您域中的图像,如前所述。 您可以选择自己的白色背景图像并更改最后一行:

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

显然,您需要从域中运行代码,而不是从jsFiddle运行代码。


编辑2:如果要裁剪和放大以保持相同的宽高比,请更改此值

 var $croppedCanvas = $("").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 = $("").attr({ width: imgWidth, height: imgHeight }); // finally crop the guy $croppedCanvas[0].getContext("2d").drawImage(canvas, cropLeft, cropTop, cropWidth, cropHeight, 0, 0, imgWidth, imgHeight); 

Edit3:在浏览器上裁剪图像的一种快速方法是通过使用Web Workers来并行化工作负载,正如这篇优秀文章所解释的那样。

根据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(); }