Python Image Library: image saved after pixel manipulation is always white - python

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')

Related

How to crop image using masked image and overlay on top of other image with same color?

I'm trying to crop an image based on the mask image and paste the cropped image on the new background image. I was able to achieve this but the cropped image is in gray color instead of color as in the original image
As it can be seen in the above image cropped image color is not the same as the original source cat image color, cropped image is a greyish color whereas in the original image it contains yellow golden color.
my code is as below to perform this
import cv2
src1=cv2.imread('cat.jpg',0)
mask=cv2.imread('mask_cat.jpg',0)
ret, thresh1 = cv2.threshold(mask, 0, 255, cv2.THRESH_BINARY)
src1 [thresh1==0] = 0
h, w = src1.shape
red = (0, 0, 0)
width, height = 1742, 815
back =cv2.cvtColor( create_blank(width, height, rgb_color=red),cv2.COLOR_BGR2GRAY)
hh, ww = back.shape
yoff = round((hh-h)/2)
xoff = round((ww-w)/2)
result = back.copy()
result[yoff:yoff+h, xoff:xoff+w] = src1
How can I get the same color in the cropped image as the original source color from where it cropped? Any suggestion or help solving this will be appreciated.
import cv2
cat = cv2.imread('cat.jpg')
mask_cat = cv2.imread('mask_cat.jpg', 0)
result = cv2.bitwise_and(cat,cat,mask = mask_cat)
I also see you try to reshape the image.It can be done as follows.
width, height = 1742, 815
reshaped_result = cv2.resize(result, dsize=(width, height))
To place the cropped image on resized image
width, height = 1742, 815
result_final = np.zeros((height,width,3), np.uint8)
h, w = result.shape[:2]
hh, ww = result_final.shape[:2]
yoff = round((hh-h)/2)
xoff = round((ww-w)/2)
result_final[yoff:yoff+h, xoff:xoff+w] = result

to crop the rgb image based on the pixel value

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], :]

Rotating an Image 90 degrees

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:
⇒

What's the most simple way to crop a circle thumbnail from an image?

I am trying to crop a centered (or not centered) circle from this image:
I stole this code from the existing questions regarding this topic on stack overflow, something goes wrong though:
import cv2
file = 'dog.png'
img = cv2.imread(file)
img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
circle = cv2.HoughCircles(img,
3,
dp=1.5,
minDist=10,
minRadius=1,
maxRadius=10)
x = circle[0][0][0]
y = circle[0][0][1]
r = circle[0][0][2]
rectX = (x - r)
rectY = (y - r)
crop_img = img[rectY:(rectY+2*r), rectX:(rectX+2*r)]
cv2.imwrite('dog_circle.png', crop_img)
Output:
Traceback (most recent call last):
File "C:\Users\Artur\Desktop\crop_circle - Kopie\crop_circle.py", line 14, in <module>
x = circle[0][0][0]
TypeError: 'NoneType' object is not subscriptable
cv2.HoughCircles() seems to produce None instead of a cropped circle array. How do I fix this?
first: HoughCircles is used to detect circles on image, not to crop it.
You can't have circle image. Image is always rectangle but some of pixels can be transparent (alpha channel in RGBA) and programs will not display them.
So you can first crop image to have square and later add alpha channel with information which pixels should be visible. And here you can use mask with white circle on black background. At the end you have to save it as png or tiff because jpg can't keep alpha channel.
I use module PIL/pillow for this.
I crop square region in center of image but you can use different coordinates for this.
Next I create grayscale image with the same size and black background and draw white circle/ellipse.
Finally I add this image as alpha channel to cropped image and save it as png.
from PIL import Image, ImageDraw
filename = 'dog.jpg'
# load image
img = Image.open(filename)
# crop image
width, height = img.size
x = (width - height)//2
img_cropped = img.crop((x, 0, x+height, height))
# create grayscale image with white circle (255) on black background (0)
mask = Image.new('L', img_cropped.size)
mask_draw = ImageDraw.Draw(mask)
width, height = img_cropped.size
mask_draw.ellipse((0, 0, width, height), fill=255)
#mask.show()
# add mask as alpha channel
img_cropped.putalpha(mask)
# save as png which keeps alpha channel
img_cropped.save('dog_circle.png')
img_cropped.show()
Result
BTW:
In mask you can use values from 0 to 255 and different pixels may have different transparency - some of them can be half-transparent to make smooth border.
If you want to use it in HTML on own page then you don't have to create circle image because web browser can round corners of image and display it as circle. You have to use CSS for this.
EDIT: Example with more circles on mask.
from PIL import Image, ImageDraw
filename = 'dog.jpg'
# load image
img = Image.open(filename)
# crop image
width, height = img.size
x = (width - height)//2
img_cropped = img.crop((x, 0, x+height, height))
# create grayscale image with white circle (255) on black background (0)
mask = Image.new('L', img_cropped.size)
mask_draw = ImageDraw.Draw(mask)
width, height = img_cropped.size
mask_draw.ellipse((50, 50, width-50, height-50), fill=255)
mask_draw.ellipse((0, 0, 250, 250), fill=255)
mask_draw.ellipse((width-250, 0, width, 250), fill=255)
# add mask as alpha channel
img_cropped.putalpha(mask)
# save as png which keeps alpha channel
img_cropped.save('dog_2.png')
img_cropped.show()
This answer explains how to apply a mask. First, read in the image:
import cv2
import numpy as np
img = cv2.imread('dog.jpg')
Next create a mask, or a blank image that is the same size as the source image:
h,w,_ = img.shape
mask = np.zeros((h,w), np.uint8)
Then, draw a circle on the mask. Change these parameters based on where the face is:
cv2.circle(mask, (678,321), 5, 255, 654)
Finally, mask the source image:
img = cv2.bitwise_and(img, img, mask= mask)
Here is the mask:
And the output:
The idea is to create a black mask then draw the desired region to crop out in white using cv2.circle(). From there we can use cv2.bitwise_and() with the original image and the mask. To crop the result, we can use cv2.boundingRect() on the mask to obtain the ROI then use Numpy slicing to extract the result. For this example I used the center point derived from the image's width and height
import cv2
import numpy as np
# Create mask and draw circle onto mask
image = cv2.imread('1.jpg')
mask = np.zeros(image.shape, dtype=np.uint8)
x,y = image.shape[1], image.shape[0]
cv2.circle(mask, (x//2,y//2), 300, (255,255,255), -1)
# Bitwise-and for ROI
ROI = cv2.bitwise_and(image, mask)
# Crop mask and turn background white
mask = cv2.cvtColor(mask, cv2.COLOR_BGR2GRAY)
x,y,w,h = cv2.boundingRect(mask)
result = ROI[y:y+h,x:x+w]
mask = mask[y:y+h,x:x+w]
result[mask==0] = (255,255,255)
cv2.imshow('result', result)
cv2.waitKey()

How to randomly crop a polygon from image A and paste it on image B in python?

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)

Categories

Resources