Python Forum

Full Version: Splitting Manhua & Manhwa
You're currently viewing a stripped down version of our content. View the full version with proper formatting.
Pages: 1 2
sorry for my bad English or wrong section,
I tried to split this image by whitespace (marked by the red line) using python
[attachment=3015]
I searched it for hours and found nothing,
Do you have any clue how to split it?
There is no colour red, just varying shades of red, but if the colour is red, its blue and green values will be small

I think cv2 designates its pixels BGR, not RGB. Or, the shape of the numpy array for the pixels is translated.

Thus, to find all the red pixels, something like this might do.

import cv2
import numpy as np

path2image = 'cv2/images/中国动画.png' # your original png
savepath = 'cv2/images/中国白黑.jpg'

# Load the image
image = cv2.imread(path2image)

# Get the dimensions of the image
height, width, channels = image.shape # height = 612, width = 506, channels = 3

for y in range(506):
    for x in range(612):
        pixel = image[x, y]
        if pixel[0] < 50 and pixel[1] < 50 and pixel[2] > 200:
            print(x, y, pixel)
This picks up 2 red pixels for each x, y across the image, which should be your curved red line.

Some of the output:

Output:
303 1 [ 0 0 234] 304 1 [ 44 36 238] 303 2 [ 23 15 236] 304 2 [ 17 7 236] 303 3 [ 41 34 237] 304 3 [ 15 6 236] 304 4 [ 16 7 236] 305 4 [ 34 26 237] 304 5 [ 32 24 237] 305 5 [ 15 7 236] 305 6 [ 12 3 235] 305 7 [ 17 8 235] 306 7 [ 21 12 236] 306 8 [ 12 3 235] 306 9 [ 14 5 235] 307 9 [ 37 29 237]
Somehow I think the numpy array is x = largest dimension, y = smallest dimension

From this, you could go through each column and get the pixels you want, reading the x,y tuples like (303,1), (304, 1)

I don't use cv2 much, but it is a great tool!

Tomorrow I will look again, it's Sunday afternoon, my brain won't work!
@Pedroski55, thank you for the reply,
I added the red mark to explain what part you should cut,
in the real case, this red mark does not exist,
i will wait for your next reply
Like I said, I don't use cv2 much, so I am not familiar with it.

Using Inkscape, I made a Dutch flag, 600 pixels by 300 pixels, made of 3 bars, 600 wide and 100 high, red, white and blue. I saved this as a .png.

Then I rotated it 90 degrees clockwise, giving a long narrow French flag.

I wanted to be clear what is what where, so I did this:

import cv2 as cv
import numpy as np

path2image = 'cv2/images/中国动画.png'
# French flag: vertical bars blue, white, red
# top let pixel is blue
path2image = 'cv2/images/french_flag.png'
# pixel = image[0,0]
# pixel array([255,   0,   0], dtype=uint8)
# Dutch flag: horizontal bars blue, white, red 600 wide x 100 high
# top left pixel is red
path2image = 'cv2/images/dutch_flag.png'
savepath = 'cv2/images/中国白黑.jpg'

image = cv2.imread(path2image) # the French flag
height, width, channels = image.shape
# the vertical bars are 100 wide and 600 high
# somehow x and y are reversed
for i in range(95, 105):
    pixel = image[i, 0]
    print(pixel)
# this goes down the image, all pixels are blue    
"""
[255   0   0]
[255   0   0]
[255   0   0]
[255   0   0]
[255   0   0]
[255   0   0]
[255   0   0]
[255   0   0]
[255   0   0]
[255   0   0]
"""
# try this way
for i in range(95, 105):
    pixel = image[0, i]
    print(pixel)

# this goes across the image
# shows the border from blue to white
"""   
[255   0   0]
[255   0   0]
[255   0   0]
[255   0   0]
[255   0   0] # blue ends here white starts next pixel
[255 255 255]
[255 255 255]
[255 255 255]
[255 255 255]
[255 255 255]
"""
# the pixels are [blue, green, red]
for i in range(195, 205):
    pixel = image[0, i]
    print(pixel)

"""   
[255 255 255]
[255 255 255]
[255 255 255]
[255 255 255]
[255 255 255] # white ends here, red starts next pixel
[  0   0 255]
[  0   0 255]
[  0   0 255]
[  0   0 255]
[  0   0 255]
"""
Getting back to your image, we need to find the black curve on the white background. It would be easier if it was actually red!

I read, cv2 has no way of identifying a curve.

All we can do is look for changes in the pixels from white to black, in the region you are interested in. Trouble is, there are other black lines on the white, like the speech bubble 你怎么能确定在我身上?

Today I am a bit busy, but tonight I will look again, Maybe this will give you a clearer understanding!
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!
Here another approach using Pillow and NumPy.
from PIL import Image
import numpy as np

image_path = "img.png"
img = Image.open(image_path)
# Convert the image to RGB for processing
img_np = np.array(img)
# Get image dimensions
height, width, _ = img_np.shape
# Convert the image to grayscale to focus on intensity changes
gray_img_np = np.mean(img_np, axis=2)
# Calculate the difference
row_diffs = np.diff(gray_img_np, axis=0)
split_y = np.argmax(np.sum(np.abs(row_diffs), axis=1))
# Split the image at the detected y-coordinate
top_half = img.crop((0, 0, width, split_y))
bottom_half = img.crop((0, split_y, width, height))
# Save or display the split images
top_half.show()  # top_half.save("top_half.png")
#bottom_half.show()  # bottom_half.save("bottom_half.png")
[Image: qxdYEs.gif]
@Pedroski55
Error:
if next_pixel[0] > max_y: TypeError: 'NoneType' object is not subscriptable
@snippsat
I tried your code and worked as you intended, but I will do some research using other images
The first next_pixel comes from the pixel (303,1), which is pixel_coords[-1] at the start. Then we look in column 2 for the next red pixel.

pixel_coords = [(302,0),(303,1)]
Then the function

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)
finds the red pixel in the right-hand column and appends the result to pixel_coords.

If you get this error:

Quote:if next_pixel[0] > max_y:
TypeError: 'NoneType' object is not subscriptable

Maybe you are working with your original image, which has no red line?

Or you did not declare:

pixel_coords = [(302,0),(303,1)]
If you have a black line, of course you won't find red pixels. Black pixels are (0, 0, 0, 255) with the alpha channel.

def next_black(tup):
    for p in range(tup[0]-2, tup[0]+2):
        pixel = image[p,tup[1]+1]
        if pixel[0] < 10 and pixel[1] < 10 and pixel[2]< 10:
                print(p, tup[1]+1, pixel)
                return (p,tup[1]+1)
I don't know how to post pictures here, but I have 2 images, neatly split on the red, curved line.
@Pedroski55 no red line.
the redline is representative which part you should cut,
in real life case that red line is not exist
Ah, well, if there is no red line, black line or any colour line, where exactly do you want to split the picture?

Post your original picture!
Pages: 1 2