Why my image is different being plotted in Opencv-Python? [duplicate] - python

This question already has answers here:
OpenCV giving wrong color to colored images on loading
(7 answers)
Closed 5 years ago.
I am trying to take an image and convert it to grayscale, adding some gaussian blur to that image, and detecting the edges. I am having trouble displaying the image with matplotlib's pyplot.
import cv2
import matplotlib.pyplot as plt
def read_image_and_print_dims(image_path):
"""Reads and returns image.
Helper function to examine ow an image is represented"""
#reading an image
image=cv2.imread(image_path)
#printing out some stats and plottin
print('This image is ',type(image),' with dinmesions',image.shape)
plt.subplot(2,2,3)
plt.imshow(image)
return image
image_path='fall-leaves.png'
img=read_image_and_print_dims(image_path)
#Make a blurred/smoothed version
def gaussian_blur(img,kernel_size):
"""Applies a Gaussian Noise Kernel"""
print ('Inside Gaussian')
return cv2.GaussianBlur(img,(kernel_size,kernel_size),4)
#Gray Scale Image
def grayscale(img):
"""Applies the Grayscale transform
This will return an image with only one color channel
but NOTE: to see the returned image as grayscale
you should call plimshow(gray, cmap='gray')"""
print ('Inside gray sale')
return cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# gray scale it
greyscaled_image = grayscale(img)
plt.subplot(2, 2, 1)
plt.imshow(greyscaled_image, cmap='gray')
# smooth it a bit with Gaussian blur
kernal_size = 11
blur_gray = gaussian_blur(img, kernal_size)
plt.subplot(2, 2, 2)
plt.imshow(blur_gray)
cv2.waitKey(0)
cv2.destroyAllWindows()
While running above code in Pycharm it generates following message:
('This image is ', <type 'numpy.ndarray'>, ' with dinmesions', (320L, 400L, 3L))
Inside gray sale
Inside Gaussian
But it doesn't plot the image.
EDIT
I got it to display using plt.show. However, now I'm having a different problem. I obtained this figure from pyplot, but using cv2.imshow, I got these: top two images, bottom two images
This is my code for plt.show:
#REad Image
import numpy as np
import cv2
import matplotlib.pyplot as plt
def read_image_and_print_dims(image_path):
"""Reads and returns image.
Helper function to examine ow an image is represented"""
#reading an image
image=cv2.imread(image_path)
#printing out some stats and plottin
print('This image is ',type(image),' with dinmesions',image.shape)
plt.subplot(2,2,1)
#cv2.imshow('Original Image',image)
plt.imshow(image)
return image
image_path='fall-leaves.png'
img=read_image_and_print_dims(image_path)
#Make a blurred/smoothed version
def gaussian_blur(img,kernel_size):
"""Applies a Gaussian Noise Kernel"""
print ('Inside Gaussian')
return cv2.GaussianBlur(img,(kernel_size,kernel_size),4)
#Gray Scale Image
def grayscale(img):
"""Applies the Grayscale transform
This will return an image with only one color channel
but NOTE: to see the returned image as grayscale
you should call plimshow(gray, cmap='gray')"""
print ('Inside gray sale')
gray_image=cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
return gray_image
def canny(img,low_threshold,high_threshold):
"""Applies the Canny Transform"""
return cv2.Canny(img,low_threshold,high_threshold)
# gray scale it
greyscaled_image = grayscale(img)
plt.subplot(2, 2, 2)
plt.imshow(greyscaled_image)
#cv2.imshow('grey scale',greyscaled_image)
# smooth it a bit with Gaussian blur
kernal_size = 11
blur_gray = gaussian_blur(img, kernal_size)
plt.subplot(2, 2, 3)
plt.imshow(blur_gray)
#cv2.imshow('gaussian ',blur_gray)
#Canny image detection
edges_image=canny(blur_gray,50,150)
plt.subplot(2, 2, 4)
plt.imshow(edges_image)
plt.show()
#cv2.imshow('Canny image detection',edges_image)
#
# cv2.waitKey(0)
# cv2.destroyAllWindows()
And this is my code for using cv2.imshow:
#REad Image
import numpy as np
import cv2
import matplotlib.pyplot as plt
def read_image_and_print_dims(image_path):
"""Reads and returns image.
Helper function to examine ow an image is represented"""
#reading an image
image=cv2.imread(image_path)
#printing out some stats and plottin
print('This image is ',type(image),' with dinmesions',image.shape)
#plt.subplot(2,2,3)
cv2.imshow('Original Image',image)
return image
image_path='fall-leaves.png'
img=read_image_and_print_dims(image_path)
#Make a blurred/smoothed version
def gaussian_blur(img,kernel_size):
"""Applies a Gaussian Noise Kernel"""
print ('Inside Gaussian')
return cv2.GaussianBlur(img,(kernel_size,kernel_size),4)
#Gray Scale Image
def grayscale(img):
"""Applies the Grayscale transform
This will return an image with only one color channel
but NOTE: to see the returned image as grayscale
you should call plimshow(gray, cmap='gray')"""
print ('Inside gray sale')
gray_image=cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
return gray_image
def canny(img,low_threshold,high_threshold):
"""Applies the Canny Transform"""
return cv2.Canny(img,low_threshold,high_threshold)
# gray scale it
greyscaled_image = grayscale(img)
#plt.subplot(2, 2, 1)
cv2.imshow('grey scale',greyscaled_image)
# smooth it a bit with Gaussian blur
kernal_size = 11
blur_gray = gaussian_blur(img, kernal_size)
#plt.subplot(2, 2, 2)
cv2.imshow('gaussian ',blur_gray)
#Canny image detection
edges_image=canny(blur_gray,50,150)
cv2.imshow('Canny image detection',edges_image)
cv2.waitKey(0)
cv2.destroyAllWindows()
Different images are obtained using pyplot and cv2. Shouldn't the same image be obtained?

You should use plt.show() to get the plot to display after you create the subplots.
Matplotlib assumes RGB order while OpenCV uses BGR ordering. To get the Matplotlib images the correct color, you need to swap the first and last channel around. You can use the built-in OpenCV method rgb_img = cv2.cvtColor(bgr_img, cv2.COLOR_BGR2RGB) to change them before you display them.
Also the images on the right in plt.imshow() are not using a gray colormap even though they are gray images. You need to use plt.imshow(blur_gray, cmap='gray') and plt.imshow(edges_image, cmap='gray') to use the grayscale colormap. cv2.imshow() always displays grayscale when there is only one channel. Your top set of code use the correct colormaps.

Related

How to visualize an image array with 2 color channels using openCV?

I have some image arrays with a shape of (128,256,2) which have two color channels. I am trying to open the image array using the openCV's imshow but I am continuously getting the following error: >
Invalid number of channels in input image:
> 'VScn::contains(scn)'
> where
> 'scn' is 2
I understand openCV can't open image array with two color space. Is there any way to open the image with the two color channels or to compute element-wise average and convert it an array with one color channel? Kindly help please!
> cv2.imshow("Window", img)
Here is one way that demonstrates adding a black channel to two channels of an image in Python/OpenCV.
Input:
import cv2
import numpy as np
# read a 3 channel image
img = cv2.imread('lena.png')
h, w = img.shape[:2]
# separate channels
b,g,r = cv2.split(img)
# create black image the same size as image
black = np.zeros((h,w), dtype=np.uint8)
# replace the red channel with black
result = cv2.merge([b,g,black])
# save results
cv2.imwrite('lena_black.png', result)
# show results
cv2.imshow('result', result)
cv2.waitKey(0)
Result:
If you already have a 2 channel image, then simply create the black image and use np.dstack([2-channel-image, black]) to make a 3 channel image.
OpenCV does not recognise images with only 2 channels, the solution is to add a black channel.
OpenCV images are represented as numpy arrays:
import cv2
img = cv2.imread(PATH_TO_IMG)
# This returns <class 'numpy.ndarray'>.
type(img)
Now we can use numpy functions to add the 3rd channel (black):
import cv2
import numpy as np
H, W, _ = img.shape
img = np.append(img, np.zeros((H, W, 1)), axis=-1)
cv2.imshow(window_name, img)
cv2.waitKey()
Credits to #fmw42.
Or, you could take the element-wise average across channels, and display it as a grayscale image:
import cv2
import numpy as np
img = np.average(img, axis=-1)
cv2.imshow(window_name, img)
cv2.waitKey()

How to improve the binarization of Text document ( Fill missing pixels of alphabets, reduce noise etc)

What (and how) can be done (like where exactly to plcae Erosion, Dialiation, Opening, Closing etc) so that the words are not cut / invisible in between (maybe some other binarization technique which works on most of the cases or some parameter tuning)
I have this code to binarize / Threshold the image. It works fine on a wide range of images except a couple of things and the major problem being that it leads to loss of info due to some brightness and other factors. Some of the words are not readable or gets broken. Below is the code to threshold / Binarize along with some of the images. Also, here is the link to 200 sample and resulting images
import cv2
import skimage.filters as filters
def convert(path):
img = cv2.imread(path)
gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
smooth = cv2.GaussianBlur(gray, (103,103), 0)
division = cv2.divide(gray, smooth, scale=255)
sharp = filters.unsharp_mask(division, radius=1.5, amount=5, multichannel=False, preserve_range=False) # High Radius increase the density or surrounding pixels making it dense
sharp = (255*sharp).clip(0,255).astype(np.uint8)
thresh = cv2.threshold(sharp, 0, 255, cv2.THRESH_OTSU )[1]
return thresh
You can test several types of histogram equalization techniques. I've scripted down two examples of histogram equalization with your above photo. You can than later keep preprocessing those raw results for better outcomes depending on your data variance.
import cv2
import matplotlib.pyplot as plt
# read a image using imread
img = cv2.imread("test.png")
img_gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
# creating a Histograms Equalization
equ = cv2.equalizeHist(img_gray)
# clahe
clahe = cv2.createCLAHE(clipLimit=10.0, tileGridSize=(100,100))
cl = clahe.apply(img_gray)
# show image input vs output
plt.figure(figsize=(10,10))
plt.imshow(img)
plt.title("original image")
plt.show()
plt.figure(figsize=(10,10))
plt.imshow(equ, cmap='gray', vmin=0, vmax=255)
plt.title("histogram equalization")
plt.show()
plt.figure(figsize=(10,10))
plt.imshow(cl, cmap='gray', vmin=0, vmax=255)
plt.title("CLAHE")
plt.show()
The results are:

How do I crop an image using a binary mask image of the same picture to remove the background in python?

I have tried to get the edge of the mask image with the following code:
import numpy as np
from matplotlib import pyplot as plt
img = cv2.imread('ISIC_0000000_segmentation.png',0)
edges = cv2.Canny(img,0,255)
plt.subplot(121), plt.imshow(img, cmap='gray')
plt.title('Original Image'), plt.xticks([]), plt.yticks([])
plt.subplot(122), plt.imshow(edges, cmap='gray')
plt.title('Edge Image'), plt.xticks([]), plt.yticks([])
plt.show
What I get is this:
But the edge isn't smooth for some reason.
My plan was to use the edge image to crop the following picture:
Does anyone know how I could make the edge image better and how I could use this to crop the normal image?
EDIT: #Mark Setchell made a good point: If i could use the mask image directly to crop the image that would be great.
Also: It is maybe possible to lay the normal image precisely on the mask image so that the black area on the mask would cover the blue-ish area on the normal picture.
EDIT: #Mark Setchell introduced the idea of multiplying the normale image with the mask image so what the background would result in 0(black) and the rest would keep its color. Would it be a problem when my mask image is .png and my normal picture is .jpg when multiplying?
EDIT:
I have written the following code to try to multiply two pictures:
# Importing Image and ImageChops module from PIL package
from PIL import Image, ImageChops
# creating a image1 object
im1 = Image.open("ISIC_0000000.jpg")
# creating a image2 object
im2 = Image.open("ISIC_0000000_segmentation.png")
# applying multiply method
im3 = ImageChops.multiply(im1, im2)
im3.show()
But I get the error:
ValueError: images do not match
Does anyone know how I could solve this?
If I understand correctly, you want to extract the object and remove the background. To do this, you can just do a simple cv2.bitwise_and() with the mask and the original input image.
Does anyone know how I could make the edge image better and how I could use this to crop the normal image?
To extract the background from the image, you don't need an edge image, the thresholded image can be used to remove only the desired parts of the image. You can use the mask image to directly drop the image and remove the background. Other approaches of obtaining a binary mask include using a fixed threshold value, adaptive threshold, or Canny edge detection. Here's a simple example using Otsu's threshold to obtain a binary mask followed by a bitwise-and operation.
Here's the result with the removed background
You can also turn all pixels on the mask to white if you wanted the removed background to be white
Note: Depending on how "smooth" you want the result, you can apply any blur to the image before thresholding to smooth out the edges. This can include averaging, Gaussian, median, or bilaterial filtering.
Code
import cv2
# Load image, grayscale, Otsu's threshold
image = cv2.imread('1.jpg')
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
thresh = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY_INV + cv2.THRESH_OTSU)[1]
# Remove background using bitwise-and operation
result = cv2.bitwise_and(image, image, mask=thresh)
result[thresh==0] = [255,255,255] # Turn background white
cv2.imshow('thresh', thresh)
cv2.imshow('result', result)
cv2.waitKey()
The detected edge isn't smooth because the actual edge in the image isn't smooth. You can try filtering the original image first with low-pass filters.
If you can use contours, the following will work:
import numpy as np
import cv2
from matplotlib import pyplot as plt
# Read in image
imgRaw = cv2.imread('./Misc/edgesImg.jpg',0)
# Blur image
blurSize = 25
blurredImg = cv2.blur(imgRaw,(blurSize,blurSize))
# Convert to Binary
thrImgRaw, binImgRaw = cv2.threshold(imgRaw, 0, 255, cv2.THRESH_OTSU)
thrImgBlur, binImgBlur = cv2.threshold(blurredImg, 0, 255, cv2.THRESH_OTSU)
# Detect the contours in the image
contoursRaw = cv2.findContours(binImgRaw,cv2.RETR_TREE,cv2.CHAIN_APPROX_NONE)
contoursBlur = cv2.findContours(binImgBlur,cv2.RETR_TREE,cv2.CHAIN_APPROX_NONE)
# Draw all the contours
contourImgOverRaw = cv2.drawContours(imgRaw, contoursRaw[0], -1, (0,255,0),5)
contourImgOverBlur = cv2.drawContours(blurredImg, contoursBlur[0], -1, (0,255,0),5)
# Plotting
plt.figure()
plt.subplot(121)
plt.imshow(contourImgOverRaw)
plt.title('Raw Edges'), plt.xticks([]), plt.yticks([])
plt.subplot(122)
plt.imshow(contourImgOverBlur)
plt.title('Edges with {}px Blur'.format(blurSize)), plt.xticks([]), plt.yticks([])
plt.show()
EDIT: Here's more info on getting a mask of an image from contours.
You can your morphological operations to get the edge.
Sorry for using MATLAB:
I = imbinarize(rgb2gray(imread('I.png'))); %Load input image, and convert to binary image.
%Erode the image with mask 3x3
J = imerode(I, ones(3));
%Pefrom XOR operation (1 xor 1 = 0, 0 xor 0 = 0, 0 xor 1 = 1, 1 xor 0 = 1)
J = xor(I, J);
%Use "skeleton" operation to make sure eage thikness is 1 pixel.
K = bwskel(J);
Result:
As Mark mentioned, you don't need the edges for cropping (unless your are using special cropping method that I am not aware of).

Detect a scratch on noise image with OpenCV

I am trying to get a small scratch from the noise image as shown. It is quite noticeable by eyes, but I would like to identify it using OpenCV Python.
I tried to use image blurring and subtract it from the original image, and then threshold to get a image as the second.
Could any body please advise to get this scratch?
Original image:
Image after blurring, subtraction, and threshold:
This is how I process this image:
import cv2
import numpy as np
from matplotlib import pyplot as plt
img = cv2.imread("scratch0.jpg")
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
blur = cv2.blur(gray,(71,71))
diff = cv2.subtract(blur, gray)
ret, th = cv2.threshold(diff, 13, 255, cv2.THRESH_BINARY_INV)
cv2.imshow("threshold", th)
cv2.waitKey(0)

Apply colored overlay over a colored rgb image with some transparency in Skimage

So basically I have a colored RGB image and I want to add a colored overlay over the RGB image without converting it to gray level.
For example if I have a colored image(RGB). And I want to add a transparent blue color over the index like this
img[200:350, 200:350] = [0, 0, 1] # Blue block
This question is a sibling question to this one:
Applying a coloured overlay to an image in either PIL or Imagemagik
Difference is the color space. The above question is for gray level images rather colored (RGB).
from skimage import io, data
import numpy as np
img = data.astronaut()
Please use the above code to answer.
Here is the code in OpenCV:
import cv2
# load the image
image = cv2.imread("2.jpg")
# I resized the images because they were to big
image = cv2.resize(image, (0,0), fx=0.75, fy=0.75)
overlay = image.copy()
output = image.copy()
#select the region that has to be overlaid
cv2.rectangle(overlay, (420, 205), (595, 385),(0, 255, 255), -1)
#Adding the transparency parameter
alpha = 1
#Performing image overlay
cv2.addWeighted(overlay, alpha, output, 1 - alpha,0, output)
#Save the overlaid image
cv2.imwrite('Output'+str(alpha) +'.jpg', output)
cv2.waitKey(0)
cv2.destroyAllWindows()
Some results:
when alpha = 0.1
when alpha = 0.5
when alpha = 0.8
when alpha = 1.0 (the overlay is no longer transparent but opaque)

Categories