python opencv how to change hue in HSV channels - python

how to achieve img_update(hue_offset) function by changing the values of the hue channel by a dynamic hue_offset. To implement img_update(hue_offset) function, to achieve this Submission:
1.Change the values of the hue channel by a dynamic hue_offset.
import numpy as np
import cv2
import matplotlib.pyplot as plt
from matplotlib.widgets import Slider
def showImage(img, show_window_now = True):
# TODO: Convert the channel order of an image from BGR to RGB
#
# img = str(img)
img2 = cv2.imread(img)
img = cv2.cvtColor(img2, cv2.COLOR_BGR2RGB)
plt_img = plt.imshow(img)
if show_window_now:
plt.show()
return plt_img
# Prepare to show the original image and keep a reference so that we can update the image plot later.
plt.figure(figsize=(4, 6))
img = "hummingbird_from_pixabay.png"
plt_img = showImage(img, False)
# TODO: Convert the original image to HSV color space.
#
img_hsv = cv2.cvtColor(img, cv2.COLOR_RGB2HSV)
def img_update(hue_offset):
print("Set hue offset to " + str(hue_offset))
# TODO: Change the hue channel of the HSV image by `hue_offset`.
# Mind that hue values in OpenCV range from 0-179.
# ???
# TODO: Convert the modified HSV image back to RGB
# and update the image in the plot window using `plt_img.set_data(img_rgb)`.
#
# ???
#
# Create an interactive slider for the hue value offset.
ax_hue = plt.axes([0.1, 0.04, 0.8, 0.06]) # x, y, width, height
slider_hue = Slider(ax=ax_hue, label='Hue', valmin=0, valmax=180, valinit=0, valstep=1)
slider_hue.on_changed(img_update)
# Now actually show the plot window
plt.show()

This is one way to do that in Python/OpenCV.
Input:
import cv2
import numpy as np
# read image
img = cv2.imread("bird.png")
# convert img to hsv
hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
h = hsv[:,:,0]
s = hsv[:,:,1]
v = hsv[:,:,2]
# shift the hue
# cv2 will clip automatically to avoid color wrap-around
huechange = 85 # 0 is no change; 0<=huechange<=180
hnew = cv2.add(h, huechange)
# combine new hue with s and v
hsvnew = cv2.merge([hnew,s,v])
# convert from HSV to BGR
result = cv2.cvtColor(hsvnew, cv2.COLOR_HSV2BGR)
# save result
cv2.imwrite('bird_hue_changed.png', result)
cv2.imshow('result', result)
cv2.waitKey(0)
cv2.destroyAllWindows()
Result for hue shifted by 85:

Related

How do I change the color of the pixels I choose?

I want to make some parts of the picture black.
How can ı change the color of the boxes that i have chosen to black?
My Code:
import cv2
import numpy as np
img = cv2.imread("colors.jpg")
height,width = 720,720
img = cv2.resize(img,(width,height))
hsv = cv2.cvtColor(img,cv2.COLOR_BGR2HSV)
lower_range = np.array([100,50,50])
upper_range = np.array([150,255,255])
kernel = np.ones((5,5),np.uint8)
mask = cv2.inRange(hsv, lower_range, upper_range)
erosion = cv2.erode(mask,kernel,iterations = 1)
res = cv2.bitwise_and(img,img, mask = erosion)
#cv2.imshow("Image",img)
#cv2.imshow("Mask",erosion)
cv2.imshow("res",res)
cv2.waitKey(0)
cv2.destroyAllWindows
Image
Solution
In Python/OpenCV, you can use Numpy to change the color corresponding to the mask pixels.
img[mask>0]=(0,0,0)

Apply a colormap only to unmasked region (excluding the black mask)

I'm still very new to cv2 and python, so please forgive me if this is basic or a duplicate. I've tried searching but to no avail. I have an image, say this penguin, that I have masked using the cv2.inRange() function. I then tried to apply the Viridis colormap to the image, but it applies to everything, including the mask. I want the colormap to only apply to the unmasked region to accentuate the subtle differences in value. In essence, I want the lowest value in the unmasked region to be mapped to Viridis' purple, and the highest value in the unmasked region to be the Viridis' yellow, with the values in between linearly mapped.
Here's my attempt and resulting image. Notice how the masked, previously black region, has been included in the mapping and is now purple. I'm not even sure if it's possible. Maybe a custom colormap is needed?
import cv2
import numpy as np
img = cv2.imread('penguin.jpg') #Read in image
img = cv2.cvtColor(img, cv2.COLOR_BGR2HSV) #Change to HSV
# define range of white color in HSV
lower_white = np.array([0,0,210])
upper_white = np.array([255,255,255])
mask = cv2.inRange(img, lower_white, upper_white) # Create the mask
bitwise_mask = cv2.bitwise_and(img,img,mask=mask) # Apply mask
masked_core = cv2.cvtColor(bitwise_mask, cv2.COLOR_HSV2BGR) #Change to BGR
#But how do I get it to apply only to unmasked area?
not_desired = cv2.applyColorMap(masked_core, cv2.COLORMAP_VIRIDIS) #Viridis Colormap
#Display and write image
cv2.imwrite('penguin_masked.jpg', not_desired)
cv2.imshow('penguin', not_desired)
cv2.waitKey()
In response to your comment, perhaps this is what you want in Python/OpenCV. My answer is nearly the same except how I compute the grayscale image to be color mapped. I convert your low and high colors to intensity values. Then convert the color image to intensity grayscale. Then stretch your [0,0,210] equivalent intensity to black and your [255,255,255] equivalent intensity (255) to white with all values stretched linearly in between.
Input:
import cv2
import numpy as np
from skimage.exposure import rescale_intensity
img = cv2.imread('penguin.jpg') #Read in image
hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV) #Change to HSV
# define range of white color in HSV
lower_white = np.array([0,0,210])
upper_white = np.array([255,255,255])
mask = cv2.inRange(hsv, lower_white, upper_white) # Create the mask
# Y = 0.299 R + 0.587 G + 0.114 B
# convert [0,0,210] to intensity = 0.299*210 = 63
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) #Change to gray (intensity)
gray = rescale_intensity(gray, in_range=(63,255), out_range=(0,255)).astype(np.uint8) # linearly stretch 63 to black and 255 to white
colormap = cv2.applyColorMap(gray, cv2.COLORMAP_VIRIDIS) #Viridis Colormap to gray image
img_masked = cv2.bitwise_and(img, img, mask=mask) # Apply mask to img
colormap_masked = cv2.bitwise_and(colormap, colormap, mask=(255-mask)) # Apply inverse mask to colormapped
result = cv2.add(img_masked, colormap_masked) # Merge original and colormapped using mask
#Display and write image
cv2.imwrite('penguin_mask.jpg', mask)
cv2.imwrite('penguin_colormapped2.jpg', colormap)
cv2.imwrite('penguin_result.jpg', result)
cv2.imshow('mask', mask)
cv2.imshow('colormapped2', colormap)
cv2.imshow('result', result)
cv2.waitKey()
Mask:
Color Mapped Image:
Result:
Here is one way to do that in Python/OpenCV.
Read the image
Convert to HSV
Threshold the HSV using inRange() to make a mask
Convert the image to grayscale
Apply the colormap to the grayscale image
Apply the mask to the input image
Apply the inverse map to the colormapped image
Add the two masked images together to for the result
Save the result
Input:
import cv2
import numpy as np
img = cv2.imread('penguin.jpg') #Read in image
hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV) #Change to HSV
# define range of white color in HSV
lower_white = np.array([0,0,210])
upper_white = np.array([255,255,255])
mask = cv2.inRange(hsv, lower_white, upper_white) # Create the mask
print(mask.shape, np.amax(mask))
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) #Change img to GRAY
colormap = cv2.applyColorMap(gray, cv2.COLORMAP_VIRIDIS) #Viridis Colormap to gray image
img_masked = cv2.bitwise_and(img, img, mask=mask) # Apply mask to img
colormap_masked = cv2.bitwise_and(colormap, colormap, mask=(255-mask)) # Apply inverse mask to colormapped
result = cv2.add(img_masked, colormap_masked) # Merge original and colormapped using mask
#Display and write image
cv2.imwrite('penguin_mask.jpg', mask)
cv2.imwrite('penguin_colormapped.jpg', colormap)
cv2.imwrite('penguin_result.jpg', result)
cv2.imshow('mask', mask)
cv2.imshow('colormapped', colormap)
cv2.imshow('result', result)
cv2.waitKey()
Mask Image:
Colormapped Image:
Result:
NOTE: If I have misunderstood and you want the penguin color mapped, then just swap the mask and the inverse mask in the bitwise_and operations.

how to remove skull in brain MRI by using watershed

I'm working on detecting the brain tumor by using python. I need some help for the segmentation process in my code. I want to remove the skull part in the brain MRI. I'm using the watershed algorithm to define the skull in the MRI images. however, the tumor region is also shaded by the code. how can I change the threshold detected by the code in order to keep the whole brain region untouched by the algorithm. the picture shown below is a sample of what i want to get. ` import numpy as np
import cv2
from matplotlib import pyplot as plt
from skimage.morphology import extrema
from skimage.morphology import watershed as skwater
def ShowImage(title,img,ctype):
plt.figure(figsize=(10, 10))
if ctype=='bgr':
b,g,r = cv2.split(img) # get b,g,r
rgb_img = cv2.merge([r,g,b]) # switch it to rgb
plt.imshow(rgb_img)
elif ctype=='hsv':
rgb = cv2.cvtColor(img,cv2.COLOR_HSV2RGB)
plt.imshow(rgb)
elif ctype=='gray':
plt.imshow(img,cmap='gray')
elif ctype=='rgb':
plt.imshow(img)
else:
raise Exception("Unknown colour type")
plt.axis('off')
plt.title(title)
plt.show()
img = cv2.imread('s1.png')
gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
ShowImage('Brain MRI',gray,'gray')
ret, thresh = cv2.threshold(gray,0,255,cv2.THRESH_OTSU)
ShowImage('Thresholding image',thresh,'gray')
ret, markers = cv2.connectedComponents(thresh)
#Get the area taken by each component. Ignore label 0 since this is the background.
marker_area = [np.sum(markers==m) for m in range(np.max(markers)) if m!=0]
#Get label of largest component by area
largest_component = np.argmax(marker_area)+1 #Add 1 since we dropped zero above
#Get pixels which correspond to the brain
brain_mask = markers==largest_component
brain_out = img.copy()
#In a copy of the original image, clear those pixels that don't correspond to the brain
brain_out[brain_mask==False] = (0,0,0)
img = cv2.imread('s1.png')
gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
ret, thresh = cv2.threshold(gray,0,255,cv2.THRESH_BINARY_INV+cv2.THRESH_OTSU)
# noise removal
kernel = np.ones((3,3),np.uint8)
opening = cv2.morphologyEx(thresh,cv2.MORPH_OPEN,kernel, iterations = 2)
# sure background area
sure_bg = cv2.dilate(opening,kernel,iterations=3)
# Finding sure foreground area
dist_transform = cv2.distanceTransform(opening,cv2.DIST_L2,5)
ret, sure_fg = cv2.threshold(dist_transform,0.7*dist_transform.max(),255,0)
# Finding unknown region
sure_fg = np.uint8(sure_fg)
unknown = cv2.subtract(sure_bg,sure_fg)
# Marker labelling
ret, markers = cv2.connectedComponents(sure_fg)
# Add one to all labels so that sure background is not 0, but 1
markers = markers+1
# Now, mark the region of unknown with zero
markers[unknown==255] = 0
markers = cv2.watershed(img,markers)
img[markers == -1] = [255,0,0]
im1 = cv2.cvtColor(img,cv2.COLOR_HSV2RGB)
ShowImage('Watershed segmented image',im1,'gray')
brain_mask = np.uint8(brain_mask)
kernel = np.ones((8,8),np.uint8)
closing = cv2.morphologyEx(brain_mask, cv2.MORPH_CLOSE, kernel)
ShowImage('Closing', closing, 'gray')
brain_out = img.copy()
#In a copy of the original image, clear those pixels that don't correspond to the brain
brain_out[closing==False] = (0,0,0)`
the picture shown below is a sample of what i want to get.this is what i get when running the code this is the original image

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

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.

Python + OpenCV color segmentation using Kmeans

I am trying to apply the kmeans from opencv in order to segment the image in HSV color space.
def leftOffset(src, p_countours):
height, width, size = src.shape
p_width = width/p_countours
o_left = src[0:height, 0:p_width]
HSV_img = cv2.cvtColor(o_left, cv2.COLOR_BGR2HSV)
hue = HSV_img[0]
hue = np.float32(HSV_img)
# Define criteria = ( type, max_iter = 10 , epsilon = 1.0 )
criteria = (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 10, 1.0)
# Set flags (Just to avoid line break in the code)
flags = cv2.KMEANS_RANDOM_CENTERS
# Apply KMeans
compactness,labels,centers = cv2.kmeans(hue,2,criteria,10,flags)
centers = np.uint8(centers)
res = centers[labels.flatten()]
res2 = res.reshape((hue.shape))
cv2.imshow("o_left", hue)
cv2.waitKey(0)
I am now able to apply the kmeans algorithm to the HSVImage[0] with K=2, and how can I get a image like threshold according to the result?
Thanks
To clarify the question:
I have color-based captchas, and I want to segment each digits.
The image is like
I am going to use k-means method to find out the dominant color and segment the digits inside.
1) If all you need is to find the dominant color why not find the histograms of each color channel? Find the dominant channel then segment only that channel using otsu? For example if I threshold only the hue I can get nice results. K-means could be an overkill for this task:
import cv2
import numpy as np
import matplotlib.pylab as plt
## Simple Otsu over hue
six = cv2.imread('7zovC.jpg')
##convert to hsv
hsv = cv2.cvtColor(six, cv2.COLOR_BGR2HSV)
hue = hsv[:, :, 0]
binary_img = cv2.threshold(hue, 128, 255, cv2.THRESH_OTSU)
plt.figure()
plt.imshow(binary_img*255)
plt.show()
2) Why not use all channels for clustering instead of just hue? What you need is clustering -> color quantization this link should be useful. This is for opencv version > 3.0.0
Note for python 2.4.11, cv2.kmeans has a slightly difference interface and you could use this instead:
def color_quantize(img, K):
Z = img.reshape((-1, 3))
# convert to np.float32
Z = np.float32(Z)
# define criteria, number of clusters(K) and apply kmeans()
criteria = (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 10, 1.0)
ret, label, center = cv2.kmeans(Z, 2, criteria, 10, cv2.KMEANS_RANDOM_CENTERS)
# Now convert back into uint8, and make original image
center = np.uint8(center)
res = center[label.flatten()]
quantized_img = res.reshape((img.shape))
label_img = label.reshape((img.shape[:2]))
return label_img, quantized_img
six = cv2.imread('7zovC.jpg')
##convert to hsv
hsv = cv2.cvtColor(six, cv2.COLOR_BGR2HSV)
K = 2
label_img, six_q = color_quantize(hsv, K)
plt.figure()
plt.imshow(label_img)
plt.show()
My results for color quantization were not impressive.
May I suggest a conventional alternative? I you get rid of the very dark and bright regions first, you may be able to simply rely on the most frequent value of the hue component calculated from the histogram.
Mind that the borders of the numbers will never be absolutely exact, since colours are similar in the surrounding.
Furthrmore, you could select the maximum blob only (according to size) to suppress remaining small blobs outside.
Results:
Code:
import cv2
import numpy as np
from matplotlib import pyplot as plt
img = cv2.imread('image1.jpg')
#get rid of very bright and very dark regions
delta=30
lower_gray = np.array([delta, delta,delta])
upper_gray = np.array([255-delta,255-delta,255-delta])
# Threshold the image to get only selected
mask = cv2.inRange(img, lower_gray, upper_gray)
# Bitwise-AND mask and original image
res = cv2.bitwise_and(img,img, mask= mask)
#Convert to HSV space
HSV_img = cv2.cvtColor(res, cv2.COLOR_BGR2HSV)
hue = HSV_img[:, :, 0]
#select maximum value of H component from histogram
hist = cv2.calcHist([hue],[0],None,[256],[0,256])
hist= hist[1:, :] #suppress black value
elem = np.argmax(hist)
print np.max(hist), np.argmax(hist)
tolerance=10
lower_gray = np.array([elem-tolerance, 0,0])
upper_gray = np.array([elem+tolerance,255,255])
# Threshold the image to get only selected
mask = cv2.inRange(HSV_img, lower_gray, upper_gray)
# Bitwise-AND mask and original image
res2 = cv2.bitwise_and(img,img, mask= mask)
titles = ['Original Image', 'Selected Gray Values', 'Hue', 'Result']
images = [img, res, hue, res2]
for i in xrange(4):
plt.subplot(2,2,i+1),plt.imshow(images[i],'gray')
plt.title(titles[i])
plt.xticks([]),plt.yticks([])
plt.show()

Categories