I am trying to make sense of the generic_filter1d() function in scipy.ndimage. My understanding is that this function would allow a function being applied to a 1d array by 'extracting' a subset of this array and applying a function that returns a single value (analogous to the generic_filter()?). Is this correct? I have the following simple code:
def test(x,y):
return np.sum(x/y)
and
import numpy as np
from scipy.ndimage import generic_filter1d
dummy_data= np.arange(10)
size = 5
y= np.linspace(0.1, size*0.1, size)
rstl = generic_filter1d(dummy_data, test, filter_size= size, extra_arguments =(y,))
And it fails. It says that I have an extra argument.
Goal: Avoid File Write/Read Operations
Task: Generate RGBA image as shown in the picture below (img1 from the code)
Issue: Without file write and read operations, Getting Black Image as shown in the picture below (img2 from the code)
import matplotlib.cm as cm
import matplotlib.pyplot as plt
import numpy as np
from PIL import Image
# download file from here: https://drive.google.com/file/d/1R9MEeK-7vUM59An-frFtZv2dtTw-jhs7/view?usp=sharing
bin_mask = np.load("bin_mask.npy") #
# Method1: Does unnecessary file write/read but works
plt.imsave('img1.png', bin_mask, cmap=cm.gray)
img1 = Image.open('img1.png')
# Method 2: No file write but img1 != img2;
# Ref: https://stackoverflow.com/questions/10965417/how-to-convert-a-numpy-array-to-pil-image-applying-matplotlib-colormap
img2 = Image.fromarray(np.uint8(cm.gray(bin_mask)*255))
# unique values of img1: [0, 255]; dtype=uint8
# unique values of img2: [0, 1, 255]; dtype=uint8
print("img1 same as img2: ", img1 == img2) # False
This task seems trivial at first sight but I'm not sure why its behaving this way.
Any suggestions would be appreciated, Thanks in advance.
You have same results from both images when bin_mask value is 0 and different when it is 1.
print('Bin mask={}, img1={}, img2={}'.format(bin_mask[-1][-1] ,np.array(img1)[-1][-1] ,np.array(img2)[-1][-1] ))
# Bin mask=0, img1=[ 0 0 0 255], img2=[ 0 0 0 255]
print('Bin mask={}, img1={}, img2={}'.format(bin_mask[0][0] ,np.array(img1)[0][0] ,np.array(img2)[0][0] ))
# Bin mask=1, img1=[255 255 255 255], img2=[ 1 1 1 255]
Looking further when you call cm.gray(1) gives (0.00392156862745098, 0.00392156862745098, 0.00392156862745098, 1.0) and cm.gray(255) gives (1,1,1,1). So you should be multiplying 255 with bin_mask if you looking for same result.
Following lines will result same content of Img1 and Img2.
img3=Image.fromarray(np.uint8(cm.gray(bin_mask*255)*255))
print(img1 == img3) #result will be false, since this is not correct way to compare data in Image
print(list(img1.getdata()) == list(img3.getdata())) # result is True
However the way you are doing takes too much time when it is compared with Opencv. You can do same thing using OpenCV using following way.
img3 = cv2.cvtColor(np.array(bin_mask.astype(np.uint8) * 255), cv2.COLOR_GRAY2RGBA)
print('Result from Opencv=',np.all(img3 == np.array(img1))) # true
Refer bellow, for full code to understand and time taken by your method vs mine.
import matplotlib.cm as cm
import matplotlib.pyplot as plt
import numpy as np
import cv2
import time
from PIL import Image
# download file from here: https://drive.google.com/file/d/1R9MEeK-7vUM59An-frFtZv2dtTw-jhs7/view?usp=sharing
bin_mask = np.load("/home/jagdish/Downloads/bin_mask.npy") #
# Method1: Does unnecessary file write/read but works
plt.imsave('img1.png', bin_mask, cmap=cm.gray)
img1 = Image.open('img1.png')
# Method 2: No file write but img1 != img2;
# Ref: https://stackoverflow.com/questions/10965417/how-to-convert-a-numpy-array-to-pil-image-applying-matplotlib-colormap
#Your way
img3=Image.fromarray(np.uint8(cm.gray(bin_mask*255)*255))
print('Comparing image class=',img1==img3)
print('Comparing content of Image=',list(img1.getdata()) == list(img3.getdata()))
#OpenCV way
img3 = cv2.cvtColor(np.array(bin_mask.astype(np.uint8) * 255), cv2.COLOR_GRAY2RGBA)
print('Result from Opencv=',np.all(img3 == np.array(img1)))
start_time = time.time()
for i in range(1000):
img3 = Image.fromarray(np.uint8(cm.gray(bin_mask,)*255))
print((time.time()-start_time)*1000)
start_time = time.time()
for i in range(1000):
img3 = cv2.cvtColor(np.array(bin_mask.astype(np.uint8) * 255), cv2.COLOR_GRAY2RGBA)
print((time.time()-start_time)*1000)
Here is time comparison for you.
Using matplotlib to process 1000 images 920 ms
using Opencv to process 1000 images 94 ms
There are a list of image files I want to convert to numpy arrays and append them to a txt file, each array line after line. This is my code:
from PIL import Image
import numpy as np
import os
data = os.listdir("inputs")
print(len(data))
with open('np_arrays.txt', 'a+') as file:
for dt in data:
img = Image.open("inputs\\" + dt)
np_img = np.array(img)
file.write(np_img)
file.write('\n')
but file.write() requires a string and does not accept a numpy ndarray. How can I solve this?
Numpy also allows you to save directly to .txt files with np.savetxt.
I'm still not entirely sure what format you want your text file to be in but a solution might be something like:
from PIL import Image
import numpy as np
import os
data = os.listdir("inputs")
print(len(data))
shape = ( len(data), .., .., ) # input the desired shape
np_imgs = np.empty(shape)
for i, dt in enumerate(data):
img = Image.open("inputs\\" + dt)
np_imgs[i] = np.array(img) # a caveat here is that all images should be of the exact same shape, to fit nicely in a numpy array
np.savetxt('np_arrays.txt', np_imgs)
Note that np.savetxt() has a lot of parameters that allow you to finetune the outputted txt file.
The write() function only allows strings as its input. Try using numpy.array2string.
I'm trying to use matplotlib to read in an RGB image and convert it to grayscale.
In matlab I use this:
img = rgb2gray(imread('image.png'));
In the matplotlib tutorial they don't cover it. They just read in the image
import matplotlib.image as mpimg
img = mpimg.imread('image.png')
and then they slice the array, but that's not the same thing as converting RGB to grayscale from what I understand.
lum_img = img[:,:,0]
I find it hard to believe that numpy or matplotlib doesn't have a built-in function to convert from rgb to gray. Isn't this a common operation in image processing?
I wrote a very simple function that works with the image imported using imread in 5 minutes. It's horribly inefficient, but that's why I was hoping for a professional implementation built-in.
Sebastian has improved my function, but I'm still hoping to find the built-in one.
matlab's (NTSC/PAL) implementation:
import numpy as np
def rgb2gray(rgb):
r, g, b = rgb[:,:,0], rgb[:,:,1], rgb[:,:,2]
gray = 0.2989 * r + 0.5870 * g + 0.1140 * b
return gray
How about doing it with Pillow:
from PIL import Image
img = Image.open('image.png').convert('L')
img.save('greyscale.png')
If an alpha (transparency) channel is present in the input image and should be preserved, use mode LA:
img = Image.open('image.png').convert('LA')
Using matplotlib and the formula
Y' = 0.2989 R + 0.5870 G + 0.1140 B
you could do:
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.image as mpimg
def rgb2gray(rgb):
return np.dot(rgb[...,:3], [0.2989, 0.5870, 0.1140])
img = mpimg.imread('image.png')
gray = rgb2gray(img)
plt.imshow(gray, cmap=plt.get_cmap('gray'), vmin=0, vmax=1)
plt.show()
You can also use scikit-image, which provides some functions to convert an image in ndarray, like rgb2gray.
from skimage import color
from skimage import io
img = color.rgb2gray(io.imread('image.png'))
Notes: The weights used in this conversion are calibrated for contemporary CRT phosphors: Y = 0.2125 R + 0.7154 G + 0.0721 B
Alternatively, you can read image in grayscale by:
from skimage import io
img = io.imread('image.png', as_gray=True)
Three of the suggested methods were tested for speed with 1000 RGBA PNG images (224 x 256 pixels) running with Python 3.5 on Ubuntu 16.04 LTS (Xeon E5 2670 with SSD).
Average run times
pil : 1.037 seconds
scipy: 1.040 seconds
sk : 2.120 seconds
PIL and SciPy gave identical numpy arrays (ranging from 0 to 255). SkImage gives arrays from 0 to 1. In addition the colors are converted slightly different, see the example from the CUB-200 dataset.
SkImage:
PIL :
SciPy :
Original:
Diff :
Code
Performance
run_times = dict(sk=list(), pil=list(), scipy=list())
for t in range(100):
start_time = time.time()
for i in range(1000):
z = random.choice(filenames_png)
img = skimage.color.rgb2gray(skimage.io.imread(z))
run_times['sk'].append(time.time() - start_time)
start_time = time.time()
for i in range(1000):
z = random.choice(filenames_png)
img = np.array(Image.open(z).convert('L'))
run_times['pil'].append(time.time() - start_time)
start_time = time.time()
for i in range(1000):
z = random.choice(filenames_png)
img = scipy.ndimage.imread(z, mode='L')
run_times['scipy'].append(time.time() - start_time)
for k, v in run_times.items():
print('{:5}: {:0.3f} seconds'.format(k, sum(v) / len(v)))
Output
z = 'Cardinal_0007_3025810472.jpg'
img1 = skimage.color.rgb2gray(skimage.io.imread(z)) * 255
IPython.display.display(PIL.Image.fromarray(img1).convert('RGB'))
img2 = np.array(Image.open(z).convert('L'))
IPython.display.display(PIL.Image.fromarray(img2))
img3 = scipy.ndimage.imread(z, mode='L')
IPython.display.display(PIL.Image.fromarray(img3))
Comparison
img_diff = np.ndarray(shape=img1.shape, dtype='float32')
img_diff.fill(128)
img_diff += (img1 - img3)
img_diff -= img_diff.min()
img_diff *= (255/img_diff.max())
IPython.display.display(PIL.Image.fromarray(img_diff).convert('RGB'))
Imports
import skimage.color
import skimage.io
import random
import time
from PIL import Image
import numpy as np
import scipy.ndimage
import IPython.display
Versions
skimage.version
0.13.0
scipy.version
0.19.1
np.version
1.13.1
You can always read the image file as grayscale right from the beginning using imread from OpenCV:
img = cv2.imread('messi5.jpg', 0)
Furthermore, in case you want to read the image as RGB, do some processing and then convert to Gray Scale you could use cvtcolor from OpenCV:
gray_image = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
The fastest and current way is to use Pillow, installed via pip install Pillow.
The code is then:
from PIL import Image
img = Image.open('input_file.jpg').convert('L')
img.save('output_file.jpg')
The tutorial is cheating because it is starting with a greyscale image encoded in RGB, so they are just slicing a single color channel and treating it as greyscale. The basic steps you need to do are to transform from the RGB colorspace to a colorspace that encodes with something approximating the luma/chroma model, such as YUV/YIQ or HSL/HSV, then slice off the luma-like channel and use that as your greyscale image. matplotlib does not appear to provide a mechanism to convert to YUV/YIQ, but it does let you convert to HSV.
Try using matplotlib.colors.rgb_to_hsv(img) then slicing the last value (V) from the array for your grayscale. It's not quite the same as a luma value, but it means you can do it all in matplotlib.
Background:
http://matplotlib.sourceforge.net/api/colors_api.html
http://en.wikipedia.org/wiki/HSL_and_HSV
Alternatively, you could use PIL or the builtin colorsys.rgb_to_yiq() to convert to a colorspace with a true luma value. You could also go all in and roll your own luma-only converter, though that's probably overkill.
Using this formula
Y' = 0.299 R + 0.587 G + 0.114 B
We can do
import imageio
import numpy as np
import matplotlib.pyplot as plt
pic = imageio.imread('(image)')
gray = lambda rgb : np.dot(rgb[... , :3] , [0.299 , 0.587, 0.114])
gray = gray(pic)
plt.imshow(gray, cmap = plt.get_cmap(name = 'gray'))
However, the GIMP converting color to grayscale image software has three algorithms to do the task.
you could do:
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.image as mpimg
def rgb_to_gray(img):
grayImage = np.zeros(img.shape)
R = np.array(img[:, :, 0])
G = np.array(img[:, :, 1])
B = np.array(img[:, :, 2])
R = (R *.299)
G = (G *.587)
B = (B *.114)
Avg = (R+G+B)
grayImage = img.copy()
for i in range(3):
grayImage[:,:,i] = Avg
return grayImage
image = mpimg.imread("your_image.png")
grayImage = rgb_to_gray(image)
plt.imshow(grayImage)
plt.show()
If you're using NumPy/SciPy already you may as well use:
scipy.ndimage.imread(file_name, mode='L')
Use img.Convert(), supports “L”, “RGB” and “CMYK.” mode
import numpy as np
from PIL import Image
img = Image.open("IMG/center_2018_02_03_00_34_32_784.jpg")
img.convert('L')
print np.array(img)
Output:
[[135 123 134 ..., 30 3 14]
[137 130 137 ..., 9 20 13]
[170 177 183 ..., 14 10 250]
...,
[112 99 91 ..., 90 88 80]
[ 95 103 111 ..., 102 85 103]
[112 96 86 ..., 182 148 114]]
With OpenCV its simple:
import cv2
im = cv2.imread("flower.jpg")
# To Grayscale
im = cv2.cvtColor(im, cv2.COLOR_BGR2GRAY)
cv2.imwrite("grayscale.jpg", im)
# To Black & White
im = cv2.threshold(im, 127, 255, cv2.THRESH_BINARY)[1]
cv2.imwrite("black-white.jpg", im)
I came to this question via Google, searching for a way to convert an already loaded image to grayscale.
Here is a way to do it with SciPy:
import scipy.misc
import scipy.ndimage
# Load an example image
# Use scipy.ndimage.imread(file_name, mode='L') if you have your own
img = scipy.misc.face()
# Convert the image
R = img[:, :, 0]
G = img[:, :, 1]
B = img[:, :, 2]
img_gray = R * 299. / 1000 + G * 587. / 1000 + B * 114. / 1000
# Show the image
scipy.misc.imshow(img_gray)
When the values in a pixel across all 3 color channels (RGB) are same then that pixel will always be in grayscale format.
One of a simple & intuitive method to convert a RGB image to Grayscale is by taking the mean of all color channels in each pixel and assigning the value back to that pixel.
import numpy as np
from PIL import Image
img=np.array(Image.open('sample.jpg')) #Input - Color image
gray_img=img.copy()
for clr in range(img.shape[2]):
gray_img[:,:,clr]=img.mean(axis=2) #Take mean of all 3 color channels of each pixel and assign it back to that pixel(in copied image)
#plt.imshow(gray_img) #Result - Grayscale image
Input Image:
Output Image:
image=myCamera.getImage().crop(xx,xx,xx,xx).scale(xx,xx).greyscale()
You can use greyscale() directly for the transformation.
I'd like to segment my image using numpy's label and then based on the number of indices found in each label remove those which satisfy my criteria. For example if an image with regions in it that I'd segmented were created like this and segmented using scipy's label:
from numpy import ones, zeros
from numpy.random import random_integers
from scipy.ndimage import label
image = zeros((512, 512), dtype='int')
regionator = ones((11, 11), dtype='int')
xs = random_integers(5, 506, size=500)
ys = random_integers(5, 506, size=500)
for x, y in zip(xs, ys):
image[x-5:x+6, y-5:y+6] = regionator
labels, n_labels = label(image)
Now I'd like to retrieve the indices for each region which has a size greater than 121 pixels (or one regionator size). I'd then like to take those indices and set them to zero so they are no longer part of the labeled image. What is the most efficient way to accomplish this task?
Essentially something similar to MATLAB's regionprops or utilizing IDL's reverse_indices output from its histogram function.
I would use bincount and threshold the result to make a lookup table:
import numpy as np
threshold = 121
size = np.bincount(labels.ravel())
keep_labels = size <= threshold
# Make sure the background is left as 0/False
keep_labels[0] = 0
filtered_labels = keep_labels[labels]
On the last above I index the array keep_labels with the array labels. This is called advanced indexing in numpy and it requires that labels be an integer array. Numpy then uses the elements of labels as indices to keep_labels and produces an array the same shape as labels.
Here's what I've found to work for me so far with good performance even for large datasets.
Using the get indices process taken from here I've come to this:
from numpy import argsort, histogram, reshape, where
import bisect
h = histogram(labels, bins=n_labels)
h_inds = where(h[0] > 121)[0]
labels_f = labels.flatten()
sortedind = argsort(labels_f)
sorted_labels_f = labels_f[sortedind]
inds = []
for i in range(1, len(h_inds)):
i1 = bisect.bisect_left(sorted_labels_f, h[1][h_inds[i]])
i2 = bisect.bisect_right(sorted_labels_f, h[1][h_inds[i]])
inds.extend(sortedind[i1:i2])
# Now get rid of all of those indices that were part of a label
# larger than 121 pixels
labels_f[inds] = 0
filtered_labels = reshape(labels_f, (512, 512))