Javascript 使用jQuery自动裁剪图像空白
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/12175991/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Crop image white space automatically using jQuery
提问by Dave Hilditch
I have 100,000 images which are not under my control. Some of these images are excellent in that the image stretches to the boundaries whilst some have excessive amounts of white space.
我有 100,000 张图像不受我控制。其中一些图像非常出色,因为图像延伸到边界,而有些图像有过多的空白。
When there is excessive white space it makes the page look terrible and means images on the screen all look like they are different sizes.
当有过多的空白空间时,它会使页面看起来很糟糕,并且意味着屏幕上的图像看起来都像它们的大小不同。
You can see what I mean here:
你可以在这里看到我的意思:
http://www.fitness-saver.com/uk/shop/mountain-bikes/
http://www.fitness-saver.com/uk/shop/mountain-bikes/
What I have been hunting for is a jQuery method of cropping the images and removing the whitespace automatically.
我一直在寻找一种 jQuery 方法来裁剪图像并自动删除空白。
1) The amount of whitespace is different in every image 2) The ratios of the images are different 3) I want to use javascript rather than pre-processing the images.
1) 每张图像的空白量不同 2) 图像的比例不同 3) 我想使用 javascript 而不是预处理图像。
I hope you can help!
我希望你能帮忙!
Edit: Here's an example image - http://images.productserve.com/preview/3395/128554505.jpg. Note the images come from various affiliate sites and are definitely from a different domain.
编辑:这是一个示例图像 - http://images.productserve.com/preview/3395/128554505.jpg。请注意,这些图像来自各种附属网站,并且肯定来自不同的域。
回答by Jose Rui Santos
To analyse the blank spaces in an image, the only way I know is to load that image into a canvas
:
要分析图像中的空格,我知道的唯一方法是将该图像加载到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';
Next, use the getImageData()method. This method returns an ImageData object that you can use to inspect each pixel data (color).
接下来,使用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.
};
Frankly I was unable to test this code for a good reason: I came across the infamous "Unable to get image data from canvas because the canvas has been tainted by cross-origin data." security exception.
坦率地说,我无法测试此代码是有充分理由的:我遇到了臭名昭著的“无法从画布获取图像数据,因为画布已被跨域数据污染。”安全异常。
This is not a bug, it is an intended feature. From the specs:
这不是错误,而是预期的功能。从规格:
The toDataURL(), toDataURLHD(), toBlob(), getImageData(), and getImageDataHD() methods check the flag and will throw a SecurityError exception rather than leak cross-origin data.
toDataURL()、toDataURLHD()、toBlob()、getImageData() 和 getImageDataHD() 方法检查该标志并将抛出 SecurityError 异常而不是泄漏跨域数据。
This happens when drawImage()
loads files from external domains, which causes the canvas's origin-cleanflag to be set to false, preventing further data manipulations.
当drawImage()
从外部域加载文件时会发生这种情况,这会导致画布的origin-clean标志设置为 false,从而阻止进一步的数据操作。
I am afraid you will run into the same problem, but anyway, here is the code.
恐怕你会遇到同样的问题,但无论如何,这是代码。
Even if this works on client side, I can imagine how miserable will be performance-wise. So, as Jan said, if you can download the images and pre-process them on the server side, that would be better.
即使这在客户端有效,我可以想象在性能方面会有多悲惨。因此,正如 Jan 所说,如果您可以下载图像并在服务器端对其进行预处理,那就更好了。
Edit:I was curious to see if my code would really crop an image, and indeed it does.
编辑:我很好奇我的代码是否真的会裁剪图像,确实如此。
You can check it out here
你可以在这里查看
It only works for images from your domain, as stated before. You can choose your own image with white background and change the last line:
如前所述,它仅适用于您域中的图像。您可以选择自己的白色背景图像并更改最后一行:
// define here an image from your domain
img.src = 'http://localhost/strawberry2.jpg';
Obviously, you will need to run the code from your domain, not from jsFiddle.
显然,您需要从您的域中运行代码,而不是从 jsFiddle。
Edit2:If you want to crop and scale up to keep the same aspect ratio, then change this
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);
to
到
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:One fast way to crop images on the browser, is to parallelize the workload through the use of Web Workers, as this excellent articleexplains.
Edit3:在浏览器上裁剪图像的一种快速方法是通过使用Web Workers并行化工作负载,正如这篇出色的文章所解释的那样。
回答by Mohammad AlBanna
Based on the great answer that provided by Jose Rui Santos
, I've changed his code to work with just image
object without jQuery library to be loaded.
基于 提供的很好的答案Jose Rui Santos
,我已经改变了他的代码,只处理image
没有加载 jQuery 库的对象。
The return of this function is cropped image data URL to be used directly in image element.
此函数的返回是裁剪图像数据 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);
}
}
}
}
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);
}
}
}
}
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();
}