Home > OS >  Python, crop an image to extract a single side of it
Python, crop an image to extract a single side of it

Time:06-07

I have an image and the vertexs of each sides of it, meaning that, if we see each side as a segment (A,B) i have both the A and B point coordinates.

Now i would like to crop the image so that i have only one segment of the image (which is the side), for example That's just one side of the original image

For now i tried to do it in opencv with binary masking creating a line (fromt he vertexes i have) and using it to crop the image so that i have the pixels corresponding to that side. The problem with this approach is that the resulting image is of the same size of the orginal one but everything that is cropped is now a [0,0,0,0] (rgba) pixel

Instead, what i want is the following: if for example the side i want to extract is 20px long, the resulting image should be of shape (1,20,4) 1 height, 20 width and 4 channels (rgba)

I tried to do it with slicing but I don't know how to account to the fact that the sides can also be oblique lines.

Example image: Example image Corresponding vertexes: [[[107, 32], [95, 30]], [[95, 30], [26, 103]], [[26, 103], [28, 119]], [[28, 119], [103, 109]], [[103, 109], [122, 64]], [[122, 64], [107, 32]]]

CodePudding user response:

So as far as I understand your question, it seems that you are able to crop with numpy slices and with a mask and manage the alpha channel, but you need to extract that cropped region as a bounding box of the shape?

As long as you have the coordinates which define the polygon, you can find the leftmost, rightmost, topmost and the bottom coordinates and slice that region.

See my answer for this example:

Finding patches of certain colors with a size smaller than x. (x =number of pixels)

If your coordinates = c:

c = [[[107, 32], [95, 30]], [[95, 30], [26, 103]], [[26, 103], [28, 119]], [[28, 119], [103, 109]], [[103, 109], [122, 64]], [[122, 64], [107, 32]]]

Then you find the extreme coordinates with something like that (given that the coordinates are in the format [y,x] as it seems)

top = tuple(c[c[:,:,].argmin()][0]
bottom = tuple(c[c[:,:,].argmax()][0]
left = tuple(c[c[:,:,].argmin()][1]
right = tuple(c[c[:,:,].argmax()][1]

( You can just iterate and find the max/min in a more ordinary way to verify for more complex examples, if you wish to use a simpler code.)

You know is it reasonable to add one pixel margin on each sides, so left = left - 1, right = right 1, top = top - 1, bottom = bottm 1, but then you have to watch is the new value < 0 or > the end of the image.

CodePudding user response:

If you don't need use numpy obligatorily, you can use Image from PIL

from PIL import Image

img1 = Image.open(r"image_name.png")

img1 = img1.crop((cord_x, cord_y, width, height))

img1.save("name_saved.png")

UPDATE: The another code, help for rectangle shapes, here is a more generic code for any array of polygon cords

import numpy
from PIL import Image, ImageDraw

im = Image.open("image_name").convert("RGBA") #only convert if is necesary
image_array = numpy.asarray(im)

#Create mask
polygon = [(107, 32), (95, 30), (26, 103), (28, 119), (103, 109), (122, 64)] #this cords is from your example
mi = Image.new('L', (image_array.shape[1], image_array.shape[0]), 0)
ImageDraw.Draw(mi).polygon(polygon, outline=1, fill=1)
mask = numpy.array(mi)

new_image_array = numpy.empty(image_array.shape,dtype='uint8')
new_image_array[:,:,:3] = image_array[:,:,:3]
new_image_array[:,:,3] = mask*255

#Convert numpy array to image
image_output = Image.fromarray(new_image_array, "RGBA")
image_output.save("file_output_name")
  • Related