This works and gets what you want, if I understand what you want correctly.
I used the red line, but it could just as well be a black line, or any colour line. You just need to know where the curve starts. Because the curve does not go completely to the left edge of the image, you need to put in some coordinates there.
The strategy is:
split the image in 2 parts, the upper part starting from the lowest point on the red curve,
the lower part starting from the highest point on the red curve.
Save these 2 parts separately.
Then, for the upper part, make the pixels below the curve 100% transparent.
For the lower part, make the pixels above the red curve 100% transparent.
Confusing is the way the coordinates are kind of reversed in the numpy array. I believe PIL starts with (0,0) as the bottom left corner, which is easier!
People say you should not use for loops because they are slow, but I can't see any other way to get the coordinates of the curve! There are lots of lines in the drawing.
import cv2
import numpy as np
# How to get a Region Of Interest from an image
"""
If we consider (0,0) as top left corner of an image called im
with left-to-right as x direction and top-to-bottom as y direction.
and we have (x1,y1) as the top-left vertex and (x2,y2)
as the bottom-right vertex of a rectangle region within that image, then:
roi = im[y1:y2, x1:x2]
"""
path2image = 'cv2/images/中国动画.png'
# Load the image
# this should load the image with alpha channel: im = cv2.imread('sunny_flat.png', -1)
# -1 loads the image with its alpha channel
# The alpha channel controls the transparency or opacity of a colour.
# 100% transparent = 0, 100% opaque = 255
# image is a numpy array of pixels in rows and columns
image = cv2.imread(path2image, -1)
# Get the dimensions of the image
# height = 612, width = 506, channels = 3
height, width, channels = image.shape # height = 612, width = 506, channels = 3
print(image.shape) # (612, 506, 4)
# pixel image[0,0] is the top left pixel
# get first red pixel by looking in gimp to get an idea of where it is
# start at 1 as the red line does not go to the edge
# at 303 we find: 303 [ 0 0 234 255]
# this is a fully opaque red pixel
# set pixel[-1] to zero to make it fully transparent
for p in range(300,306):
pixel = image[p,1]
if pixel[0] < 50 and pixel[1] < 50 and pixel[2] > 200:
print(p, pixel)
# tup is (y,x) initial value (303,1)
# if there is a red line, at least 1 adjoining pixel must be red
# look right and up and down to find the next red pixel in the line
# set initial red coordinates from the above
pixel_coords = [(302,0),(303,1)]
# this is some kind of red: 303 [ 0 0 234 255]
# look down 2 and up 2 for a red pixel in each column of pixels
# pixels in cv2 are [blue, green, red, transparency]
def next_red(tup):
for p in range(tup[0]-2, tup[0]+2):
pixel = image[p,tup[1]+1]
if pixel[0] < 50 and pixel[1] < 50 and pixel[2] > 200:
print(p, tup[1]+1, pixel)
return (p,tup[1]+1)
# found the start position of the red line using gimp (303,1)
# (303,1) is also the highest red pixel, the other end is lower
# use max to get the lowest point of the red line
# use the highest and lowest values to split the picture in 2 parts
# then make the bits you don't want transparent
# we want the y value of lowest red pixel, at the bottom of the red curve
# this turns out to be 409
max_y = 303
# the image is 506 pixels across, so image[0,505] is the top right pixel
for x in range(2, 506):
tup = pixel_coords[-1]
next_pixel = next_red(tup)
if next_pixel[0] > max_y:
max_y = next_pixel[0]
pixel_coords.append(next_pixel)
# now, len(pixel_coords) = 506, the width of the image
# now, max_y = 409 which is the lowest point on the curve
# make a numpy slice of the region of interest
# save the parts and have a look
roi_upper = image[0:409, 0:506]
savepath_upper = 'cv2/images/中国动画_上面.png'
cv2.imwrite(savepath_upper, roi_upper)
# reload the original in case of changes
image = cv2.imread(path2image, -1)
roi_lower = image[303:612, 0:506]
savepath_lower = 'cv2/images/中国动画_下面.png'
cv2.imwrite(savepath_lower, roi_lower)
# as far as I know, a digital image must be rectangular
# but some pixels can be 100% transparent
# now the picture is split in 2 overlapping halves
# just need to make the pixels below the red curve transparent for the upper half
# and the pixels above the curve transparent for the lower half
# do that by setting the alpha channel to zero
# start with the upper part
# we need to make the pixels below the red line transparent
roi_upper.shape # (409, 506, 4)
# pixel_coords[0] = (302, 0)
# pixel_coords[1] = (303, 1)
# a function to make all pixels in a column transparent
# starting with a given value and going down
def blank_out_from_below(tup):
start = tup[0]
column = tup[1]
for i in range(start, max_y):
roi_upper[i,column][-1] = 0
# works
for tup in pixel_coords:
blank_out_from_below(tup)
cv2.imwrite(savepath_upper, roi_upper)
# pixel_coords[0] = (302, 0)
# pixel_coords[1] = (303, 1)
# pixel_coords[2] = (303, 2)
# roi_lower.shape # (309, 506, 4)
# start at zero go down tup[0] minus the minimum value of y which is 302
def blank_out_from_above(tup):
stop = tup[0] - 302
column = tup[1]
for i in range(0, stop):
roi_lower[i,column][-1] = 0
# works
for tup in pixel_coords:
blank_out_from_above(tup)
cv2.imwrite(savepath_lower, roi_lower)
print('All done!')
The main point is, you need to find the coordinates of the curve you want to split on and save them in a list of tuples. The rest is then easy!
Was fun!