I would like to convert this image to gray scale in OpenCV and then find the settings to threshold it. I want to threshold the black spaces inside of the skull. Can someone write out a Python script using OpenCV?
I convert to grayscale with: gray = cv2.cvtColor(pic, cv2.COLOR_BGR2GRAY)
When I do (ret, thresh) = cv2.threshold(gray, 177, 255, cv2.THRESH_BINARY) I get a black image.
When I do (ret, thresh) = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY) the whole head becomes a white blob.
If i has understood your goal correctly, here is my proposal. It uses Otsu's Thresholding as #stateMachine has said.
import cv2
im = cv2.imread("IScDg.png")
gray = cv2.cvtColor(im, cv2.COLOR_BGR2GRAY)
# Your first method
(_, thresh1) = cv2.threshold(gray, 177, 255, cv2.THRESH_BINARY)
# Your second method
(ret, thresh2) = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY)
# Proposed method
(_, thresh3) = cv2.threshold(gray, 0, 1, cv2.THRESH_BINARY | cv2.THRESH_OTSU)
different thresholds on your image
I usually apply OTSU with good results.
ret,gray = cv2.threshold(gray,100,255,cv2.THRESH_BINARY+cv2.THRESH_OTSU)
Related
I have a bean on a white background damper, the issue is that the damper is not perfectly white (as in 255,255,255). I have tried using cv2.threshold() method but I kept getting a deformed image with spots. What is the best way to achieve this?
My Image
My Code
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
ret, thresh = cv2.threshold(gray, 240, 255, cv2.THRESH_BINARY)
img[thresh == 255] = 0
kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (5, 5))
erosion = cv2.erode(img, kernel, iterations = 1)
cv2.namedWindow('image', cv2.WINDOW_NORMAL)
cv2.imshow("image", erosion)
In the end, the beans should only be surrounded by black images!
It could be the solution:
import cv2
import numpy as np
img = cv2.imread("data/bob.jpg")
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
img_blured = cv2.blur(gray, (5, 5))
ret, thres = cv2.threshold(img_blured, 130, 255, cv2.THRESH_BINARY)
neg = cv2.bitwise_not(thres)
erosion = cv2.erode(neg, np.ones((6, 6), np.uint8), iterations=1)
cv2.imshow("erosion", erosion)
# ret, thresh = cv2.threshold(gray, 240, 255, cv2.THRESH_BINARY)
img[erosion == 0] = 0
cv2.imshow("image", img)
cv2.waitKey(0)
Here I am using cv2.threshold but for bigger range than you used, but before it I blured the image. Then I negate and erode it.
But this cuts off the bean itself a little, if this is critical, then you should use a completely different algorithm. For example, the cv2.Canny method to find the contours of a bean and somehow further process it.
I'm trying to remove a noise from a photo of a monitor screen. Here's the source photo:
I've tried some different approaches, so the current version of my code is as follows:
clr_img = cv2.imread("D:\Noisy.jpg", 1)
gray_img = cv2.cvtColor(clr_img, cv2.COLOR_BGR2GRAY)
gray_img = cv2.fastNlMeansDenoising(gray_img, h=11)
binary_image = cv2.adaptiveThreshold(gray_img, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C, cv2.THRESH_BINARY_INV, 91, 12)
Here's the result:
Is it possible to get rid of this kind of the noise?
You need to apply a smoothing operation before adaptive thresholding. A simple blur should help to reduce the noise. Any of these should work: Simple average blur (cv2.blur), Gaussian blur (cv2.GaussianBlur), or Median blur (cv2.medianBlur). Here's the result using a (7,7) Gaussian blur:
import cv2
image = cv2.imread('1.jpg')
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
blur = cv2.GaussianBlur(gray, (7,7), 0)
thresh = cv2.adaptiveThreshold(blur,255,cv2.ADAPTIVE_THRESH_GAUSSIAN_C, cv2.THRESH_BINARY_INV,51,9)
result = 255 - thresh
cv2.imshow('thresh', thresh)
cv2.imshow('result', result)
cv2.waitKey()
I am trying to segment this image because I need only to obtain the document.
Applying some filters I got this result:
I am trying to get the outline of the white rectangle but I get this result:
Anyone have any idea how to do better?
this is my code :/
import cv2
image = cv2.imread('roberto.jpg')
image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
gray = cv2.cvtColor(image, cv2.COLOR_RGB2GRAY)
_, binary = cv2.threshold(gray, 225, 255, cv2.THRESH_BINARY_INV)
cv2.imshow('test', binary)
cv2.waitKey(0)
contours, hierarchy = cv2.findContours(binary, cv2.RETR_LIST, cv2.CHAIN_APPROX_SIMPLE)[-2:]
idx = 0
for cnt in contours:
idx += 1
x,y,w,h = cv2.boundingRect(cnt)
roi=binary[y:y+h,x:x+w]
cv2.rectangle(image,(x,y),(x+w,y+h),(200,0,0),2)
cv2.imshow('img',image)
cv2.waitKey(0)
You're almost there, you just need to obtain the x,y,w,h bounding rectangle coordinates using cv2.boundingRect then you can extract/save the ROI using Numpy slicing. Here's the result
import cv2
# Load image, grayscale, threshold
image = cv2.imread('1.jpg')
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
blur = cv2.GaussianBlur(gray, (3,3), 0)
thresh = cv2.threshold(gray, 225, 255, cv2.THRESH_BINARY_INV)[1]
# Get bounding box and extract ROI
x,y,w,h = cv2.boundingRect(thresh)
ROI = image[y:y+h, x:x+w]
cv2.imshow('thresh', thresh)
cv2.imshow('ROI', ROI)
cv2.waitKey()
I'm attempting to extract a blue object, very much like the one described in https://docs.opencv.org/3.0-beta/doc/py_tutorials/py_imgproc/py_colorspaces/py_colorspaces.html#object-tracking
An example of a raw image with three blue shapes to extract is here:
The captured image is noisy and the unfiltered shape detection returns hundreds to thousands of "blue" shapes. In order to mitigate this, I applied the following steps:
Blurring the image before filtering it, resulting in closed surfaces
Converting the masked image (after bitwise_and) back to grayscale
Applying an OTSU threshold
Finally, detect the contours
The complete code is:
import cv2
import numpy as np
cap = cv2.VideoCapture(0)
while(True):
ret, frame = cap.read()
blur = cv2.GaussianBlur(frame, (15, 15), 0)
hsv = cv2.cvtColor(blur, cv2.COLOR_BGR2HSV)
lower_red = np.array([115, 50, 50])
upper_red = np.array([125, 255, 255])
mask = cv2.inRange(hsv, lower_red, upper_red)
blue = cv2.bitwise_and(blur, blur, mask=mask)
gray = cv2.cvtColor(blue, cv2.COLOR_BGR2GRAY)
(T, ted) = cv2.threshold(gray, 0, 255, cv2.THRESH_OTSU)
im2, contours, hierarchy = cv2.findContours(
ted, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
for cnt in contours:
cv2.drawContours(frame, [cnt], 0, (0, 255, 0), 3)
font = cv2.FONT_HERSHEY_SIMPLEX
cv2.putText(frame, str(len(contours)), (10, 500), font, 2, (0, 0, 255), 2, cv2.LINE_AA)
cv2.imshow('mask', mask)
cv2.imshow('blue', blue)
cv2.imshow('grey', gray)
cv2.imshow('thresholded', ted)
cv2.imshow('frame', frame)
if cv2.waitKey(1) & 0xFF == ord('q'):
break
cap.release()
cv2.destroyAllWindows()
Unfortunately, there are still 6-7 contours left whereas there should be three.
How can I further refine image processing to get just the three shapes?
You could use morphological operations coupled with connected components analysis:
Apply erosion on grayscale image: https://docs.opencv.org/2.4/modules/imgproc/doc/filtering.html?highlight=erode#erode
Find connected components, function cv::connectedComponents (https://docs.opencv.org/3.1.0/d3/dc0/group__imgproc__shape.html#gac2718a64ade63475425558aa669a943a)
Retain connected components whose area is bigger than a given threshold.
Dilate the resulting mask: https://docs.opencv.org/2.4/modules/imgproc/doc/filtering.html?highlight=dilate#dilate
If the shapes that you're looking for specific shapes (e.g. shapes), you could use some shape descriptors.
Finally, I suggest you trying replacing the Gaussian Filter with a bilateral filter (https://docs.opencv.org/3.0-beta/modules/imgproc/doc/filtering.html#bilateralfilter) to better preserve the shapes. If you want an even better filter, have a look at this tutorial on NL-means filter (https://docs.opencv.org/3.3.1/d5/d69/tutorial_py_non_local_means.html)
For a prototype I need to build a 3d model of a gear. This have a "many" number of teeth.
So I am trying to count them using OpenCV and Python. I found this (only?) post which explain how to do it in C++.
I am following the steps and, for now this is the code I made.
import numpy as np
import cv2
img = cv2.imread('C:\\Users\\Link\\Desktop\\gear.png')
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
ret, thresh = cv2.threshold(gray, 127, 255, cv2.THRESH_BINARY_INV | cv2.THRESH_OTSU)
kernel = np.ones((3, 3), np.uint8)
img_erosion = cv2.erode(thresh, kernel, iterations=1)
edges = cv2.Canny(img_erosion, 50, 150)
img_dilate = cv2.dilate(edges, kernel, iterations=1)
cv2.imshow('i', thresh)
cv2.waitKey(0)
cv2.imshow('i', img_erosion)
cv2.waitKey(0)
cv2.imshow('i', edges)
cv2.waitKey(0)
cv2.imshow('i', img_dilate)
cv2.waitKey(0)
What stopped me from go ahead is this: the image at some point became really a mess.
This is the original on which I am working:
And this is the output of image_dilate
As you can see, the teeth at the bottom is not displayed properly, maybe because of the shaddow in the original image. How can I get rid of this ?
Because your source image is cleaner than the link your post, so you can do approx on the max-area-contour, then get half number of points, the result is 84.
Sample code:
#!/usr/bin/python3
# 2018.01.22 11:53:24 CST
import cv2
import myutils
## Read
img = cv2.imread("img13_2.jpg")
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
## threshold and find contours
ret, threshed = cv2.threshold(gray, 50, 255, cv2.THRESH_BINARY_INV)
cnts= cv2.findContours(threshed, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE)[-2]
## Find the max-area-contour
cnt = max(contours, key=cv2.contourArea)
## Approx the contour
arclen = cv2.arcLength(cnt, True)
approx = cv2.approxPolyDP(cnt, 0.002*arclen, True)
## Draw and output the result
for pt in approx:
cv2.circle(img, (pt[0][0],pt[0][1]), 3, (0,255,0), -1, cv2.LINE_AA)
msg = "Total: {}".format(len(approx)//2)
cv2.putText(img, msg, (20,40),cv2.FONT_HERSHEY_PLAIN, 2, (0,0,255), 2, cv2.LINE_AA)
## Display
cv2.imshow("res", img);cv2.waitKey()
Result:
Solved it..
This is the code. The count is wrong by one because one teeth, on the right is lower than the others and because it found two points by itself. Don't know why this happens.
Also, it has been made with another image. It's not the source I posted above as long as it is in low definition.
import numpy as np
import cv2
img = cv2.imread('C:\\Users\\Link\\Desktop\\gear.png')
img2 = cv2.imread('C:\\Users\\Link\\Desktop\\gear.png')
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
ret, thresh = cv2.threshold(gray, 127, 255, cv2.THRESH_BINARY_INV | cv2.THRESH_OTSU)
kernel = np.ones((3, 3), np.uint8)
img_dilate = cv2.dilate(thresh, kernel, iterations=1)
im2, contours, hierarchy = cv2.findContours(img_dilate, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
cnts = cv2.drawContours(img, contours, -1, (0, 255, 0), -1)
edges = cv2.Canny(cnts, 350, 350)
cnt = contours[0]
hull = cv2.convexHull(cnt, returnPoints=False)
defects = cv2.convexityDefects(cnt, hull)
for i in range(defects.shape[0]):
s, e, f, d = defects[i, 0]
start = tuple(cnt[s][0])
end = tuple(cnt[e][0])
far = tuple(cnt[f][0])
cv2.line(edges, start, end, [0, 255, 255], 1)
circles = cv2.circle(img2, end, 5, [0, 255, 0], -1)
# print(len(defects)) - number of points
cv2.imshow('thresh', thresh)
cv2.waitKey(0)
cv2.imshow('dilate', img_dilate)
cv2.waitKey(0)
cv2.imshow('edges', edges)
cv2.waitKey(0)
cv2.imshow('cnts', cnts)
cv2.waitKey(0)
cv2.imshow('points', circles)
cv2.waitKey(0)