Python fit rectangle picture in squared area - python

I would like to use Python (and the PIL library) to resize a picture (eventually with width > height or height > width) in a fixed size, let's say (144px * 144px).
I've already found topics about cropping the picture, but I would like to :
Find the biggest side of the picture
Do a thumbnail of the picture with biggest side = 144px and the other side = a ratio
Center the smallest side
Fill the borders of the smallest side with a transparent background so that the final picture is (144px * 144px)
Any ideas on how to do that? For the two first points I would use :
image = PIL.Image.open(pic)
image.thumbnail((144,144), PIL.Image.BILINEAR)
But then I'm blocked. Thanks!

Related

Extracting same point from images

Hope you guys are doing well.
I have a question about opencv and extracting the same point from number of images.
Say, we have a dataset of around 100 images. (maybe more but for this purpose it will suffice).
Image will look something like:
As you can see in the image, there is an area marked in RED. I have marked that using Paint for this purpose. It indicates the highest point of the heap of soil. All the 100 images we have look more or less the same (without that crane in the background. But it can be removed using some opencv techniques, so that is not an issue). But this heap of soil can be either on the left hand side or the right hand side. According to its position, the coordinate of the highest point in the heap will change.
So, my question is, how to find this position given that the heap can be either on left or right side?
Note that this position can be relative to some object (for example in this image, midpoint of the crane) or if the images are of different size than we can resize the images to have same dimensions and take the coordinates of the point w.r.t the image itself.
How do we find out the highest point of the heap though? Should we manually go through each image, label that point and make a dataset with the images and bounding boxes? Or is there another decent soulution to this?
Also, if the soil heap is labelled manually (Like shading the required area i.e. heap of an image) using Paint or some other software, would that help too? But I cannot think of anything to do after this.
Thank You.
So I am sure this can be done in a better way than my answer, but here goes:
"Also, if the soil heap is labelled manually (Like shading the required area i.e. heap of an image) using Paint or some other software, would that help too? But I cannot think of anything to do after this."
In regards to that particular statement if you mark out the region of interest in a distinctive color and shape, like you have done in the above example. You can use opencv to detect that particular region of interest and its coordinates within the image.
I think the best solution is deep learning because detective always has different backgrounds. You can use Faster rcnn, or if you want speed, you can make nice detectives with a good training using Yolo algorithm. You can find Github repo easily. The mathematics of work is described in these links.
Faster RCNN https://arxiv.org/abs/1506.01497
Yolo https://pjreddie.com/darknet/yolo/
basically you can resize image. Keep aspect ratio!
def image_resize(image, width = None, height = None, inter = cv2.INTER_AREA):
dim = None
(h, w) = image.shape[:2]
if width is None and height is None:
return image
if width is None:
# calculate the ratio of the height and construct the
# dimensions
r = height / float(h)
dim = (int(w * r), height)
else:
# calculate the ratio of the width and construct the
# dimensions
r = width / float(w)
dim = (width, int(h * r))
# resize the image
resized = cv2.resize(image, dim, interpolation = inter)
# return the resized image
return resized
image = image_resize(image, height = ..What you want..)

finding canvas boundary of a photo frame

Say we have a photo frame like the one above.
Starting from center, how would u find a rectangle with maximum area that can be used to draw (all pixels in the rectangle must be rgb(255,255,255)?
I need to find the x and y coordinate of point A and B shown in the picture.
One of my approach is to do this:
starting from the center, and expand the boundary like the graph above.
But I am not sure how you could write loop(s) like that.
You should use the flood fill algorithm: link.
I suggest you to use sets to store the pixels to be altered in a set; that way the number of recursions to be done can be reduced.
Edit: I obviously didn't read the question well. Still, the flood fill could be used, if you used it it on a circle that is expanded.
Start with a single pixel, that is the center of your circle.
set the radius lager by 1 unit.
Find the pixels within your circle, get their colours using flood fill.
If they are the same color, goto 2. If not, you have the radius finding the rectangle is next.
This algorithm may give you a possible solution, but there may be more than one, depending on your frame - you should start developing using some simple frame where the correctness of the solution can be judged easily.
Edit: based on the comment, the problem is to find the largest area axis-parallel rectangle in a polygon - and luckily there is a paper on this: here. Doesn't look as an easy task though.
I would use brute force here. Choose a y_bottom and y_top, determine the corresponding x_left and x_right and loop both y_bottom and y_top over the width of the picture. In pseudocode:
for y_bottom in range (0, H):
for y_top in range (y_bottom, H):
# Assume that x = W/2 is part of the rectangle
x_left = maximum X such that all pixels in box (x_left, y_top, W/2, y_bottom) are white
x_right = minimum X such that all pixels in box (W/2, y_top, x_right, y_bottom) are white
determine Area of box (x_left, y_top, x_right, y_bottom)
store this box if Area is larger than max found so far

Find upper left of image after resizing to fit screen

I have a problem I haven't been able to figure out-
Say I have an image of arbitrary dimensions. I resize it so that it fits inside 1024x768 while keeping the aspect ratio. I center it on the screen. After doing this, how can I find where the upper left corner will end up?
So, if the image is wider than it is tall, we end up with something like
The green rectangle started at a different size. It was resized to fit the pink rectangle. I want to find the upper left corner of the green rectangle.
I wrote a bunch of notes and drew a bunch of diagrams, but I'm getting all the wrong answers. Can someone explain how to do this? I'm using python 2.7
Let w,h be the size of your image.
To fit the width of 1024, we must scale the image by:
>>> r=1024./w
However, if the image is taller, after scaling it by r, its height won't fit the screen, so in this case the scaling factor is:
>>> if h*r > 768: r=768./h
The coordinate of the upper left corner of the scaled image is:
>>> (1024-w*r)*0.5,(768-h*r)*0.5
Edit:
A handy function to compute the topleft point (works in Python 2.x as well):
def topLeft(w,h,screenw=1024,screenh=768):
r=float(screenw)/float(w)
if h*r > screenh: r=float(screenh)/float(h)
return (screenw-w*r)*0.5,(screenh-h*r)*0.5

Mirror Image but wrong size

I am trying to input an image (image1) and flip it horizontally and then save to a file (image2). This works but not the way I want it to
currently this code gives me a flipped image but it just shows the bottom right quarter of the image, so it is the wrong size. Am I overwriting something somewhere? I just want the code to flip the image horizontally and show the whole picture flipped. Where did I go wrong?
and I cannot just use a mirror function or reverse function, I need to write an algorithm
I get the correct window size but the incorrect image size
def Flip(image1, image2):
img = graphics.Image(graphics.Point(0, 0), image1)
X, Y = img.getWidth(), img.getHeight()
for y in range(Y):
for x in range(X):
r, g, b = img.getPixel(x,y)
color = graphics.color_rgb(r, g, b)
img.setPixel(X-x, y, color)
win = graphics.GraphWin(img, img.getWidth(), img.getHeight())
img.draw(win)
img.save(image2)
I think your problem is in this line:
win = graphics.GraphWin(img, img.getWidth(), img.getHeight())
The first argument to the GraphWin constructor is supposed to be the title, but you are instead giving it an Image object. It makes me believe that maybe the width and height you are supplying are then being ignored. The default width and height for GraphWin is 200 x 200, so depending on the size of your image, that may be why only part of it is being drawn.
Try something like this:
win = graphics.GraphWin("Flipping an Image", img.getWidth(), img.getHeight())
Another problem is that your anchor point for the image is wrong. According to the docs, the anchor point is where the center of the image will be rendered (thus at 0,0 you are only seeing the bottom right quadrant of the picture). Here is a possible solution if you don't know what the size of the image is at the time of creation:
img = graphics.Image(graphics.Point(0, 0), image1)
img.move(img.getWidth() / 2, img.getHeight() / 2)
You are editing your source image. It would be
better to create an image copy and set those pixels instead:
create a new image for editing:
img_new = img
Assign the pixel values to that:
img_new.setPixel(X-x, y, color)
And draw that instead:
win = graphics.GraphWin(img_new, img_new.getWidth(), img_new.getHeight())
img_new.draw(win)
img_new.save(image2)
This will also check that your ranges are correct. if they are not, you will see both flipped and unflipped portions in the final image, showing which portions are outside of your ranges.
If you're not opposed to using an external library, I'd recommend the Python Imaging Library. In particular, the ImageOps module has a mirror function that should do exactly what you want.

Python image mirroring

I've been making a picture mirroring in horizontal and vertical axes. Now I'm going to make the diagonal.
I had done the hori and verti width two for loops which in the hori scenario loops through all the pixels in the height and only the half of the pixels in the width. Then it gets the color of the pixel and set the same color to the pixel on the other side. Going from the getWidth(pic) to the center.
Then I have my mirror in the middle of the pic. How to do the diagonal way?
Edit:
img_src = makePicture(pickAFile())
W = getWidth(img_src)
H = getHeight(img_src)
for x in range(W):
for y in range(H):
p = getPixel(img_src, x, y)
colorInSrc = getColor( getPixel(img_src, x, y) )
destPixel = getPixel(img_src, H-y-1, W-x-1)
setColor(destPixel, colorInSrc)
Using PIL (the Python Imaging Library) this is a relatively straightforward task. Notice however, that the output image is square -- thus not the same size as the original image.
Here is the code:
from PIL import Image, ImageDraw
# load the image, create the mirrored image, and the result placeholder
img = Image.open('img.png')
mirror = img.transpose(Image.FLIP_LEFT_RIGHT).transpose(Image.ROTATE_90)
sz = max(img.size + mirror.size)
result = Image.new(img.mode, (sz,sz))
result.paste(img, (0,0)+img.size)
# now paste the mirrored image, but with a triangular binary mask
mask = Image.new('1', mirror.size)
draw = ImageDraw.Draw(mask)
draw.polygon([0,0,0,sz,sz,sz], outline='white', fill='white')
result.paste(mirror, (0,0)+mirror.size, mask)
# clean up and save the result
del mirror, mask, draw
result.save('result.png')
If I understood correctly what you need is to "flip" the image by a diagonal. Since there are two of them I'll presume that you mean the one that goes from left bottom to right top.
In order to flip by this diagonal you need to transform each row from the source in columns in the destination. The left part of the rows will become the bottom part of the new columns. Also the topmost row will become the rightmost column. You will need to do this pixel by pixel on the whole image. Also keep in mind that the width and height of the image will be swapped.
Edit: A small example. Say you start with an image 5 pixels wide and 3 pixels high (5x3). You will need to create a new blank image 3 pixels wide and 5 pixels high.
If you start pixel numbering from left top corner with (0,0), then this pixel will end up at (2,4) in the new image, pixel (1,0) will end at (2,3) and so on.
If your original width and height are W and H then you should use something like this:
for x in xrange(W):
for y in xrange(H):
p = img_src.getpixel(x, y)
img_dest.setpixel(H-y-1, W-x-1)
This should work, but is not tested.
It's not really an Python question, is it?
The easiest solution would be to first mirror horizontal and then vertical.
Another one would be to switch pixel rows with columns.
Or to do your algorithm but switch the pixels from left-top to bottom-right...
Here's how to mirror diagonally in JES; It only works for a square image though:
def mirrorDiagonal(picture):
for sourceX in range(0,getWidth(picture)):
for sourceY in range (0,getHeight(picture)):
pex=getPixel(picture,sourceY,sourceX)
pix=getPixel(picture, sourceX,sourceY)
color=getColor(pix)
setColor(pex,color)
show(picture)

Categories

Resources