B&W image to binary array - python

I want to convert my b&w image(.png) to binary array(black is 1 white is 0). I have written some code, but it's not working. Error says: argument 2 to map() must support iteration.
Here is my code:
from PIL import Image
from resizeimage import resizeimage
import sys
def threshold(col):
s = sum(col)
return int(s > 255 * 3 // 2)
img = Image.open("filename.png")
ratio = float((img.size[1]) / (img.size[0]))
img = resizeimage.resize_cover(img, [100, int(ratio * 100)])
pixels = img.getdata()
binary = list(map(threshold, img))
array2d = [binary[i * img.size[0] : (i+1) * img.size[0]] for i in range(img.size[1])]
print('\n'.join(''.join(map(str, line)) for line in array2d))
Here is the image:

You need to convert your image to grayscale first, since PIL opens it as RGB. Then, invert the 0 & 255 values. Then, you can convert the non-zero values to 1. Here's one way:
from PIL import Image
import numpy as np
img = Image.open('bw_circle.png').convert('L')
np_img = np.array(img)
np_img = ~np_img # invert B&W
np_img[np_img > 0] = 1
And an alternative way using PIL for the inversion:
from PIL import Image, ImageOps
import numpy as np
img = Image.open('bw_circle.png').convert('L')
img_inverted = ImageOps.invert(img)
np_img = np.array(img_inverted)
np_img[np_img > 0] = 1

Related

Get contrast for each channel in an image opencv python

How to calculate each channel contrast in images?
Since they are lot of contrast definitions out there
Webar Contrast
Michelson Contrast
RMS contrast
I need to calculate these contrasts.
from PIL import Image
import numpy as np
from numpy import mean, sqrt, square
im = Image.open("leaf.jpg") # Image file name
pixels = list(im.getdata())
width, height = im.size
pixels = np.asarray([pixels[i * width:(i + 1) * width] for i in range(height)], dtype=int)
ch_1 = pixels[:,:,0]
ch_2 = pixels[:,:,1]
ch_3 = pixels[:,:,2]
rms_of_ch1 = sqrt(mean(square(ch_1)))
rms_of_ch2 = sqrt(mean(square(ch_2)))
rms_of_ch3 = sqrt(mean(square(ch_3)))

Adding black frame to images in a list

I have read in some images with the code below. These images are of different sizes. In order to get them to equal sizes, I would like to add a black frame around the images. I found some code to do this for a single image but not for a list as in my case.
import cv2
import numpy
import glob
import matplotlib.pyplot as plt
from PIL import Image, ImageOps
folders = glob.glob(r'path\to\images\*')
imagenames_list = []
for folder in folders:
for f in glob.glob(folder+'/*.png'):
imagenames_list.append(f)
read_images = []
for image in imagenames_list:
read_images.append(cv2.imread(image, cv2.IMREAD_GRAYSCALE))
To add a black frame for a single picture I used this code:
from PIL import Image
import numpy as np
old_im = Image.open('path/to/single/picture/*.png')
old_size = old_im.size
print(old_size)
new_size = (500, 500)
print(new_size)
new_im = Image.new("RGB", new_size)
x = int((new_size[0]-old_size[0])/2)
y = int((new_size[1]-old_size[1])/2)
new_im.paste(old_im, (x,y))
Image read by OpenCV are just numpy arrays. You can just use numpy slicing to copy:
def makeborder(cv2img, new_width, new_height):
'''
cv2img: an image returned by cv2.imread()
'''
# gray scale or BGR/BGRA
if len(cv2img.shape) == 2:
new_shape = (new_height, new_width)
else:
new_shape = (new_height, new_width, cv2img.shape[-1])
new_img = np.zeros(new_shape, dtype=cv2img.dtype)
# compute the offsets, similar to your x & y
offset_height = (new_height - cv2img.shape[0])//2
offset_weight = (new_width - cv2img.shape[1])//2
# should check offset_height >= 0 and offset_weight >= 0
# but we skip here
# ...
# now we just use numpy slicing to copy
new_img[offset_height:offset_height + cv2img.shape[0],
offset_width: offset_width + cv2img.shape[1]] \
= cv2img
return new_img

How to Create New Image From Array List Which Contains Binary Pixel Data

Here I use the PIL Library to read and manipulate images. I am confused, how to create a new image from the list of arrays containing binary pixel data, after being converted to binary images.
I have tried it, but the resulting image is of type RGB, not a binary image. The following is the code that I wrote:
from PIL import Image
import numpy as np
img = Image.open('data_train/ga.jpeg')
pixels = img.load()
width, height = img.size
all_pixels = []
for x in range(width):
for y in range(height):
hpixel = pixels[x, y]
img_gray = (0.2989 * hpixel[0]) + (0.5870 * hpixel[1]) + (0.1140 * hpixel[2])
if img_gray >= 110:
all_pixels.append('1')
else:
all_pixels.append('0')
data_isi = {'0': 0,
'1': 255}
data = [data_isi[letter] for letter in all_pixels]
img_new = Image.fromarray(data)
img_new.save('data_train/gabiner.jpeg')
Updated Answer
As you are required to use a for loop, you could go with something more like this:
#!/usr/bin/env python3
from PIL import Image
# Load image and get dimensions
img = Image.open('start.jpg').convert('RGB')
width, height = img.size
# Actually load input pixels, else PIL is too lazy
imi = img.load()
# List of result pixels
imo = []
for y in range(height):
for x in range(width):
R, G, B = imi[x, y]
gray = (0.2989 * R) + (0.5870 * G) + (0.1140 * B)
if gray >= 110:
imo.append(255)
else:
imo.append(0)
# Make output image and put output pixels into it
result = Image.new('L', (width,height))
result.putdata(imo)
# Save result
result.save('result.png')
Which turns this start image:
Into this result:
Original Answer
You appear to be converting the image to greyscale and thresholding at 110, which can be done much more simply, and faster, like this:
#!/usr/local/bin/python3
from PIL import Image
# Load image and make greyscale
im = Image.open('image.png').convert('L')
# Threshold to make black and white
thr = im.point(lambda p: p > 110 and 255)
# Save result
thr.save('result.png')

Numpy array to PIL image format

I'm trying to convert an image from a numpy array format to a PIL one. This is my code:
img = numpy.array(image)
row,col,ch= np.array(img).shape
mean = 0
# var = 0.1
# sigma = var**0.5
gauss = np.random.normal(mean,1,(row,col,ch))
gauss = gauss.reshape(row,col,ch)
noisy = img + gauss
im = Image.fromarray(noisy)
The input to this method is a PIL image. This method should add Gaussian noise to the image and return it as a PIL image once more.
Any help is greatly appreciated!
In my comments I meant that you do something like this:
import numpy as np
from PIL import Image
img = np.array(image)
mean = 0
# var = 0.1
# sigma = var**0.5
gauss = np.random.normal(mean, 1, img.shape)
# normalize image to range [0,255]
noisy = img + gauss
minv = np.amin(noisy)
maxv = np.amax(noisy)
noisy = (255 * (noisy - minv) / (maxv - minv)).astype(np.uint8)
im = Image.fromarray(noisy)

Find EyeMap of an image using python opencv

I tried coding for EyeMapC but couldn't get expected output.
EyeMapC = 1/3(Cb2 + Cr'2 + Cb/Cr)
where Cr' = 255 - Cr
Here's my code
from __future__ import division
import cv2
import numpy as np
img = cv2.imread('img/file.jpg')
EyeMap = cv2.cvtColor(img,cv2.COLOR_BGR2YCR_CB)
y,Cr,Cb = cv2.split(EyeMap)
Q = np.square(Cb) #,dtype=np.float64)
Cr_bar = (255-Cr)
R = np.square(Cr_bar) #,dtype=np.float64)
G = Cb/Cr
EyeC = (Q/3+R/3+G/3)
#EyeC = np.mod(EyeC,255)
cv2.imshow('EyeC',EyeC)
cv2.waitKey(0)
cv2.destroyAllWindows()
Cb2 and Cr'2 have to be normalized between [0,255] before putting in formula or the final EyeC value needs to be normalized ?
Original Image: Original Image
Output: Output Image

Categories

Resources