简体   繁体   English

使用jQuery自动裁剪图像白色空间

[英]Crop image white space automatically using jQuery

I have 100,000 images which are not under my control. 我有100,000张不受我控制的图像。 Some of these images are excellent in that the image stretches to the boundaries whilst some have excessive amounts of white space. 这些图像中的一些是优秀的,因为图像延伸到边界,而一些图像具有过量的白色空间。

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 . 编辑:这是一个示例图像 - http://images.productserve.com/preview/3395/128554505.jpg Note the images come from various affiliate sites and are definitely from a different domain. 请注意,图片来自各个联盟网站,绝对来自不同的域名。

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. 接下来,使用getImageData()方法。 This method returns an ImageData object that you can use to inspect each pixel data (color). 此方法返回一个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-clean flag to be set to false, preventing further data manipulations. drawImage()从外部域加载文件时会发生这种情况,这会导致canvas的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 编辑2:如果要裁剪和放大以保持相同的宽高比,请更改此值

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

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM