Sample Image:
Here's my code
import cv2
import numpy as np
image = cv2.imread("1.bmp")
img_grey = cv2.cvtColor(image,cv2.COLOR_BGR2GRAY)
ret, thresh1 = cv2.threshold(img_grey,120,255,cv2.THRESH_BINARY)
ret, thresh = cv2.threshold(img_grey,120,255,cv2.THRESH_BINARY_INV)
cv2.imshow("image 1", thresh1)
cv2.imshow("image 2", thresh)
cv2.waitKey(0)
Any idea to segment the grey region ? I thought some sort of subtracting the white and black region using raw image but not working.
Here are two ways to do that in Python/OpenCV/Numpy. The first method uses cv2.inRange() color thresholding. The second method use Numpy. Both threshold so that the gray region becomes white and the rest black.
Input:
import cv2
import numpy as np
# read input
img = cv2.imread("white_gray_black.png")
# Method 1 Use cv2.inRange
low = (127,127,127)
high = (127,127,127)
mask1 = cv2.inRange(img, low, high)
cv2.imwrite("white_gray_black_mask1.png",mask1)
# Method 2 Use Numpy
mask2 = img.copy()
mask2[img==127] = 255
mask2[img!=127 ] = 0
cv2.imwrite("white_gray_black_mask2.png",mask2)
cv2.imshow('mask1', mask1)
cv2.imshow('mask2', mask2)
cv2.waitKey(0)
cv2.destroyAllWindows()
Result 1:
Result 2:
If you need to preserve the gray, then the second method is easier.
# Method 3 Use Numpy
mask3 = img.copy()
mask3[img!=127] = 0
cv2.imwrite("white_gray_black_mask3.png",mask3)
cv2.imshow('mask3', mask3)
cv2.waitKey(0)
cv2.destroyAllWindows()
Option 1:
You need to place different threshold values but the same binary operation. Then try subtracting the result:
ret, thresh1 = cv2.threshold(img_grey,20,255,cv2.THRESH_BINARY)
ret, thresh2 = cv2.threshold(img_grey,235,255,cv2.THRESH_BINARY)
result = thresh1 - thresh2
cv2.imshow("Result", result)
cv2.waitKey(0)
See the images of thresh1 and thresh2 to understand what's going on.
And if you are worried about how to subtract use cv2.subtract(), it handles pixel range:
result2 = cv2.subtract(thresh1, thresh2)
cv2.imshow("Result2", result2)
cv2.waitKey(0)
The output does not provide the desired result.
Option 2:
You can also use multi-Otsu threshold module available from skimage. This module provides threshold values based on the number of regions you want to segment the image.
If you want to segment the image into 3 regions (A, B, C), the function gives you 2 threshold values (t1, t2).
0 - t1 --> region A
t1 - t2 --> region B
t2 - 255 --> region C
The same can be extended for any number of regions, but the function takes more time to calculate threshold values for regions greater than 5.
Number of Threshold values = Number of regions - 1
Code:
# import the module
from skimage.filters import threshold_multiotsu
im = cv2.imread('cells.png', 0)
# create black screen of same image shape
b = np.zeros(im.shape, np.uint8)
# find threshold values for 3 regions
thresholds = threshold_multiotsu(im,3)
print(thresholds)
>>> [ 65 189] # 2 threshold values
# finding average value
mean = int(np.mean(thresholds))
b[im == mean] = 255
Option 2 would be a more statistical way of determining threshold value for any image
Related
I currently have the following image and the salience map below which reflects the attention areas of the first image:
Both of them are the same size. What I am trying to do is amplify the region of areas that are very white in the salient region. For example, the eyes, collar and hair would be a bit more highlighted. I have the following code which I have tried to split the image into h, s, v and then multiply through but the output is black and white. Any help is greatly appreciated:
hsv_image = cv2.cvtColor(image, cv2.COLOR_BGR2HSV)
h, s, v = cv2.split(hsv_image)
dimensions = (384, 384)
saliencyMap = cv2.resize(saliencyMap, dimensions)
s1 = s * saliencyMap.astype(s.dtype)
hsv_image = cv2.merge([h, s1, v])
out = cv2.cvtColor(hsv_image, cv2.COLOR_HSV2BGR)
cv2.imshow('example', out)
cv2.waitKey()
Here is how to do that in Python/OpenCV. Add the two images (from your other post). Modify the mask to have values near a mean of mid-gray. Separate the image into H,S,V channels. Apply the mask to the Saturation channel doing hard light composition. Combine the new saturation with the old hue and value channels and convert back to BGR.
Input 1:
Input 2:
Mask:
import cv2
import numpy as np
# read image 1
img1 = cv2.imread('img1.png')
hh, ww = img1.shape[:2]
# read image 2 and resize to same size as img1
img2 = cv2.imread('img2.png')
img2 = cv2.resize(img2, (ww,hh))
# read saliency mask as grayscale and resize to same size as img1
mask = cv2.imread('mask.png')
mask = cv2.cvtColor(mask, cv2.COLOR_BGR2GRAY)
mask = cv2.resize(mask, (ww,hh))
# add img1 and img2
img12 = cv2.add(img1, img2)
# get mean of mask and shift mean to mid-gray
# desirable for hard light compositing
# add bias as needed
mean = np.mean(mask)
bias = -32
shift = 128 - mean + bias
mask = cv2.add(mask, shift)
# threshold mask at mid gray and convert to 3 channels
# (needed to use as src < 0.5 "if" condition in hard light)
thresh = cv2.threshold(mask, 128, 255, cv2.THRESH_BINARY)[1]
# convert img12 to hsv
hsv = cv2.cvtColor(img12, cv2.COLOR_BGR2HSV)
# separate channels
hue,sat,val = cv2.split(hsv)
# do hard light composite of saturation and mask
# see CSS specs at https://www.w3.org/TR/compositing-1/#blendinghardlight
satf = sat.astype(np.uint8)/255
maskf = mask.astype(np.uint8)/255
threshf = thresh.astype(np.uint8)/255
threshf_inv = 1 - threshf
low = 2.0 * satf * maskf
high = 1 - 2.0 * (1-satf) * (1-maskf)
new_sat = ( 255 * (low * threshf_inv + high * threshf) ).clip(0, 255).astype(np.uint8)
# combine new_sat with old hue and val
result = cv2.merge([hue,new_sat,val])
# save results
cv2.imwrite('img12_sat_hardlight.png', result)
# show results
cv2.imshow('img12', img12)
cv2.imshow('mask', mask)
cv2.imshow('thresh', thresh)
cv2.imshow('result', result)
cv2.waitKey(0)
cv2.destroyAllWindows()
Result:
So I currently have 2 images below. These were obtained using a neural style transfer network on the foreground and background parts of the image.
The code used was the following:
add = cv2.add(image1, image2)
cv2.imshow('Addition', add)
cv2.waitKey(0)
cv2.destroyAllWindows()
I have merged them to create the following resultant image:
However, I want to use the salience mask on the obtained picture. The salience mask is below. I have tried inputting the mask argument to the add function but had no luck. I've tried looking at this article but wasn't able to work out what to do Add 2 images together based on a mask
EDIT:
Found the issue was that I was not converting the data types. The updated code is:
saliencyMap = cv2.resize(saliencyMap, (384,384))
newmap = saliencyMap.astype(np.uint8)
add = cv2.add(image1, image2, mask=newmap)
cv2.imshow('addition', add)
cv2.waitKey(0)
However, the resultant image is and I'm not sure why:
I think what you might want is to simply resize the two images to the same size and then add them. Then use the mask to do hard light compositing in Python/OpenCV.
img1:
img2:
mask (saliency):
import cv2
import numpy as np
# read image 1
img1 = cv2.imread('img1.png')
hh, ww = img1.shape[:2]
# read image 2 and resize to same size as img1
img2 = cv2.imread('img2.png')
img2 = cv2.resize(img2, (ww,hh))
# read saliency mask as grayscale and resize to same size as img1
mask = cv2.imread('mask.png')
mask = cv2.cvtColor(mask, cv2.COLOR_BGR2GRAY)
mask = cv2.resize(mask, (ww,hh))
# add img1 and img2
img12 = cv2.add(img1, img2)
# get mean of mask and shift mean to mid-gray
# desirable for hard light compositing
# add bias as needed
mean = np.mean(mask)
bias = -32
shift = 128 - mean + bias
mask = cv2.add(mask, shift)
mask = cv2.merge([mask,mask,mask])
# threshold mask at mid gray and convert to 3 channels
# (needed to use as src < 0.5 "if" condition in hard light)
thresh = cv2.threshold(mask, 128, 255, cv2.THRESH_BINARY)[1]
# do hard light composite of img12 and mask
# see CSS specs at https://www.w3.org/TR/compositing-1/#blendinghardlight
img12f = img12.astype(np.uint8)/255
maskf = mask.astype(np.uint8)/255
threshf = thresh.astype(np.uint8)/255
threshf_inv = 1 - threshf
low = 2.0 * img12f * maskf
high = 1 - 2.0 * (1-img12f) * (1-maskf)
result = ( 255 * (low * threshf_inv + high * threshf) ).clip(0, 255).astype(np.uint8)
# save results
cv2.imwrite('img12_hardlight.png', result)
# show results
cv2.imshow('img12', img12)
cv2.imshow('mask', mask)
cv2.imshow('thresh', thresh)
cv2.imshow('result', result)
cv2.waitKey(0)
cv2.destroyAllWindows()
Result:
I have detected two lines in an image using cv2. now I want to get the RGB values of both lines in separate variables like left_line_veriable = ['rgb values'], right_line_rgb_values = ['rgb values']
Here is my code:
import cv2
import numpy as np
image = cv2.imread('tape.png')
image = cv2.cvtCOLOR(image, cv2.COLOR_BGR2GRAY)
# Apply adaptive threshold
image_thr = cv2.adaptiveThreshold(image, 255, cv2.THRESH_BINARY_INV, cv2.ADAPTIVE_THRESH_GAUSSIAN_C, 81, 2)
# Apply morphological opening with vertical line kernel
kernel = np.ones((image.shape[0], 1), dtype=np.uint8) * 255
image_mop = cv2.morphologyEx(image_thr, cv2.MORPH_OPEN, kernel)
color_detected_img = cv2.bitwise_and(image, image, mask=image_mop)
cv2.imshow('image', color_detected_img)
cv2.waitKey(0)
cv2.destroyAllWindows()
This is the image from which I want to get both line's RGB values in two variables as described above:
Maybe is not the most optimal way, but it is not hard to do. As I said in my comments, you can label the image to kind of segment the lines, then get the mean of the rgb values in it and the average position to get to know which one is left and right. Here is a small script to demonstrate what I am saying. The last part is just to show the results.
import cv2
import numpy as np
# load img and get the greyscale
img = cv2.imread("x.png")
grey = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# label the image
ret, thres = cv2.threshold(grey, 1, 255, cv2.THRESH_BINARY)
labelAmount, labels = cv2.connectedComponents(thres)
# get the mean of the color and position
values = []
# first label (0) is background
for i in range(1, labelAmount):
mask = np.zeros(labels.shape, dtype=np.uint8)
mask[labels == i] = 255
mean = cv2.mean(img, mask)[:-1]
meanPos = np.mean(cv2.findNonZero(mask), axis=0)[0]
values.append((mean, meanPos))
# sort them by x value (left to right)
values = sorted(values, key = lambda v : v[1][0])
left_line_color = values[0][0]
right_line_color = values[1][0]
# just to show the results
left_only = np.zeros(img.shape, dtype=np.uint8)
right_only = np.zeros(img.shape, dtype=np.uint8)
left_only = cv2.line (left_only, (int(values[0][1][0]), 0), (int(values[0][1][0]), img.shape[0]), left_line_color,5 )
right_only = cv2.line (right_only, (int(values[1][1][0]), 0), (int(values[1][1][0]), img.shape[0]), right_line_color,5 )
cv2.imshow("left_line", left_only)
cv2.imshow("right_line", right_only)
cv2.imshow("original", img)
cv2.waitKey(0)
I am new to image manipulation in python and would appreciate some advice on 2 problems.
I have an image: and its mask:
and open it as follows:
import cv2
import matplotlib.pyplot as plt
mask = cv2.imread('img_mask.jpg')
img = cv2.imread('img.jpg')
1) I have the following (x,y) pixel locations:
pt1 = 43.35, 22.49
pt2 = 49.035, 46.985
pt3 = 18.326, 21.822
On the mask, the pixel value at pt1 and pt2 is 0 and at pt3 it is 16. Given the three (x,y) pixel locations as a list, as well as the mask, as provided. How can I efficiently filter the locations whose value is 0 on the mask ?
2) How can I efficiently create a new thresholded masked image , then overlay it on the original image, such that the thresholded mask image is an image where the mask only has a value of 16 as obtained from pixel locations in the original mask where the values are 16.
For the first part I don't know why your points are not integers. I downloaded the mask in the question and this code can be used to print the pairs having pixel value=0. I read the mask in grayscale format.
img = cv2.imread('mask.jpg',0)
for i in range(0,img.shape[0]):
for j in range(0,img.shape[1]):
if img[i,j] == 0:
print(i,j)
For the second part normal thresholding can be used. Refer to opencv documentation for more on thresholding. I zoomed the image by 4 times to have a better view. Then two thresholds were created having threshold value of 16 and 17 and performing a bitwise xor of them to obtain the result.
zoom = cv2.resize(img, None, fx = 4, fy = 4, interpolation = cv2.INTER_CUBIC)
ret,thresh1 = cv2.threshold(zoom, 16, 255, cv2.THRESH_BINARY)
ret,thresh2 = cv2.threshold(zoom, 17, 255, cv2.THRESH_BINARY)
output = cv2.bitwise_xor(thresh1, thresh2)
cv2.imshow('threshold with 16', thresh1)
cv2.imshow('threshold with more than 16', thresh2)
cv2.imshow('result', output)
cv2.waitKey(0)
cv2.destroyAllWindows()
Threshold 1
Threshold 2
Output
I have tried this code.
import sys
import numpy as np
sys.path.append('/usr/local/lib/python2.7/site-packages')
import cv2
from cv2.cv import *
img=cv2.imread("test2.jpg",cv2.IMREAD_COLOR)
gimg = cv2.imread("test2.jpg",cv2.IMREAD_GRAYSCALE)
b,g,r = cv2.split(img)
ret,thresh1 = cv2.threshold(gimg,127,255,cv2.THRESH_BINARY);
numrows = len(thresh1)
numcols = len(thresh1[0])
thresh = 170
im_bw = cv2.threshold(gimg, thresh, 255, cv2.THRESH_BINARY)[1]
trig=0
trigmax=0;
xmax=0
ymax=0
for x in range(0,numrows):
for y in range(0,numcols):
if(im_bw[x][y]==1):
trig=trig+1;
if(trig>5):
xmax=x;
ymax=y;
break;
print x,y,numrows,numcols,trig
roi=gimg[xmax:xmax+200,ymax-500:ymax]
cv2.imshow("roi",roi)
WaitKey(0)
here test2.jpg is what I am tring to do is to concentrate on the high intensity part of the image(i.e the circle with high intensity in image).But my code does not seem to do so.
Can anyone help?
I found answer to my question from here
here is my code
# import the necessary packages
import numpy as np
import cv2
# load the image and convert it to grayscale
image = cv2.imread('test2.jpg')
orig = image.copy()
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
# apply a Gaussian blur to the image then find the brightest
# region
gray = cv2.GaussianBlur(gray, (41, 41), 0)
(minVal, maxVal, minLoc, maxLoc) = cv2.minMaxLoc(gray)
image = orig.copy()
roi=image[maxLoc[1]-250:maxLoc[1]+250,maxLoc[0]-250:maxLoc[0]+250,2:2]
cv2.imshow("Robust", roi)
cv2.waitKey(0)
test2.jpg
ROI
Try checking whether a pixel is not zero - it turns out those pixels have a value of 255 after thresholding, as it is a grayscale image after all.
The threshold seems to be wrong also, but I don't really know what you want to see (display it with imshow - it isn't just the circle). And your code matches the number '3' in the bottom left corner, therefore the ROI matrix indices are invalid in your example.
EDIT:
After playing around with the image, I ended up using a different approach. I used the SimpleBlobDetector and did an erosion on the image before, so the region you're interested in remains connected. For the blob detector the program inverts the image first. (You may want to read a SimpleBlobDetector tutorial as I did, parts of the code are based on that page - many thanks to the author!)
The following code displays the procedure step by step:
import cv2
import numpy as np
# Read image
gimg = cv2.imread("test2.jpg", cv2.IMREAD_GRAYSCALE)
# Invert the image
im_inv = 255 - gimg
cv2.imshow("Step 1 - inverted image", im_inv)
cv2.waitKey(0)
# display at a threshold level of 50
thresh = 45
im_bw = cv2.threshold(im_inv, thresh, 255, cv2.THRESH_BINARY)[1]
cv2.imshow("Step 2 - bw threshold", im_bw)
cv2.waitKey(0)
# erosion
kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE,(10,10))
im_bw = cv2.erode(im_bw, kernel, iterations = 1)
cv2.imshow('Step 3 - erosion connects disconnected parts', im_bw)
cv2.waitKey(0)
# Set up the detector with default parameters.
params = cv2.SimpleBlobDetector_Params()
params.filterByInertia = False
params.filterByConvexity = False
params.filterByCircularity = False
params.filterByColor = False
params.minThreshold = 0
params.maxThreshold = 50
params.filterByArea = True
params.minArea = 1000 # you may check with 10 --> finds number '3' also
params.maxArea = 100000 #im_bw.shape[0] * im_bw.shape[1] # max limit: image size
# Create a detector with the parameters
ver = (cv2.__version__).split('.')
if int(ver[0]) < 3 :
detector = cv2.SimpleBlobDetector(params)
else :
detector = cv2.SimpleBlobDetector_create(params)
# Detect blobs.
keypoints = detector.detect(im_bw)
print "Found", len(keypoints), "blobs:"
for kpt in keypoints:
print "(%.1f, %.1f) diameter: %.1f" % (kpt.pt[0], kpt.pt[1], kpt.size)
# Draw detected blobs as red circles.
# cv2.DRAW_MATCHES_FLAGS_DRAW_RICH_KEYPOINTS ensures the size of the
# circle corresponds to the size of blob
im_with_keypoints = cv2.drawKeypoints(gimg, keypoints, np.array([]), (0,0,255),
cv2.DRAW_MATCHES_FLAGS_DRAW_RICH_KEYPOINTS)
# Show keypoints
cv2.imshow("Keypoints", im_with_keypoints)
cv2.waitKey(0)
This algorithm finds the coordinate (454, 377) as the center of the blob, but if you reduce the minArea to e.g. 10 then it will find the number 3 in the bottom corner as well.