def rotate_picture_90_left(img: Image) -> Image:
"""Return a NEW picture that is the given Image img rotated 90 degrees
to the left.
Hints:
- create a new blank image that has reverse width and height
- reverse the coordinates of each pixel in the original picture, img,
and put it into the new picture
"""
img_width, img_height = img.size
pixels = img.load() # create the pixel map
rotated_img = Image.new('RGB', (img_height, img_width))
pixelz = rotated_img.load()
for i in range(img_width):
for j in range(img_height):
pixelz[i, j] = pixels[i, j]
return rotated_img
I believe my code does not seem to work because of the new image I have created and the reverse width, length and reversing the coordinates in the original picture. How can I fix my code to rotate the image correctly?
You need to consider following logic when converting coordinates:
y turning to x
x turning to y but moving from end to start
Here is the code:
from PIL import Image
def rotate_picture_90_left(img: Image) -> Image:
w, h = img.size
pixels = img.load()
img_new = Image.new('RGB', (h, w))
pixels_new = img_new.load()
for y in range(h):
for x in range(w):
pixels_new[y, w-x-1] = pixels[x, y]
return img_new
Example:
⇒
Related
I am having an rgb image and I want to crop it from all sides. i have tried the below code on gray scale image it works but for RGB image it doesn't.
My code is:
import cv2
import numpy as np
import glob,os
from PIL import Image
inputdir = "/Users/sripdeep/Desktop/Projects/3 Norm_Sub_Norm/rgb/"
outpath = "/Users/sripdeep/Desktop/Projects/3 Norm_Sub_Norm/rgb_norm/"
#filesname = sorted(glob.glob( inputdir + "*.jpg"))
for img1 in sorted(glob.glob( inputdir + "*.jpeg")):
img_path = img1.split('/')[-1]
imgstr = img_path.split('.')[0]
print(imgstr)
im1 = cv2.imread(img1,0)
thresh = cv2.threshold(im1, 100, 255, cv2.THRESH_BINARY)[1]
x, y, w, h = cv2.boundingRect(im1) # calculates nonzero pixels
# print(x,y,w,h)
# a tuple (x, y, w, h) with (x, y) the upper left point
# as well as width w and height h of the bounding rectangle.
left = (x, np.argmax(thresh[:, x])) #
right = (x+w-1, np.argmax(thresh[:, x+w-1])) #
top = (np.argmax(thresh[y, :]), y) #
bottom = (np.argmax(thresh[y+h-1, :]), y+h-1)
print('left: {}'.format(left))
print('right: {}'.format(right))
print('top: {}'.format(top))
print('bottom: {}'.format(bottom))
# cropped__img = img[y1:y2, x1:x2]
cropped__img = thresh[top[1]:bottom[1], left[0]:right[0]]
cropped__img = cv2.resize(cropped__img, (256,256), interpolation=cv2.INTER_LINEAR)
save_fname = os.path.join(outpath, os.path.basename(imgstr)+'.jpg')
cv2.imwrite(save_fname, cropped__img)
I want my image to be cropped like this:
But I get output is:
Your approach is close, and can be simplified a bit.
Note that cv2.boundingRect(..) can only be applied on single channel images. Because of this, it seems easiest to first read a color image, then convert it to grayscale to figure out bounding rectangle (x,h,w,h) coordinates (x1, y1, x2, y2), and then apply cropping to the original color image.
Perhaps something like this:
im1 = cv2.imread(img1,0) # color image
im1_gray = cv2.cvtColor(im1, cv2.COLOR_BGR2GRAY) # grayscale
thresh = cv2.threshold(im1_gray, 100, 255, cv2.THRESH_BINARY)[1] # threshold image
x, y, w, h = cv2.boundingRect(thresh) # find the bounding rectangle of nonzero points in the image
cropped__img = im1[y:y+h, x:x+w,:] # crop the original color image
This one is pretty straightforward. A color image in OpenCV in Python is a 3-dimensional NumPy array (x, y, color). A grayscale image is a 2-dimensional array (x, y).
In this line, you are cropping a grayscale (2D) image by giving limits on the x and y dimensions:
cropped__img = thresh[top[1]:bottom[1], left[0]:right[0]]
see this answer for more info on slice notation in Python. To crop a color image, you need to tell Python to grab everything in the third dimension by using simply :
# v
cropped__img = img[top[1]:bottom[1], left[0]:right[0], :]
I'm trying to crop a random polygon from one image and pasting it on another. So far I know how to do this for a rectangular box.
Is there a way to crop an n-shaped polygon and paste it onto another image? Thanks!
from PIL import Image
import random
x_rand = random.randrange(0,1080)
y_rand = random.randrange(0,1920)
w_rand = random.randrange(0,min(1080, (1080-x_rand)))
h_rand = random.randrange(0,min(1920, (1920-y_rand)))
# tmp1 - source image path
# tmp2 - target image path
image1 = Image.open(tmp1)
x, y, w, h = (x_rand, y_rand, w_rand, h_rand)
print(x, y, w, h)
box = (x, y, x + w, y + h)
region = image1.crop(box)
image2 = Image.open(tmp2)
image2.paste(region, box)
Ok, I've figured how to cut a polygon image, how do I paste it?
import cv2
import numpy as np
image = cv2.imread(source)
image2 = cv2.imread(target)
# Create white pixel mask
mask = np.ones(image.shape, dtype=np.uint8)
mask.fill(255)
# Specify polyon and crop
roi_corners = np.array([[(0, 300), (200, 300), (300, 400), (0, 400)]], dtype=np.int32)
cv2.fillPoly(mask, roi_corners, 0)
# Crop original image
masked_image = cv2.bitwise_or(image, mask)
can someone tell me how to implement a bisection of the image into upper and lower part? so that I could overlap them. for example, I have an image and I should divide it to calculate a number of pixels on each part. I am new to OpenCV and don't exactly understand the geometry of the image.
To simplify #avereux's answer:
In Python you can use splicing to break down an image into sub-images. The syntax for this is:
sub_image = full_image[y_start: y_end, x_start:x_end]
Note that for images, the origin is the top-left corner of the image. So a pixel on the first row of the image (that is the topmost row) would have coordinates x_coordinate = x, y_coordinate = 0
To get the shape of the image, use image.shape. This returns (no_of_rows, no_of_cols)
You can use these to break down the image any which way you want.
You can crop the top and bottom portion of the image down the middle horizontally.
Open the image.
import cv2
import numpy as np
image = cv2.imread('images/blobs1.png')
cv2.imshow("Original Image", image)
cv2.waitKey(0)
Use image.shape to let us capture the height and width variables.
height, width = image.shape[:2]
print image.shape
Now we can start cropping.
# Let's get the starting pixel coordiantes (top left of cropped top)
start_row, start_col = int(0), int(0)
# Let's get the ending pixel coordinates (bottom right of cropped top)
end_row, end_col = int(height * .5), int(width)
cropped_top = image[start_row:end_row , start_col:end_col]
print start_row, end_row
print start_col, end_col
cv2.imshow("Cropped Top", cropped_top)
cv2.waitKey(0)
cv2.destroyAllWindows()
# Let's get the starting pixel coordiantes (top left of cropped bottom)
start_row, start_col = int(height * .5), int(0)
# Let's get the ending pixel coordinates (bottom right of cropped bottom)
end_row, end_col = int(height), int(width)
cropped_bot = image[start_row:end_row , start_col:end_col]
print start_row, end_row
print start_col, end_col
cv2.imshow("Cropped Bot", cropped_bot)
cv2.waitKey(0)
cv2.destroyAllWindows()
Finally, we can use image.size to give use the number of pixels in each part.
cropped_top.size
cropped_bot.size
You can do the same thing with contours but it will involve bounding boxes.
Here is much more flexible method by which you can cut image in half or into 4 equal parts or 6 parts how ever parts you may need.
This code will crop image first into 2 piece Horizontally
Then for each of that 2 piece it will crop another 3 images
Leaving total 6 cropped images
Change the CROP_W_SIZE and CROP_H_SIZE value to adjust your crop settings.
you will need a CROP folder where this code will save images to.
import cv2,time
img = cv2.imread('image.png')
img2 = img
height, width, channels = img.shape
# Number of pieces Horizontally
CROP_W_SIZE = 3
# Number of pieces Vertically to each Horizontal
CROP_H_SIZE = 2
for ih in range(CROP_H_SIZE ):
for iw in range(CROP_W_SIZE ):
x = width/CROP_W_SIZE * iw
y = height/CROP_H_SIZE * ih
h = (height / CROP_H_SIZE)
w = (width / CROP_W_SIZE )
print(x,y,h,w)
img = img[y:y+h, x:x+w]
NAME = str(time.time())
cv2.imwrite("CROP/" + str(time.time()) + ".png",img)
img = img2
Check this out for two left-right and bottom-up division:
import cv2
img = cv2.imread('img.jpg')
h, w, channels = img.shape
for left-right division:
half = w//2
left_part = img[:, :half]
right_part = img[:, half:]
cv2.imshow('Left part', left_part)
cv2.imshow('Right part', right_part)
for top-bottom division:
half2 = h//2
top = img[:half2, :]
bottom = img[half2:, :]
cv2.imshow('Top', top)
cv2.imshow('Bottom', bottom)
I edited a bit this source: https://www.geeksforgeeks.org/dividing-images-into-equal-parts-using-opencv-in-python/
I have over 100 images that each of them is in different angle. I have written an basic python code to rotate each of the image one by one from any angle to zero degree ( I mean making them flat). The python code that I have attached is a simple code and unfortunately it doesn't automatically find the angle and it does not make it exact zero. Any time for any of the image I should find the angle and run the code for many time to make it zero (sometimes I am not able to make it exactly flat or what we celled zero degree). According to the images that I have attached, image1 is one of the sample image as an input and image_2 is the rotated image that I want to have at the end as an output.
I would like to ask anyone that can help me to modify the current code or provide me with the new python code (which I prefer the new code)that I can make my images rotate from any angle to zero degree.
please feel free to ask me for more explanation if you want.
my opencv-python code is:
import cv2
import numpy as np
img = cv2.imread('image1.tif')
num_rows, num_cols = img.shape[:2]
rotation_matrix = cv2.getRotationMatrix2D((num_cols/2, num_rows/2),69.4, 1)
img_rotation = cv2.warpAffine(img, rotation_matrix, (num_cols, num_rows))
cv2.imshow('Rotation', img_rotation)
cv2.imwrite('image_2.tif',img_rotation)
cv2.waitKey()
Note: input and output images deleted.
it's definitely not the most robust method, but perhaps an option would be to:
assume that the boundary is all black
identify the top-most (x0,y0) / right-most (x1,y1) corners of the image
calculate the angle of rotation as alpha = math.atan2(x1-x0,y1-y0)
I downloaded your figure (it was converted to png on imgur) and tested the procedure with:
#!/usr/bin/env python
import cv2
import math
import numpy as np
img = cv2.imread('test.png')
H, W = img.shape[:2]
x0,y0 = None,None
x1,y1 = None,None
#scan all rows starting with the first
for i in range(0, H):
row = img[i].sum(axis=1)
s = np.sum(row)
if s:
#if there is at least one non-black pixel, mark
#its position
x0 = np.max(np.where(row>0))
y0 = i
break
#scan all columns starting with the right-most one
for j in range(W-1,-1,-1):
col = img[:,j,:].sum(axis=1)
s = np.sum(col)
if s:
#mark the position of the first non-black pixel
x1 = j
y1 = np.min(np.where(col>0))
break
dx = x1 - x0
dy = y1 - y0
alpha = math.atan2(dx, dy) / math.pi * 180
rotation_matrix = cv2.getRotationMatrix2D((W/2, H/2), -alpha, 1)
img_rotation = cv2.warpAffine(img, rotation_matrix, (W, H))
cv2.imwrite('image_2.tif',img_rotation)
EDIT:
The previous method can be inaccurate in case the "corner" pixel is black as well so that the calculated angle is then biased. Slightly more accurate approach could be as follows:
determine the "upper" boundary of the rectangle (i.e., the coordinates of the pixels defining the edges)
take the edge the projection of which on the x-axis is longer
fit the coordinates in order to calculate the slope of the line defining the edge
The implementation:
#!/usr/bin/env python
import cv2
import math
import numpy as np
img = cv2.imread('test.png')
H, W = img.shape[:2]
data = []
for j in range(0, W):
col = img[:,j,:].sum(axis=1)
s = np.sum(col)
if not s:
continue
for i in range(0, H):
if col[i] > 0:
data.append((j, i))
break
y_min, min_pos = None, None
for idx, (x, y) in enumerate(data):
if y_min is None or y < y_min:
y_min = y
min_pos = idx
N = len(data)
if min_pos > N - min_pos:
data = data[:min_pos]
else:
data = data[min_pos:]
data = np.asarray(data).T
coeffs = np.polyfit(data[0], data[1], 1)
alpha = math.atan(coeffs[0]) / math.pi * 180
print(alpha)
rotation_matrix = cv2.getRotationMatrix2D((W/2, H/2), alpha, 1)
img_rotation = cv2.warpAffine(img, rotation_matrix, (W, H))
cv2.imwrite('image_2.tif',img_rotation)
another way to find the angle is (asuming that the image is over a black background):
Convert the image to grayscale
Segmentate the image using a threshold
Find the contours of the image
Find the parameters of the ellipse that fit the contour
import cv2
import numpy as np
image = cv2.imread("DlYEa.png")
gray = cv2.cvtColor(image, cv2.COLOR_RGB2GRAY)
num_rows, num_cols = image.shape[:2]
_, thresh = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY)
img, contours, hier = cv2.findContours(thresh, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
cnt = contours[0]
(x, y), (Ma, ma), angle = cv2.fitEllipse(cnt)
angle = int(angle - 90)
rotation_matrix = cv2.getRotationMatrix2D((num_cols/2, num_rows/2), angle, 1)
img_rotation = cv2.warpAffine(image, rotation_matrix, (num_cols, num_rows))
cv2.imshow("rotation", img_rotation)
cv2.waitKey()
cv2.destroyAllWindows()
I have the following code. src_img is a 1250x1250 rgb image. I want to create another grayscale image with averaged intensity.
from PIL import Image
img = Image.open(src_img)
width, height = img.size
avg_img = Image.new('1', img.size, 'black')
avg_pixels = avg_img.load()
for x in range(width):
for y in range(height):
r, g, b = img.getpixel((x, y))
avg_pixels[x, y] = int((r + g + b) / 3.0)
avg_img.save('avg.tiff')
But the resulting avg.tiff file is plain white. I can see that avg_pixels has the necessary values but the saved image doesn't correspond to those.
Mode '1' is a bilevel image - meaning either white or black. For grayscale, you want 'L'.
avg_img = Image.new('L', img.size, 'black')