1

In Java, I'm trying to replace one BufferedImage inside another BufferedImage.

For example, enter image description here could be replaced with enter image description here inside enter image description here, so that enter image description here is produced as a result.

Would it be possible to write a function that would replace one BufferedImage inside another BufferedImage, and return the resulting BufferedImage?

public static BufferedImage replaceInsideBufferedImage(BufferedImage containingImage, BufferedImage toBeReplaced, BufferedImage replaceWithThis){
//In containingImage, replace all occurrences of toBeReplaced with replaceWithThis    
}
Anderson Green
  • 30,230
  • 67
  • 195
  • 328
  • Just search for the pattern, and when you've found it, replace it. What's the part you can't implement? – thejh Jun 10 '13 at 07:28
  • @thejh In order to search for the pattern, I might need to somehow convert each BufferedImage into a 2d integer array, and then convert the resulting array back into a BufferedImage after replacing one of the integer arrays inside the other integer array. Converting each BufferedImage to an integer array (and vice-versa) would be cumbersome: is there any simpler approach to this problem? – Anderson Green Jun 10 '13 at 07:34
  • I just found a closely related question that asks how to find an image inside another image: http://stackoverflow.com/questions/454498/find-an-image-within-an-image – Anderson Green Jul 10 '13 at 04:54

2 Answers2

2

The method below does the trick. Pseudo-code:

  • (1) For every pixel of containingImage source:
    • Begin matching the toBeReplaced toBeReplaced (pixel by pixel)
      • If it finds it (all pixels matched), it goes and replaces all of them with replaceWithThis replaceWithThis
      • If not, goes back to (1)

As all patterns will be found, finally, it will return returnImage final.

replaceInsideBufferedImage() code:

public static BufferedImage replaceInsideBufferedImage(BufferedImage containingImage, BufferedImage toBeReplaced, BufferedImage replaceWithThis) {
    BufferedImage returnImage = deepCopyImage(containingImage);
    for (int x = 0; x+toBeReplaced.getWidth() < containingImage.getWidth(); x++) {
        for (int y = 0; y+toBeReplaced.getHeight() < containingImage.getHeight(); y++) {
            BufferedImage subImg = containingImage.getSubimage(x, y, toBeReplaced.getWidth(), toBeReplaced.getHeight());
            if (imageEquals(subImg,toBeReplaced)) {
                for (int sx = 0; sx < replaceWithThis.getWidth(); sx++) {
                    for (int sy = 0; sy < replaceWithThis.getHeight(); sy++) {
                        returnImage.setRGB(x+sx, y+sy, replaceWithThis.getRGB(sx, sy));
                    }
                }
            }
        }
    }
    return returnImage;
}

Full working code:

import java.awt.image.BufferedImage;
import java.awt.image.ColorModel;
import java.awt.image.WritableRaster;
import java.io.File;

import javax.imageio.ImageIO;

public class ReplacePattern {

    public static void main(String[] args) throws Exception {
        BufferedImage containingImage = ImageIO.read(new File("fourWhites.png"));
        BufferedImage toBeReplaced = ImageIO.read(new File("oneWhite.png"));
        BufferedImage replaceWithThis = ImageIO.read(new File("oneRed.png"));
        BufferedImage replaced = replaceInsideBufferedImage(containingImage, toBeReplaced, replaceWithThis);
        ImageIO.write(replaced, "png", new File("fourReds.png"));
    }

    public static BufferedImage replaceInsideBufferedImage(BufferedImage containingImage, BufferedImage toBeReplaced, BufferedImage replaceWithThis) {
        BufferedImage returnImage = deepCopyImage(containingImage);
        for (int x = 0; x+toBeReplaced.getWidth() < containingImage.getWidth(); x++) {
            for (int y = 0; y+toBeReplaced.getHeight() < containingImage.getHeight(); y++) {
                BufferedImage subImg = containingImage.getSubimage(x, y, toBeReplaced.getWidth(), toBeReplaced.getHeight());
                if (imageEquals(subImg,toBeReplaced)) {
                    for (int sx = 0; sx < replaceWithThis.getWidth(); sx++) {
                        for (int sy = 0; sy < replaceWithThis.getHeight(); sy++) {
                            returnImage.setRGB(x+sx, y+sy, replaceWithThis.getRGB(sx, sy));
                        }
                    }
                }
            }
        }
        return returnImage;
    }

    // http://stackoverflow.com/a/3514297/1850609
    public static BufferedImage deepCopyImage(BufferedImage bi) {
        ColorModel cm = bi.getColorModel();
        boolean isAlphaPremultiplied = cm.isAlphaPremultiplied();
        WritableRaster raster = bi.copyData(null);
        return new BufferedImage(cm, raster, isAlphaPremultiplied, null);
    }

    // http://stackoverflow.com/a/11006474/1850609
    private static boolean imageEquals(BufferedImage image1, BufferedImage image2) {
        int width;
        int height;
        boolean imagesEqual = true;
        if( image1.getWidth()  == ( width  = image2.getWidth() ) && 
            image1.getHeight() == ( height = image2.getHeight() ) ){
            for(int x = 0;imagesEqual == true && x < width; x++){
                for(int y = 0;imagesEqual == true && y < height; y++){
                    if( image1.getRGB(x, y) != image2.getRGB(x, y) ){
                        imagesEqual = false;
                    }
                }
            }
        }else{
            imagesEqual = false;
        }
        return imagesEqual;
    }
}
acdcjunior
  • 132,397
  • 37
  • 331
  • 304
0

It would be possible, however I would not suggest doing that.

Detecting whether an image is present within another image will be very slow.

Moreover, due to possible encoding artifacts, it is possible that an image will never be detected in another image. In such case you would have to implement a more flexible detection function, which will take even longer and may lead to false positives.

Most likely, you have the data to rebuild the image from scratch. Instead of operating on images simply get the data used to generate the initial image and create a new one based on it.

But if you really need to do it, you will have to loop over both images and compare pixels. Use getRGB() function and compare the images pixel by pixel.

Dariusz
  • 21,561
  • 9
  • 74
  • 114
  • I'm trying to replace all matches of one image with another image in another specific image. Which of these three images is the "initial image" that you're referrring to? – Anderson Green Jun 10 '13 at 07:19
  • As long as I'm working with a lossless format (such as .bmp or .png), there won't be any encoding artifacts. – Anderson Green Jun 10 '13 at 07:21
  • @AndersonGreen initial image is the image to look in for the other images; as for artifacts: that's why I wrote *possible*. – Dariusz Jun 10 '13 at 07:22
  • Also, I'm not sure how it would be possible to "rebuild the image from scratch" if all 3 of the images that I'm working with are generated from external files. – Anderson Green Jun 10 '13 at 07:22
  • 1
    @AndersonGreen how much simpler answering would be if you put all that information in the question in the first place. – Dariusz Jun 10 '13 at 07:24