How to remove part of a PNG image in Python to make it Opaque

986 Views Asked by At

I have a script that is creating occlusions on PNG images. The data augmentation occlusion technique from https://arxiv.org/pdf/2001.04086.pdf.

The script runs works well for creating occlusions but they are drawn over the PNG as black rectangles.

What I would like them to do is cut these rectangles out of the PNG and make them part of the alpha layer of the PNG so that when pasted on top of a background, the background shows through the rectangles. Essentially make the black rectangles transparent.

Current implemenation output looks like this:

enter image description here

The complete script below, but area that needs work here:

    # Draw each box onto the image
    for index, row in boxes.iterrows():
        shape = [(row['topleftcornerxpx'], row['topleftcornerypx']), (row['topleftcornerxpx'] + row['sizepx'], row['topleftcornerypx'] + row['sizepx'])]
        img1 = ImageDraw.Draw(img)
        img1.rectangle(shape, fill = "black")

Have tried adding an alpha mask, but this removes the alpha channel from the background and keeps the rectangles black.

    # Draw each box onto the image
    for index, row in boxes.iterrows():
        shape = [(row['topleftcornerxpx'], row['topleftcornerypx']), (row['topleftcornerxpx'] + row['sizepx'], row['topleftcornerypx'] + row['sizepx'])]
        mask = Image.new('L', img.size, color = 255)
        draw=ImageDraw.Draw(mask)
        img1 = ImageDraw.Draw(img)
        img1.rectangle(shape, fill=0)
        img.putalpha(mask)

Complete script (someone might like it):

import os
from pandas import DataFrame
from PIL import Image, ImageDraw
import numpy as np

#set directories
directory = str("C:/GIT/Temp/Test/test/")
target_directory = str("C:/GIT/Temp/Test/test/occluded/")
occlusion_scales = [.20, .125, .08] #Percent of image the occlusions will cover. Add or remove as many as required.  
image_padding = int(5)

existing_files = os.listdir(target_directory)
#print(existing_files)

#Get files
for filename in os.listdir(directory):
  if filename.endswith('.png'):
      # Process for each occlusion scale in the list. 
      for scales in occlusion_scales:
        img = Image.open(directory + filename)
        # Get image dimensions
        imgwidth, imgheight = img.size
        # Get smallest value out of x & y to scale box size by. 
        box1sizepx = round(min(imgwidth,imgheight) * scales)
        print(filename)
        
        #Dont process files already processed, can comment out for replace. 
        if (filename.replace('.png','') + '_occluded_' + str(scales)+'.png') not in existing_files: 
            #print(filename + ' not in list')
            
            # Calculate number of boxes accross and down required
            boxesaccross = round((imgwidth/2) / box1sizepx)
            boxesdown = round((imgheight/2) / box1sizepx)

            # Create dataframe for boxes 
            boxes = DataFrame(columns=['sizepx','topleftcornerxpx','topleftcornerypx'])
            # Set row counter for loop.
            boxrow = 0

            #Draw a box for each row and within that each column
            while boxesdown >= 1:
                boxesdown = boxesdown -1
                boxcolumn = 0

                while boxesaccross >= 1: 
                    boxesaccross = boxesaccross -1
                    new_box = {'sizepx':box1sizepx,'topleftcornerxpx':round(box1sizepx*.8) + (box1sizepx * boxcolumn),'topleftcornerypx':round(box1sizepx*.8) + (box1sizepx * boxrow)}
                    boxes = boxes.append(new_box, ignore_index=True)
                    boxcolumn = boxcolumn + 2

                boxrow = boxrow + 2
                boxesaccross = round((imgwidth/2) / box1sizepx)

            # Draw each box onto the image
            for index, row in boxes.iterrows():
                shape = [(row['topleftcornerxpx'], row['topleftcornerypx']), (row['topleftcornerxpx'] + row['sizepx'], row['topleftcornerypx'] + row['sizepx'])]
                img1 = ImageDraw.Draw(img)
                img1.rectangle(shape, fill = "black")

            #Save the image 
            print(target_directory + filename.replace('.png','') + '_occluded_' + str(scales)+'.png')

            #Crop the image with some padding
            cropped_object = img.crop(((0 - image_padding), (0 - image_padding), (imgwidth + image_padding), (imgheight + image_padding)))
            cropped_object.save(target_directory + filename.replace('.png','') + '_occluded_' + str(scales)+'.png')
1

There are 1 best solutions below

0
On BEST ANSWER

Solved thanks @furas, set fill to 0 not "black". Updated script if anyone wants it.

import os
from pandas import DataFrame
from PIL import Image, ImageDraw
import numpy as np

#set directories
directory = str("C:/GIT/Temp/Test/test/")
target_directory = str("C:/GIT/Temp/Test/test/occluded/")
occlusion_scales = [.20, .125, .08] #Percent of image the occlusions will cover. Add or remove as many as required.  
image_padding = int(5)

existing_files = os.listdir(target_directory)
#print(existing_files)

#Get files
for filename in os.listdir(directory):
  if filename.endswith('.png'):
      # Process for each occlusion scale in the list. 
      #pixdata = filename.load()
      #print(pixdata)
      for scales in occlusion_scales:
        img = Image.open(directory + filename)
        # Get image dimensions
        imgwidth, imgheight = img.size
        # Get smallest value out of x & y to scale box size by. 
        box1sizepx = round(min(imgwidth,imgheight) * scales)
        print(filename)
        
        #Dont process files already processed, can comment out for replace. 
        if (filename.replace('.png','') + '_occluded_' + str(scales)+'.png') not in existing_files: 
            #print(filename + ' not in list')
            
            # Calculate number of boxes accross and down required
            boxesaccross = round((imgwidth/2) / box1sizepx)
            boxesdown = round((imgheight/2) / box1sizepx)

            # Create dataframe for boxes 
            boxes = DataFrame(columns=['sizepx','topleftcornerxpx','topleftcornerypx'])
            # Set row counter for loop.
            boxrow = 0

            #Draw a box for each row and within that each column
            while boxesdown >= 1:
                boxesdown = boxesdown -1
                boxcolumn = 0

                while boxesaccross >= 1: 
                    boxesaccross = boxesaccross -1
                    new_box = {'sizepx':box1sizepx,'topleftcornerxpx':round(box1sizepx*.8) + (box1sizepx * boxcolumn),'topleftcornerypx':round(box1sizepx*.8) + (box1sizepx * boxrow)}
                    boxes = boxes.append(new_box, ignore_index=True)
                    boxcolumn = boxcolumn + 2

                boxrow = boxrow + 2
                boxesaccross = round((imgwidth/2) / box1sizepx)

            # Draw each box onto the image
            for index, row in boxes.iterrows():
                shape = [(row['topleftcornerxpx'], row['topleftcornerypx']), (row['topleftcornerxpx'] + row['sizepx'], row['topleftcornerypx'] + row['sizepx'])]
                img1 = ImageDraw.Draw(img)
                img1.rectangle(shape, fill = 0)

            #Save the image 
            print(target_directory + filename.replace('.png','') + '_occluded_' + str(scales)+'.png')

            #Crop the image with some padding
            cropped_object = img.crop(((0 - image_padding), (0 - image_padding), (imgwidth + image_padding), (imgheight + image_padding)))
            cropped_object.save(target_directory + filename.replace('.png','') + '_occluded_' + str(scales)+'.png')