I want to be able to count the number of pixels in a detected object. I'm using the cv2.threshold function. Here is some sudo code.
import cv2
import numpy as np
import time
while True:
cam= cv2.VideoCapture(0)
while(cam.isOpened())
ret, image = cam.read()
image = cv2.GaussianBlur(image, (5,5), 0)
Image1 = cv2.cvtColor(image, cv2.COLOR_BGR2HSV)
lower= np.array([30,40,40], dtype='uint8')
upper= np.array([95,240,240], dtype='uint8')
Thresh= cv2.inRange(Image1, lower, upper)
From here on out, I have no idea how to count the pixels of my objects. How do you find the contours of a binary image? I suppose it could be possible to cv2.bitwise_and a full black image over the Thresh/ mask, but that seems like it could be slow and also I don't know how to create a fully black and white image like that.
So TD:LR, how do you count the number of pixels in an object from a binary image?
Note: I'm actually just after the largest object and only need the number of pixels, not the image.
Edit: not trying to count the total number of pixels detected, I've already done that. Want the number of pixels detected from the object with the largest number.
This is how I did it
import cv2
import numpy as np
import time
from scipy.ndimage import (labeled_comprehension, label, measurements, generate_binary_structure) # new import
while True:
cam= cv2.VideoCapture(0)
while(cam.isOpened())
ret, image = cam.read() # record image
image = cv2.GaussianBlur(image, (5,5), 0) # blur to remove noise
Image1 = cv2.cvtColor(image, cv2.COLOR_BGR2HSV) # convert to better color scheme
lower= np.array([30,40,40], dtype='uint8') # low green
upper= np.array([95,240,240], dtype='uint8') # high green
Thresh= cv2.inRange(Image1, lower, upper) # returns array with 255 as pixel if in threshold
struct = generate_binary_structure(2,2) # seems necessary for some reason
Label, features = label(Thresh, struct) # label is object, features is number of objects
Arange = np.arange(1, features+1) # seems necessary for some reason
Biggest = sorted(labeled_comprehension(Thresh, Label, Arange, np.sum, float, -1))[features-1]//255 # counts and organises the objects based on size. [features-1] means last object, ie: biggest. //255 because that's each pixel work (from thresh)
Related
I have this image shown below
And, here I am trying to define the threshold to distinguish bimodal class by using the Otsu technique based on intensity and then visualise those in the histogram. So far I have written following codes:
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("Fig_1.png")
# 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
fig, ax = plt.subplots(nrows=1, ncols=3, figsize=(10, 3.5))
# Plotting the original image.
ax[0].imshow(image, cmap='gray')
ax[0].set_title('Original')
ax[0].axis('off')
# Plotting the histogram and the two thresholds obtained from
# multi-Otsu.
ax[1].hist(image.ravel(), bins=255)
ax[1].set_title('Histogram')
for thresh in thresholds:
ax[1].axvline(thresh, color='r')
# Plotting the Multi Otsu result.
ax[2].imshow(regions, cmap='gray')
ax[2].set_title('Multi-Otsu result')
ax[2].axis('off')
plt.subplots_adjust()
plt.show()
This gives me the following result. Here As you can see Multi-Otsu result is totally black and does not show the two class of object present in the figure.
I choose classes=5 but this is bimodal hence putting classes=3 also giving me the same result.
Any advice on how to correct this? Thanks in advance.
Scenario
I've one image where I want to replace one color (in range of course). It is a bed-sheet image where I want to replace the color with other but I don't want to lose the reflection effect also.(I've picked the range values manually)
Code
import numpy as np
import cv2
image = cv2.imread('4.png')
cv2.imshow("images",image)
boundaries = [
([102,116,127], [203,218,227])
]
# loop over the boundaries
for (lower, upper) in boundaries:
# create NumPy arrays from the boundaries
lower = np.array(lower, dtype = "uint8")
upper = np.array(upper, dtype = "uint8")
# find the colors within the specified boundaries and apply
# the mask
mask = cv2.inRange(image, lower, upper)
#print mask
output = cv2.bitwise_and(image, image, mask = mask)
# show the images
cv2.imshow("images", np.hstack([image, output]))
cv2.waitKey(0)
Current Output
I think the bed is the only problem I've facing (that's why I wanted to replace the colors only for above bed pixels) which is kinda having the same RGB values as the Bed-sheet(my guess). I've not found any resource where I can get how to do this. I will appreciate your guidance, and I'm totally new to this.
UPDATE
I have tried once again with HSV values and I'm kind of happy with the result, but how do I assign(change) the color?
The following python script should split overlapping cells apart which does work quite good. The problem is now that it also splits some of the cells apart which don't overlap with other cells. To make things clear to you i'll add my input image and the output image.
The input:input image
The output:
output image
Output image where I marked two "bad" segmented cells:Output image with marked errors
Thresholded image: Thresholded image
Does someone have an idea how to avoid this problem or is the whole approach not good enough to process these kind of images?
I am using the following piece of code to segment the cells:
from skimage.feature import peak_local_max
from skimage.morphology import watershed
from scipy import ndimage
import numpy as np
import cv2
# load the image and perform pyramid mean shift filtering
# to aid the thresholding step
image = cv2.imread('C:/Users/Root/Desktop/image13.jpg')
shifted = cv2.pyrMeanShiftFiltering(image, 41, 51)
# convert the mean shift image to grayscale, then apply
# Otsu's thresholding
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
thresh = cv2.threshold(gray, 0, 255,
cv2.THRESH_BINARY | cv2.THRESH_OTSU)[1]
im = gray.copy()
D = ndimage.distance_transform_edt(thresh)
localMax = peak_local_max(D, indices=False, min_distance=3,
labels=thresh)
# perform a connected component analysis on the local peaks,
# using 8-connectivity, then apply the Watershed algorithm
markers = ndimage.label(localMax, structure=np.ones((3, 3)))[0]
labels = watershed(-D, markers, mask=thresh)
print("[INFO] {} unique segments found".format(len(np.unique(labels)) - 1))
conts=[]
for label in np.unique(labels):
# if the label is zero, we are examining the 'background'
# so simply ignore it
if label == 0:
continue
# otherwise, allocate memory for the label region and draw
# it on the mask
mask = np.zeros(gray.shape, dtype="uint8")
mask[labels == label] = 255
# detect contours in the mask and grab the largest one
cnts = cv2.findContours(mask.copy(), cv2.RETR_EXTERNAL,
cv2.CHAIN_APPROX_SIMPLE)[-2]
c = max(cnts, key=cv2.contourArea)
rect = cv2.minAreaRect(c)
box = cv2.boxPoints(rect)
box = np.int0(box)
if cv2.contourArea(c) > 150:
#cv2.drawContours(image,c,-1,(0,255,0))
cv2.drawContours(image,[box],-1,(0,255,0))
cv2.imshow("output", image)
cv2.waitKey()
I am attempting to threshold a wave so that the white background appears black and the wave itself which was originally black is white, however it only seems to return an entirely black image. What am I doing wrong?
import cv2
src = cv2.imread("C:\\Users\\ksatt\\Desktop\\SoundByte\\blackwaveblackaxis (1).PNG",0)
maxValue = 255
thresh= 53
if not src is None:
th, dst = cv2.threshold(src, thresh, maxValue, cv2.THRESH_BINARY_INV)
cv2.imshow("blackwave.PNG", dst)
cv2.imwrite("blackwave.PNG", dst)
cv2.waitKey(0)
else:
print 'Image could not be read'
Your threshold is too low, and the dark paper is going to pick up values that you don't want anyways. Basically, the contrast of the image is too low.
One easy solution is to subtract out the background. The simple way to do this is to dilate() your grayscale image, which will expand the white area and overtake the black lines. Then you can apply a small GaussianBlur() to that dilated image, and this will give you a "background" image that you can subtract from your original image to get a clear view of the lines. From there you'll have a much better image to threshold(), and you can even use OTSU thresholding to automatically set the threshold level for you.
import cv2
import numpy as np
# read image
src = cv2.imread('wave.png',0)
# create background image
bg = cv2.dilate(src, np.ones((5,5), dtype=np.uint8))
bg = cv2.GaussianBlur(bg, (5,5), 1)
# subtract out background from source
src_no_bg = 255 - cv2.absdiff(src, bg)
# threshold
maxValue = 255
thresh = 240
retval, dst = cv2.threshold(src_no_bg, thresh, maxValue, cv2.THRESH_BINARY_INV)
# automatic / OTSU threshold
retval, dst = cv2.threshold(src_no_bg, 0, maxValue, cv2.THRESH_BINARY_INV+cv2.THRESH_OTSU)
You can see that manual thresholding gives the same results as OTSU, but you don't have to play around with the values for OTSU, it'll find them for you. This isn't always the best way to go but it can be quick sometimes. Check out this tutorial for more on different thresholding operations.
if you take a look at http://docs.opencv.org/2.4/modules/imgproc/doc/miscellaneous_transformations.html#threshold it will tell you what each parameter does of the function.
Also here is a good tutorial:
http://docs.opencv.org/trunk/d7/d4d/tutorial_py_thresholding.html
Python: cv.Threshold(src, dst, threshold, maxValue, thresholdType) →
None
Is the prototype which gets further explenation in the mentioned API.
So simply change your code to:
cv2.threshold(src,RESULT, thresh, maxValue, cv2.THRESH_BINARY_INV)
cv2.imshow("blackwave.PNG", RESULT)
Could you post a picture of the wave? Have you tried using standard python? Something like this should work:
import numpy as np
import matplotlib.pyplot as plt
maxValue = 255
thresh= 53
A = np.load('file.png')
# For each pixel, see if it's above/below the threshold
for i in range(A.shape[0]): # Loop along the X direction
for j in range(A.shape[1]): # Loop along the Y direction
# Set to black the background
if A[i,j] > thresh:
A[i,j] = 0
if A[i,j] == 0:
A[i,j] = 255
Or something similar.
I am trying to calibrate camera using OpenCV tools according to the following this guide.
The problem is that function findChessboardCorners cannot find any chessboard on images I tried. I used a lot of them - even just plain chessboard pattern. In any case, nothing was detected.
Here is the code (almost the same as from link above):
import numpy as np
import cv2
import glob
# termination criteria
criteria = (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 30, 0.001)
# prepare object points, like (0,0,0), (1,0,0), (2,0,0) ....,(6,5,0)
objp = np.zeros((6*7,3), np.float32)
objp[:,:2] = np.mgrid[0:7,0:6].T.reshape(-1,2)
# Arrays to store object points and image points from all the images.
objpoints = [] # 3d point in real world space
imgpoints = [] # 2d points in image plane.
images = glob.glob('*.png')
for fname in images:
img = cv2.imread(fname)
gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
# Find the chess board corners
ret, corners = cv2.findChessboardCorners(gray, (7,6),None)
# If found, add object points, image points (after refining them)
if ret == True:
objpoints.append(objp)
corners2 = cv2.cornerSubPix(gray,corners,(11,11),(-1,-1),criteria)
imgpoints.append(corners2)
# Draw and display the corners
img = cv2.drawChessboardCorners(img, (7,6), corners2,ret)
cv2.imshow('img',img)
cv2.waitKey(500)
cv2.destroyAllWindows()
The only change I made is that i switched from .jpg files to .png files - for some reason, function imread cannot read jpg images (that's another strange problem for other topic).
Thank you in advance for advices!
Image ref:
Just for other Python newbies that may go down this road. Working code:
import numpy as np
import cv2
import glob
# termination criteria
criteria = (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 30, 0.001)
# Arrays to store object points and image points from all the images.
imgpoints = [] # 2d points in image plane.
images = glob.glob('*.png')
for fname in images:
img = cv2.imread(fname)
gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
ret = False
# Find the chess board corners
ret, corners = cv2.findChessboardCorners(gray, (7,7))
# If found, add object points, image points (after refining them)
if ret == True:
cv2.cornerSubPix(gray, corners, (11,11), (-1,-1), criteria)
imgpoints.append(corners)
# Draw and display the corners
cv2.drawChessboardCorners(img, (7,7), corners, ret)
cv2.imshow('img',img)
cv2.waitKey(0)
cv2.destroyAllWindows()
Two main points:
You have to carefully count dimension of you pattern. (7,7) is for usual chessboard.
Line img = cv2.drawChessboardCorners(img, (7,6), corners2,ret) doesn't work, you have to change it to cv2.drawChessboardCorners(img, (7,6), corners2,ret) (function doesn't return image).
Thanks to AldurDisciple!