Extract L* component from image after converting from RGB to LAB - python

I am trying to take an RGB image, convert it into LAB (aka CIE L* a* b*) colorspace, and extract the L* component.
Here is my code:
from skimage import io, color
from scipy import misc
import matplotlib.pyplot as plt
import cv2
img = misc.imread("/Users/zheyuanlin/Desktop/opencv_tests/parrots.png", mode='RGB')
img_resized = misc.imresize(img, (256, 256), 'bilinear') # resized to 256x256
img_cielab = color.rgb2lab(img_resized, illuminant='D50')
# Rescale due to range of LAB values being L (0-100), a (-128-127), b (-128-127)
cielab_scaled = (img_cielab + [0, 128, 128]) / [100, 255, 255]
cie_l, cie_a, cie_b = cv2.split(cielab_scaled)
""" Display the image """
plt.imshow(cie_l)
plt.show()
This is the image produced:
Here is an example of an L* component of the same image from a research paper I found on google:
I don't know why mine looks so green, does anyone know the problem with my code? Thanks!

The image you display is identical to the one from the paper. But pyplot has a default color map that adds blue, green and yellow to the gray-scale image.
To change the color map used, use the set_cmap function:
plt.set_cmap('gray')

Related

How to create semi transparent pattern with python pil?

I have found some examples on this site. I would like to create, example 6. Can you help?
Create, as a numpy array, the image of the napkin. The squares have a size of 10×10. You may use the command numpy tile. Save the resulting image to a file.
In a standard grayscale image, black pixels are 0, gray pixels are 128, and white ones are 255:
import numpy as np
import matplotlib.pyplot as plt
# first create one 20 x 20 tile
a1 = np.zeros((20,20), dtype=int)
a1[10:20,0:10] = a1[0:10,10:20] = 128
a1[10:20,10:20] = 255
# fill the whole 100 x 100 area with the tiles
a = np.tile(a1, (5,5))
# plot and save
plt.imshow(a, 'Greys_r')
plt.savefig('pattern.png')
You could do this:
from PIL import Image
import numpy as np
# Make grey 2x2 image
TwoByTwo = np.full((2,2), 128, np.uint8)
# Change top-left to black, bottom-right to white
TwoByTwo[0,0] = 0
TwoByTwo[1,1] = 255
# Tile it
tiled = np.tile(TwoByTwo, (5,5))
# Make into PIL Image, rescale in size and save
Image.fromarray(tiled).resize((100,100), Image.NEAREST).save('result.png')

How to Calculate total area of pixels in each class in multi class segmented image

I have a multi class segmented image consisting of labels of 4 different classes represented in 4 different colors ( Darkblue,red,yellow and sky blue ), i would like to calculate the total area of pixels in each class label of segmented prediction.
I tried writing this code for obtaining total number of pixels in each label but i am not able to get any result which consists of total number of pixels in each corresponding class label.
import matplotlib.pyplot as plt
import numpy as np
from skimage import data, io, img_as_ubyte
from skimage.filters import threshold_multiotsu
# Read an image
image = io.imread("images/Ulcer_segmented.jpg")
# Apply multi-Otsu threshold
thresholds = threshold_multiotsu(image, classes=5)
# Digitize (segment) original image into multiple classes.
#np.digitize assign values 0, 1, 2, 3, ... to pixels in each class.
regions = np.digitize(image, bins=thresholds)
output = img_as_ubyte(regions) #Convert 64 bit integer values to uint8
plt.imsave("images/Ulcer_segmented..jpg", output)
props = measure.regionprops_table(label_image, output,
properties=['label',
'area', 'equivalent_diameter',
'mean_intensity', 'solidity'])
This is described in the docs:
from skimage.measure import label, regionprops
# Read an image
image = io.imread("your/image.jpg")
# label image regions
label_image = label(image)
for region in regionprops(label_image):
print(region.area)
Looks like you want to get an image histogram the issue of using np.histogram or skimage.exposure.histogram is that your image is not single-channel and using these functions you would get a histogram of flattened image which would not yield the expected results.
The way you chose to overcome this problem is using otsu thresholding which I'm not sure if works as the documentation states that it expects a single channel (grayscale) image.
The knowledge of the colors used to represent your classes would help here, you could do something like
coors = [
[cls_0_rgb_color],
[cls_1_rgb_color],
[cls_2_rgb_color],
[cls_3_rgb_color]
]
areas = [np.count_nonzero(np.all(img == c, axis=-1)) for c in colors]
If you don't know exactly what colors the classes have you probably have to reduce the last dimension of your image to uniquely represent the 3-dimensional color (I'm not sure exactly how this is done correctly, maybe someone smarter than me can answer this in a new question). What I would do is convert the image to HSV format and use the hue component as a class representation.
from skimage.color import rgb2hsv
hsv = rgb2hsv(image)
hue = hsv[:, :, 0]
areas, bin_edges = np.histogram(hue, bins=4)
What could be tricky here is deciphering which area corresponds to what class but knowing approximately what colors to expect and from knowing how colors in hue space are aligned we could say that the order would be red, yellow, light_blue, dark_blue or yellow, light_blue, dark_blue, red as red hue is symmetrical around 0 or 360 degrees. Checking the bin_edges vector could do the trick here.
# set red_threshold experimentally
if bin_edges[1] < red_threshold:
# (red, yellow, light_blue, dark_blue)
else:
# (yellow, light_blue, dark_blue, red)

Create image mask in Python for DNG and processing

I have a RAW image that is saved as .dng from a phone's camera. I want to segment the colors with the OpenCV library in Python. The picture is primarily black and green and I want to get the values of the green parts of the image. I've not worked with images in this way and am completely clueless. The tutorial I am following says to convert the image to H.S.V. color space and to use a mask, but I'm running into problems with the mask, if not in other steps. I'm using Google Colabs.
import cv2
import matplotlib.pyplot as plt
import numpy as np
import os
from google.colab import drive
import imageio
import scipy.misc
import skimage.filters
import skimage.metrics
from PIL import Image
# Colabs...
!pip install rawpy
import rawpy
# Colabs...
!pip install ExifRead
import exifread
#image
plate = rawpy.imread('/content/drive/MyDrive/Colab Notebooks/Copy of 0724201706a.dng')
#EXIF
plate_x = open('/content/drive/MyDrive/Colab Notebooks/Copy of 0724201706a.dng', 'rb')
#There are several lines returned. I've left this out for now...
plate_tags = exifread.process_file(plate_x)
plate_tags
plt.imshow(plate.raw_image)
plate_rgb = plate.postprocess( use_camera_wb=True)
plt.imshow(plate_rgb)
plate_rgb.shape
(5312, 2988, 3)
These are a slightly edited RGB, the green channel, and blue channel of the RGB image.
Histograms of the values for each channel in R.G.B. image. The other channels are 0, but green has various values.
I supplied all this info to try to describe the RAW image and the R.G.B.
The tutorial says to convert to the H.S.V. color space. I saw somewhere that the image comes in as B.G.R., so I tried two approaches:
plateRGB_hsv = cv2.cvtColor(plate_rgb, cv2.COLOR_RGB2HSV)
plateBGR_hsv = cv2.cvtColor(plate_rgb, cv2.COLOR_BGR2HSV)
# A lower and upper threshold for mask
hsv_green_lo = (59, 100, 135) #h = 50, s = 100, v = 135)
hsv_green_hi = (75, 250, 255) #h = 75, s = 250, v = 255)
plateRGB_hsv.shape
(5312, 2988, 3)
# Create mask
green_thr = cv2.inRange(plateRGB_hsv, hsv_green_lo, hsv_green_hi)
# Apply mask
img_msk = cv2.bitwise_and(plateRGB_hsv, plateRGB_hsv, green_thr)
plt.subplot(1,2,1)
plt.imshow(green_thr)
plt.subplot(1,2,2)
plt.imshow(img_msk)
plt.show()
Output of the inRange (mask layer creation) and bitwise_and (mask application).
rgb_out = cv2.bitwise_and(plate_rgb, plate_rgb, green_thr)
plt.imshow(rgb_out)
plt.plot()
Apply mask and this is output.
So I didn't seem to create the mask properly? And with the bad mask, there was no change when bitwise_and ran it looks like? I don't know why the mask failed. Is the fact that the R.G.B. or H.S.V. is in three channels complicating the mask and mask application?
The image is here.
EDIT after comments and submitted answer:
I was not clear about what I want my output to look like. I said "green", but really I want it to look like this:
I made a new array with just the green channel as advised.
green_c = plate_rgb[...,1]
But now, I'm confused about how to create a mask. Since the array is just one level, I think of it as a "layer", like in G.I.S. or GIMP, how do I change the unwanted values to black? Sorry if this is obvious. I'm still pretty new to Python.
I am not really sure what you think the problem is. Basically, the Red and Blue channels of your image are empty (look at their mean values in the output below) and you may as well discard them and just use the Green channel as your mask.
#!/usr/bin/env python3
import rawpy
import matplotlib.pyplot as plt
import numpy as np
# Load and process raw DNG
plate = rawpy.imread('raw.dng')
rgb = plate.postprocess()
# Show what we've got
print(f'Dimensions: {rgb.shape}, dtype: {rgb.dtype}')
R = rgb[...,0]
print(f'R channel: min={R.min()}, mean={R.mean()}, max={R.max()}')
G = rgb[...,1]
print(f'G channel: min={G.min()}, mean={G.mean()}, max={G.max()}')
B = rgb[...,2]
print(f'B channel: min={B.min()}, mean={B.mean()}, max={B.max()}')
# Display green channel
plt.imshow(G, cmap='gray')
plt.show()
Output for your image
Dimensions: (5312, 2988, 3), dtype: uint8
R channel: min=0, mean=0.013673103558813567, max=255
G channel: min=0, mean=69.00267554908389, max=255
B channel: min=0, mean=0.017269189710649828, max=255
Keywords: Python, image processing, rawpy, DNG, Adobe DNG format.

Low Pass Filter for blurring an image

I'm trying to blur an image using fft by passing a low pass filter that I created but the output yields to be an image full of gray noise. I'm just trying to follow the basics here but it seems like there is something wrong with my implementation:
from scipy import fftpack
import numpy as np
import imageio
from PIL import Image, ImageDraw
image1 = imageio.imread('image.jpg',as_gray=True)
#convert image to numpy array
image1_np=np.array(image)
#fft of image
fft1 = fftpack.fftshift(fftpack.fft2(image1_np))
#Create a low pass filter image
x,y = image1_np.shape[0],image1_np.shape[1]
#size of circle
e_x,e_y=50,50
#create a box
bbox=((x/2)-(e_x/2),(y/2)-(e_y/2),(x/2)+(e_x/2),(y/2)+(e_y/2))
low_pass=Image.new("L",(image1_np.shape[0],image1_np.shape[1]),color=0)
draw1=ImageDraw.Draw(low_pass)
draw1.ellipse(bbox, fill=255)
low_pass_np=np.array(low_pass)
low_pass_fft=fftpack.fftshift(fftpack.fft2(low_pass))
#multiply both the images
filtered=np.multiply(fft1,low_pass_fft)
#inverse fft
ifft2 = abs(fftpack.ifft2(fftpack.ifftshift(filtered)))
#save the image
imageio.imsave('fft-then-ifft.png', ifft2.astype(np .uint8))
As mentioned in comments by Cris Luengo, there are a few things that need to be corrected:
The provided elliptical shape for the low-pass filter makes sense in the frequency-domain, so you shouldn't be computing its FFT.
The filter magnitude of 255 scales the results by the same amount. As you store such large values, the uint8 type wraps around to keep only the 8 least significant bits, resulting in something that looks like noise. This can be fixed by simply changing the value of the filter:
draw1.ellipse(bbox, fill=1)
After readjusting the scaling, there computed filtered may still get slightly out of the desired 0-255 range in some areas of the image. This creates wrap-around spots (black areas in regions surrounded by white pixels, white areas in regions surrounded by black pixels, or even gradient bands where the image goes from white to black to white). To avoid this is common to clip the values to the 0-255 range with the following:
ifft2 = np.real(fftpack.ifft2(fftpack.ifftshift(filtered)))
ifft2 = np.maximum(0, np.minimum(ifft2, 255))
After making these corrections, you should have the following code:
from scipy import fftpack
import numpy as np
import imageio
from PIL import Image, ImageDraw
image1 = imageio.imread('image.jpg',as_gray=True)
#convert image to numpy array
image1_np=np.array(image1)
#fft of image
fft1 = fftpack.fftshift(fftpack.fft2(image1_np))
#Create a low pass filter image
x,y = image1_np.shape[0],image1_np.shape[1]
#size of circle
e_x,e_y=50,50
#create a box
bbox=((x/2)-(e_x/2),(y/2)-(e_y/2),(x/2)+(e_x/2),(y/2)+(e_y/2))
low_pass=Image.new("L",(image1_np.shape[0],image1_np.shape[1]),color=0)
draw1=ImageDraw.Draw(low_pass)
draw1.ellipse(bbox, fill=1)
low_pass_np=np.array(low_pass)
#multiply both the images
filtered=np.multiply(fft1,low_pass_np)
#inverse fft
ifft2 = np.real(fftpack.ifft2(fftpack.ifftshift(filtered)))
ifft2 = np.maximum(0, np.minimum(ifft2, 255))
#save the image
imageio.imsave('fft-then-ifft.png', ifft2.astype(np .uint8))
And the following filtered image:

crop image in skimage?

I'm using skimage to crop a rectangle in a given image, now I have (x1,y1,x2,y2) as the rectangle coordinates, then I had loaded the image
image = skimage.io.imread(filename)
cropped = image(x1,y1,x2,y2)
However this is the wrong way to crop the image, how would I do it in the right way in skimage
This seems a simple syntax error.
Well, in Matlab you can use _'parentheses'_ to extract a pixel or an image region. But in Python, and numpy.ndarray you should use the brackets to slice a region of your image, besides in this code you is using the wrong way to cut a rectangle.
The right way to cut is using the : operator.
Thus,
from skimage import io
image = io.imread(filename)
cropped = image[x1:x2,y1:y2]
One could use skimage.util.crop() function too, as shown in the following code:
import numpy as np
from skimage.io import imread
from skimage.util import crop
import matplotlib.pylab as plt
A = imread('lena.jpg')
# crop_width{sequence, int}: Number of values to remove from the edges of each axis.
# ((before_1, after_1), … (before_N, after_N)) specifies unique crop widths at the
# start and end of each axis. ((before, after),) specifies a fixed start and end
# crop for every axis. (n,) or n for integer n is a shortcut for before = after = n
# for all axes.
B = crop(A, ((50, 100), (50, 50), (0,0)), copy=False)
print(A.shape, B.shape)
# (220, 220, 3) (70, 120, 3)
plt.figure(figsize=(20,10))
plt.subplot(121), plt.imshow(A), plt.axis('off')
plt.subplot(122), plt.imshow(B), plt.axis('off')
plt.show()
with the following output (with original and cropped image):
You can crop image using skimage just by slicing the image array like below:
image = image_name[y1:y2, x1:x2]
Example Code :
from skimage import io
import matplotlib.pyplot as plt
image = io.imread(image_path)
cropped_image = image[y1:y2, x1:x2]
plt.imshow(cropped_image)
you can go ahead with the Image module of the PIL library
from PIL import Image
im = Image.open("image.png")
im = im.crop((0, 50, 777, 686))
im.show()

Categories

Resources