Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
458 views
in Technique[技术] by (71.8m points)

javascript - Crop image white space automatically using jQuery

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.

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/

What I have been hunting for is a jQuery method of cropping the images and removing the whitespace automatically.

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.

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.

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

To analyse the blank spaces in an image, the only way I know is to load that image into a 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).

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.

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.

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.


Edit: I was curious to see if my code would really crop an image, and indeed it does. enter image description here

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.


Edit2: If you want to crop and scale up to keep the same aspect ratio, then change this

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.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...