How can I achieve PIL colorize functionality? - scikit-image

Using PIL, I can transform an image's color by first converting it to grayscale and then applying the colorize transform. Is there a way to do the same with scikit-image?
The difference with e.g. the question at Color rotation in HSV using scikit-image is that there the black stays black while in PIL colorize function, I can define both where I want black and white mapped to.

I think you want something like this to avoid any dependency on PIL/Pillow:
#!/usr/bin/env python3
import numpy as np
from PIL import Image
def colorize(im,black,white):
"""Do equivalent of PIL's "colorize()" function"""
# Pick up low and high end of the ranges for R, G and B
Rlo, Glo, Blo = black
Rhi, Ghi, Bhi = white
# Make new, empty Red, Green and Blue channels which we'll fill & merge to RGB later
R = np.zeros(im.shape, dtype=np.float)
G = np.zeros(im.shape, dtype=np.float)
B = np.zeros(im.shape, dtype=np.float)
R = im/255 * (Rhi-Rlo) + Rlo
G = im/255 * (Ghi-Glo) + Glo
B = im/255 * (Bhi-Blo) + Blo
return (np.dstack((R,G,B))).astype(np.uint8)
# Create black-white left-right gradient image, 256 pixels wide and 100 pixels tall
grad = np.repeat(np.arange(256,dtype=np.uint8).reshape(1,-1), 100, axis=0)
Image.fromarray(grad).save('start.png')
# Colorize from green to magenta
result = colorize(grad, [0,255,0], [255,0,255])
# Save result - using PIL because I don't know skimage that well
Image.fromarray(result).save('result.png')
That will turn this:
into this:
Note that this is the equivalent of ImageMagick's -level-colors BLACK,WHITE operator which you can do in Terminal like this:
convert input.png -level-colors lime,magenta result.png
That converts this:
into this:
Keywords: Python, PIL, Pillow, image, image processing, colorize, colorise, colourise, colourize, level colors, skimage, scikit-image.

Related

Why is the whole picture turning red?

We’ve been trying to fix this program for hours, yet nothing seems to work, we just can’t figure out what the problem is. It is supposed to make the whole picture black white, besides the red pixels. (https://imgur.com/a/gxRm3N1 should look like that afterwards)
Here is the result after the using the program, the whole picture is turning red:
https://imgur.com/a/yWYVoIx
How can I fix this?
from image_helper import *
img = load_rgb_image("ara.jpg")
w, h = img.size
pixels = load_rgb_pixels("ara.jpg")
#img.show()
for i in range(w*h):
r,g,b = pixels[i]
new_r = 2*r
new_g = g // 2
new_b = b + 10
pixels[i] = (new_r, new_g, new_b)
new_img = new_rgb_image(w, h, pixels)
new_img.show()
There is an excellent solution implemented in MATLAB.
I was tempting to translate the code from MATLAB to Python (using OpenCV).
Convert the image to HSV color space.
Select "non-red" pixels. In HSV color space, the first channel is the hue - there is a range of hue for pixels that considered to be red.
Set the selected pixel saturation channel to 0. (Pixels with zero saturation are gray).
Convert the image back from HSV to BGR color space.
Here is the Python code (conversation of the original MATLAB code):
import cv2
# The following code is a Python conversion to the following MATLAB code:
# Original MATLAB code: https://stackoverflow.com/questions/4063965/how-can-i-convert-an-rgb-image-to-grayscale-but-keep-one-color
img = cv2.imread('roses.jpg') # Load image.
hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV) # Convert the image to HSV color space.
h = hsv[:, :, 0] # Note: in OpenCV hue range is [0,179]) The original MATLAB code is 360.*hsvImage(:, :, 1), when hue range is [0, 1].
s = hsv[:, :, 1] # Get the saturation plane.
non_red_idx = (h > 20//2) & (h < 340//2) # Select "non-red" pixels (divide the original MATLAB values by 2 due to the range differences).
s[non_red_idx] = 0 # Set the selected pixel saturations to 0.
hsv[:, :, 1] = s # Update the saturation plane.
new_img = cv2.cvtColor(hsv, cv2.COLOR_HSV2BGR) # Convert the image back to BGR space
# Show images for testing:
cv2.imshow('img', img)
cv2.imshow('new_img', new_img)
cv2.waitKey()
cv2.destroyAllWindows()
cv2.imwrite('new_img.jpg', new_img)
Result:
Notes:
For the method used for selecting the color range, refer to the original post.
The reference post has a simpler solution, using simple for loop (with inferior results), that more resembles your code.
Consider using this code as reference.

how to convert bayerrg8 format image to rgb image

I've got a camera that provides images in Bayer RG8 format.
I'm using skimage for processing images, but I could not find away to convert the Bayer RG8 format to standard RGB (to display on screen).
Is there any way to do this with skimage?
I did find a reference to opencv conversion, but I'm trying to avoid including opencv in my app (unless it is absolutely necessary).
As you have not provided any input data, I took the greyscale image from here and made it into a raw Bayer8 file with GBRG ordering using ImageMagick as follows:
magick mandi.png -trim -depth 8 gray:bayer.bin
which gives me an 1013x672 pixel file of 680,736 bytes.
Then I read it like this and made it into an image that skimage can understand like this:
#!/usr/bin/env python3
import numpy as np
from skimage.io import imsave
# Width and height of Bayer image, not original which is w/2 x h/2
w, h = 1013, 672
ow, oh = w//2, h//2
# Load in Bayer8 image, assumed raw 8-bit GBRG
bayer = np.fromfile('bayer.bin', dtype=np.uint8).reshape((h,w))
# Pick up raw uint8 samples
R = bayer[1::2, 0::2] # rows 1,3,5,7 columns 0,2,4,6
B = bayer[0::2, 1::2] # rows 0,2,4,6 columns 1,3,5,7
G0 = bayer[0::2, 0::2] # rows 0,2,4,6 columns 0,2,4,6
G1 = bayer[1::2, 1::2] # rows 1,3,5,7 columns 1,3,5,7
# Chop any left-over edges and average the 2 Green values
R = R[:oh,:ow]
B = B[:oh,:ow]
G = G0[:oh,:ow]//2 + G1[:oh,:ow]//2
# Formulate image by stacking R, G and B and save
out = np.dstack((R,G,B))
imsave('result.png',out)
And get this:
Copyright Mathworks, Inc.
Of course, there are more sophisticated methods of interpolating, but this is the most basic and you are welcome to take it and improve it!
Ok, I had some time and I tried to do a 2d-interpolation of the missing values in the Bayer array. I am not 100% confident of my answer, but I think it should be pretty close.
Basically, I copy the original Bayer array at full resolution, and overwrite all green and blue samples with np.Nan and call that Red. Then I do a 2d-interpolation to replace the Nans.
Same again for green and blue, that gives this:
#!/usr/bin/env python3
import numpy as np
from skimage.io import imsave
from scipy.interpolate import griddata
def interp2d(im):
"""Interpolate in 2d array, replacing NaNs with interpolated values"""
x, y = np.indices(im.shape)
im[np.isnan(im)] = griddata(
(x[~np.isnan(im)], y[~np.isnan(im)]),
im[~np.isnan(im)],
(x[np.isnan(im)], y[np.isnan(im)]))
im = np.nan_to_num(im)
return np.clip((im),0,255)
# Width and height of Bayer image
w, h = 1013, 672
# Calculate output width and height as multiples of 4
ow = (w//4) * 4
oh = (h//4) * 4
# Load in Bayer8 image, assumed raw 8-bit GBRG, reshape and make sides multiple of 4
bayer = np.fromfile('bayer.bin', dtype=np.uint8).reshape((h,w)).astype(np.float)[:oh, :ow]
# In following code you'll see "cell" which is the basic repeating 2x2 cell of a Bayer matrix
#
# cell = G B
# R G
#
# Set everything not Red in bayer array to Nan, then replace Nans with interpolation
cell = np.array([[np.NaN, np.NaN],
[1.0 , np.NaN]])
R = bayer*np.tile(cell,(oh//2,ow//2))
R = interp2d(R).astype(np.uint8)
# Set everything not Green in bayer array to Nan, then replace Nans with interpolation
cell = np.array([[1.0 , np.NaN],
[np.NaN, 1.0 ]])
G = bayer*np.tile(cell,(oh//2,ow//2))
G = interp2d(G).astype(np.uint8)
# Set everything not Blue in bayer array to Nan, then replace Nans with interpolation
cell = np.array([[np.NaN, 1.0 ],
[np.NaN, np.NaN]])
B = bayer*np.tile(cell,(oh//2,ow//2))
B = interp2d(B).astype(np.uint8)
# Form image by stacking R, G and B and save
imsave('result.png',np.dstack((R,G,B)))
Keywords: Python, bayer, bayer8, debayer, de-bayer, de-mosaic, de-mosaicking, image, raw, CFA, skimage, scikit-image, image processing.

Remove icon from jpeg image

Is there any way to remove icon from image that in original didn't had the icon.
Maybe with help of hexdump or something?
Here is an example of image.
is there a way to remove the heart icon from it?
*I don't really need this image it is just for example
One method is to use color thresholding to obtain a binary mask which can be used to isolate the desired regions to keep. Once we have this mask, we bitwise-and to effectively remove the heart
After color thresholding with a HSV lower and upper range, we obtain this mask
To remove the heart, we invert the mask which represents all regions in the image that we want to keep then bitwise-and with the input image. Since you didn't specify what you want to replace it with, I've just colored the removed region with white. Here's an implementation using Python and OpenCV
import numpy as np
import cv2
image = cv2.imread('1.jpg')
original = image.copy()
hsv = cv2.cvtColor(image, cv2.COLOR_BGR2HSV)
lower = np.array([0, 138, 155])
upper = np.array([179, 255, 255])
mask = cv2.inRange(hsv, lower, upper)
invert = 255 - mask
result = cv2.bitwise_and(original, original, mask=invert)
result[invert==0] = (255,255,255)
cv2.imshow('mask', mask)
cv2.imshow('result', result)
cv2.waitKey()

Image processing with numpy arrays

In the grayscale mode, 255 indicates white. So if all elements of the numpy matrix are 255, should not it be a white image?
l = np.full((184,184),255)
j = Image.fromarray(l,'L')
j.show()
I am getting a black-and-white vertical striped image as output instead of pure white image. Why is it so?
The issue is the 'L' Mode. L = 8 bit pixels, black and white. The array you created is likely 32 bit values.
Try j = Image.fromarray(l, 'I') ## (32-bit signed integer pixels)
reference.
(note: Many thanks to you for introducing me to the Pillow Image module for Python with this posting...)
Complete test code:
from PIL import Image
import numpy as np
l = np.full((184,184),255)
j = Image.fromarray(m, 'I')
j.show()

Greyscale in python - incorect colors changing from dark grey to light grey to dark grey

I am plotting a greyscale version of this image:
SOURCE: http://matplotlib.org/examples/pylab_examples/griddata_demo.html
I have used the following code:
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.cm as cm
from PIL import Image
file_name = 'griddata_demo.png'
def func_grey(fname):
image = Image.open(fname).convert("L")
arr = np.asarray(image)
plt.imshow(arr, cmap = cm.Greys_r)
plt.show()
func_grey(file_name)
Display image as grayscale using matplotlib
The setup I am working is has python 2.7 and Pandas and I have installed Pillow with easy install.
Background information about the image and the requirements:
The image come from data found here. Ideally, the greyscale
version of this image should be generated directly from this raw
data.i.e. do not save it as a colored image and then try to convert
to greyscale - rather just produce a greyscale version of the plot.
I do not know the colors that correspond to the z-values - these
colors can be set arbitrarily.
The color map of the image can also be chosen arbitrarily - there is no preference. It
is the greyscale version that is of concern.
My question is related to the color scheme shown in the colorbar. I need to display a color scheme where the color bar has colors from light grey (lowest intensity) to dark grey (highest intensity).
After running the above code, a greyscale image is produced. In the color bar of the greyscale image, the intensity level -0.36 is dark grey. At 0.00, it is light grey. But then 0.48 is also dark grey.
Question:
Is is possible to change the colormap such that -0.36 is light grey and 0.48 is dark grey? I mean, is it possible to display to colorbar from light to dark?
I think this question may be about how to use a grayscale colormap in matplotlib. If so, then it's straightforward. Here's an example using different colormaps (based on the code for the op image):
from numpy.random import uniform, seed
from matplotlib.mlab import griddata
import matplotlib.pyplot as plt
import numpy as np
# make up data.
#npts = int(raw_input('enter # of random points to plot:'))
def f(spi, the_colormap):
plt.subplot(spi)
seed(0)
npts = 200
x = uniform(-2, 2, npts)
y = uniform(-2, 2, npts)
z = x*np.exp(-x**2 - y**2)
xi = np.linspace(-2.1, 2.1, 100)
yi = np.linspace(-2.1, 2.1, 200)
zi = griddata(x, y, z, xi, yi, interp='linear')
CS = plt.contour(xi, yi, zi, 15, linewidths=0.5, colors='k')
CS = plt.contourf(xi, yi, zi, 15, cmap=the_colormap,
vmax=abs(zi).max(), vmin=-abs(zi).max())
plt.colorbar() # draw colorbar
# plot data points.
plt.scatter(x, y, marker='o', c='b', s=5, zorder=10)
plt.xlim(-2, 2)
plt.ylim(-2, 2)
plt.title('griddata test (%d points)' % npts)
f(131, plt.cm.rainbow)
f(132, plt.cm.gray)
f(133, plt.cm.hot)
plt.show()
If one actually wants to convert to grayscale using PIL (a far less favorable, but sometimes necessary task), it's best to start with a colormap that has monotonic brightness, like hot above, but not rainbow. Also, in the comments I suggested using cubehelix but that's not standard with matplotlib, instead see here. See here for an image of the available matplotlib colormaps.
this solution works for me, and is a lot simpler
from PIL import Image
im = Image.open("image.png")
im.convert('L').show()
im.convert('L').save("image.png")
note that if you want to mix up the file types, you can (.png to .jpg for example)

Resources