Search code examples
javaimageprocessingpngtransparency

Finding transparent pixels on an image and making same pixels transparent on another image


I have two images. The second one is the result of applying some sort of mask to the first one. What I need is to get that mask and be able to apply it to other images. Here are the two images: normal, tattered.

As you can see, the second one has tattered edges, they are transparent, not white. (There's also some kind of blur going on, if there's a way I can find out what blur it is exactly, then great, but it's not really necessary here)

What I need is to be able to create the second image from the first one.

Theoretically, I should create a mask - an image of the same size with any color and each pixel having transparency of either 0 or 255, depending on the transparency value of the same pixel in the second image from above. Then I can just set alpha of pixels of any input image to alpha values from this mask.

However, I have no idea how to do it practically. I tried it in java using BufferedImage, however, it does not work. When I try to getAlpha from the color of the selected pixel, it is always 255, even for pixels which are supposed to be transparent. I did manage to get alpha values in Processing (they are actually not just 0 or 255, lots of values inbetween), however, when I try to apply this value to a new image and save it, it saves as fully opaque image, when I load it, alpha values are all 255.

  PImage mask = loadImage("some/path/1.png");
  PImage img = loadImage("some/path/2.png");

  img.loadPixels();
  for (int x = 0; x < img.width; x++) {
    for (int y = 0; y < img.height; y++) {
      color maskColor = mask.get(x, y);
      if (alpha(maskColor) < 255) {
        color imgColor = img.get(x, y);
        img.pixels[y*img.width + x] = color(red(imgColor), green(imgColor), blue(imgColor), alpha(maskColor));
      }
    }
  }
  img.updatePixels();
  img.save("some/path/3.png"); 

Solution

  • You can use the tattered image as mask for other images as well. You just need the alpha information from the mask.
    Implementation of creating tattered borders using BufferedImage:

    public class Test {
    
        public static void main(String[] args) throws IOException {
            
             BufferedImage mask = loadImage("e:\\mask.png");
             BufferedImage img = loadImage("e:\\1.png");
    
             int width = mask.getWidth();
             int height = mask.getHeight();
             
             BufferedImage processed = new BufferedImage(width, height, BufferedImage.TYPE_4BYTE_ABGR);
    
              for (int x = 0; x < width; x++) {
                for (int y = 0; y < height; y++) {
                  int rgb = mask.getRGB(x,y);
                  int maskAlpha = alpha(rgb);
                  int imgColor = img.getRGB(x, y);
                  if (maskAlpha < 255) {
                    processed.setRGB(x,y,maskAlpha(imgColor, maskAlpha));
                  } else {
                      processed.setRGB(x,y,imgColor);
                  }
                }
              }
             
              writeImage(processed, "e:\\2.png");
        }
    
        static BufferedImage loadImage(String imagePath) {
            File file = new File(imagePath);
            BufferedImage image = null;
            try {
                image = ImageIO.read(file);
            } catch (IOException e) {
                e.printStackTrace();
            }
            return image;
        }
        
        static void writeImage(BufferedImage img,String filePath){
            String format = filePath.substring(filePath.indexOf('.')+1);
            //Get Picture Format
            System.out.println(format);
            try {
                ImageIO.write(img,format,new File(filePath));
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        
        static int maskAlpha(int rgb, int alpha) {
            //strip alpha from original color
            int color = (0x00ffffff&rgb);
            return color + ((alpha)<<24);
        }
        
        static int alpha(int rgb) {
            return (0xff&(rgb>>24));
        }
        
        static int red(int rgb) {
            return (0xff&rgb);
        }
        static int green(int rgb) {
            return (0xff&(rgb>>8));
        }
        static int blue(int rgb) {
            return (0xff&(rgb>>16));
        }
    }
    

    Here BufferedImage.TYPE_4BYTE_ABGR means

    Represents an image with 8-bit RGBA color components with the colors Blue, Green, and Red stored in 3 bytes and 1 byte of alpha. The image has a ComponentColorModel with alpha. The color data in this image is considered not to be premultiplied with alpha. The byte data is interleaved in a single byte array in the order A, B, G, R from lower to higher byte addresses within each pixel.

    That means color integer is 32 bits, which is stored in java in the order of abgr, i.e., alpha is the first 8 bits, and r is the last 8 bits, so you can get the argb value as follows:

    int r = (0xff&rgb);
    int g = (0xff&(rgb>>8));
    int b = (0xff&(rgb>>16));
    int a = (0xff&(rgb>>24));