So I've been trying to remove the green screen, and crop, and I've had success, however, it is pretty slow, especially when I'm trying to use it on hundreds of pictures. I am not very familiar with image processing or PIL libraries, so I would love advice on how to make my code faster.
How it works: Basically it loops through each pixel, recording the pixel it looped over, and does it until it hits a non green like color, at which point it records the number of pixels away from the edge. I went with four loops, because i wanted to minimize the number of pixels i had to traverse (I can do the same thing with one loop but it would traverse across every pixel). The visitedPixel set prevents dealing with the same pixel. After the loops were done, it got a set of pixels that can be used to trim out the green screen edges, and thus cropping the image.
def trim_greenscreen_and_crop(image_name, output_name):
img = Image.open(image_name)
pixels = img.load()
width = img.size[0]
height = img.size[1]
visitedPixel = set()
box = [0, 0, 0, 0]
# left edge
break_flag = False
for x in range(width):
for y in range(height):
coords = (x, y)
r, g, b = pixels[x, y]
if not (g > r and g > b and g > 200) and coords not in visitedPixel:
box[0] = x - 1
break_flag = True
break
visitedPixel.add(coords)
if break_flag:
break
# top edge
break_flag = False
for y in range(height):
for x in range(width):
coords = (x, y)
r, g, b = pixels[x, y]
if not (g > r and g > b and g > 200) and coords not in visitedPixel:
box[1] = y-1
break_flag = True
break
visitedPixel.add(coords)
if break_flag:
break
# right edge
break_flag = False
for x in range(width - 1, -1, -1):
for y in range(height):
coords = (x, y)
r, g, b = pixels[x, y]
if not (g > r and g > b and g > 200) and coords not in visitedPixel:
box[2] = x 1
break_flag = True
break
visitedPixel.add(coords)
if break_flag:
break
# bottom edge
break_flag = False
for y in range(height - 1, -1, -1):
for x in range(width):
coords = (x, y)
r, g, b = pixels[x, y]
if not (g > r and g > b and g > 200) and coords not in visitedPixel:
box[3] = y 1
break_flag = True
break
visitedPixel.add(coords)
if break_flag:
break
cropped_img = img.crop(box)
if cropped_img.size == (0, 0):
return img.size
# cropped_img.save(output_name)
return cropped_img.size
Before:
After:
CodePudding user response:
So i figured using numpy, and got this much faster solution which involves finding the variance of the rows and columns, thanks to MarkSetchell's idea.
draft:
def trim_greenscreen_and_crop(image_name, output_name):
# use numpy to read the image
img = Image.open(image_name)
np_img = np.array(Image.open(image_name))
# use numpy to get the variance across the rows and columns
row_var = np.var(np_img, axis=0)
col_var = np.var(np_img, axis=1)
# select the rows and columns with some variance (basically not all green)
no_variance_row = np.where(row_var > 5)
no_variance_col = np.where(col_var > 5)
# checks if the entire image is green, then dont trim
if len(no_variance_row[0]) == 0 or len(no_variance_col[0]) == 0:
return img.size
else:
# crops the image using the distance from the edges to the first non-green pixel
cropped_img = img.crop((no_variance_row[0][0], no_variance_col[0][0], no_variance_row[0][-1], no_variance_col[0][-1]))
cropped_img.save(output_name)
return cropped_img.size
CodePudding user response:
You could speed up edge detection if you consider image itself is 'big enough' and loop not every pixel of the source image but go by diagonal, incrementing in one go both x and y until reach non-green color. You can repeat this process from all four corners. I hope you got the idea.
Edit:
you can also speed up by checking not every pixel, but check pixels on some 'grid', i.e. increment x and y by some big enough step. This also will work if your image is big enough