How to obtain long edge between black and white thresholds? - opencv

Using OpenCv I was converted green areas to white(255) and black(0): see CURRENT OUTPUT.
Using Canny, Laplacian and Sobel edge detection yielded many little edges, instead of a long consistent edge see CANNY EDGE Example.
How could I achieve one long edge as seen in the desired output?
Relevant Code:
image = cv.imread('grass pic.jpg')
lane_image = np.copy(image)
pic = cv.cvtColor(lane_image ,cv.COLOR_BGR2RGB)
lower = np.array([24,0,0])
upper = np.array([177, 194, 20])
green_selection = cv.inRange(pic, lower, upper)
canny= cv.Canny(green_selection,50,150)
plt.imshow(canny, cmap='gray')
CURRENT OUTPUT:
CANNY EDGE Example
DESIRED OUTPUT:

Converting the image to HSV colour space might help you to detect your desired line.
import cv2
import numpy as np
image = cv2.imread("image.png")
HSV = cv2.cvtColor(image, cv2.COLOR_BGR2HSV)
low = np.array([23, 98, 0])
high = np.array([253, 255, 255])
mask = cv2.inRange(HSV, low, high)
cv2.imshow("mask", mask)
cv2.imwrite("mask.png", mask)
result = cv2.bitwise_and(image, image, mask=mask)
cv2.imshow("result", result)
cv2.imwrite("result.png", result)
cv2.waitKey(0)
cv2.destroyAllWindows()
Mask Image
Result Image

Related

filtering out anything besides white in opencv

I am currently developing a robot that stays within the lines and avoids circles on the ground of the same color as the lines. I want to print all the coordinates(x,y) to the terminal to get started to make sure I am detecting the correct pixels. I wrote a program that I thought filtered out anything besides white and prints out the x,y coordinates of the white pixels, but it doesn't seem to be working. I passed through a completely black image and a completely white image and received the same coordinates. Can anyone tell me what I am doing wrong?
import cv2
import numpy as np
#cap = cv2.VideoCapture(1)
frame = cv2.imread("black.png")
while(True):
#_, frame = cap.read()
hsv = cv2.cvtColor(frame, cv2.COLOR_BGR2HSV)
# define range of white color in HSV
lower_white = np.array([0, 0, 0], dtype=np.uint8)
upper_white = np.array([0, 0, 255], dtype=np.uint8)
# Threshold the HSV image to get only white colors
mask = cv2.inRange(hsv, lower_white, upper_white)
# Bitwise-AND mask and original image
res = cv2.bitwise_and(frame,frame, mask= mask)
coord = cv2.findNonZero(mask)
print(coord)
cv2.imshow('frame',frame)
cv2.imshow('mask',mask)
cv2.imshow('res',res)
k = cv2.waitKey(5) & 0xFF
if k == 27:
break
cv2.destroyAllWindows()
Black image
White image
Test image

Blurred Circle detection

I am new to opencv and want to detect the center point of these circles. I tried with Hough Circles with thresholding but it doesn't seem to generate good results all the time.
This image is easy to get using contours and threshloding:
It is harder to do this one:
The thresholding and Hough circle doesn't work with this image:
Adding more images for help
Can you suggest any method that will be reliable for all the images?
Since the circle is the only bright thing in the image, we can get the center by looking for the centroid of the white blob. We'll auto-threshold with otsu's and use findContours to get the centroid of the mask.
import cv2
import numpy as np
# load image
img = cv2.imread("circ1.png");
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY);
# threshold
gray = cv2.GaussianBlur(gray, (5,5), 0);
_, mask = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY+cv2.THRESH_OTSU);
# contour
_, contours, _ = cv2.findContours(mask, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE);
# center
M = cv2.moments(contours[0]);
cx = int(M['m10']/M['m00']);
cy = int(M['m01']/M['m00']);
center = (int(cx), int(cy));
# draw
img = cv2.circle(img, center, 4, (0,0,200), -1);
# show
cv2.imshow("marked", img);
cv2.imshow("mask", mask);
cv2.waitKey(0);

How to detect edges for pattern on space?

I'm building a new machine and have a problem with edge detection. I have a piece of paper put on one cylinder as shown in the image below. How to detect edges of this paper?
I attempted to build a led background but this paper is very large. Therefore, my machine don't have enough spaces to run.
You can use below code as a reference. Here, I am basically using cv2.inRange function to segment light green color from the image(not dark green, otherwise edge of one of the axis will also be detected) and finally applying Canny edge detection on the grayscaled version of the segmented image, i.e., cv2.Canny.
import cv2
import numpy as np
img = cv2.imread('cylinder.png')
# convert to HSV color space
img_hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
# Threshold the HSV image to get green colors by defining range of green color in HSV
mask = cv2.inRange(img_hsv, (36,0,0), (55,255,255))
# Bitwise-AND mask and original image
res = cv2.bitwise_and(img, img, mask = mask)
# coverting image with green colored region of interest from HSV to RGB
img_hsv2bgr = cv2.cvtColor(res, cv2.COLOR_HSV2BGR)
# coverting image from RGB to GRAYSCALE
img_gray = cv2.cvtColor(img_hsv2bgr, cv2.COLOR_BGR2GRAY)
# canny edge detection
edges = cv2.Canny(img_gray, 100, 200)
cv2.imshow('Edges', edges)
cv2.waitKey(0)
cv2.destroyAllWindows()
output:
EDIT: After making some modification to above code, like segmenting yellow color portion along with light green color and applying Gaussian blurring before passing into the cv2.Canny function gives even better output than above.
Code:
# Threshold the HSV image to get both green and yellow colors by defining range of color in HSV
mask_green = cv2.inRange(img_hsv, (36,0,0), (55,255,255))
mask_yellow = cv2.inRange(img_hsv, (21, 39, 64), (38, 255, 255))
mask = cv2.bitwise_or(mask_green, mask_yellow)
# Bitwise-AND mask and original image
res = cv2.bitwise_and(img, img, mask = mask)
# coverting image with green colored region of interest from HSV to RGB
frame_hsv2bgr = cv2.cvtColor(res, cv2.COLOR_HSV2BGR)
# coverting image from RGB to GRAYSCALE
frame_gray = cv2.cvtColor(frame_hsv2bgr, cv2.COLOR_BGR2GRAY)
gaussian_blurred = cv2.GaussianBlur(frame_gray,(5, 3), 0)
# canny edge detection
edges = cv2.Canny(gaussian_blurred, 100, 200)
output:

Processing an image with block letters for OCR

I would like to OCR this battle log from a game:
battle log image
The original image had block text font, so after thresholding I inverted the colors. Now I want to remove the "black" background (but not the black text), but I'm not sure how to achieve that in OpenCV. Afterwards I imagine I would like to sharpen the text to be thicker for better OCR.
May I ask how I can go about this?
Try this out.
import cv2
import numpy as np
img = cv2.imread("1.png")
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
invert0 = cv2.bitwise_not(gray)
_,thresh = cv2.threshold(invert0,128,255,cv2.THRESH_BINARY)
invert1 = cv2.bitwise_not(thresh)
im2, contours, hierarchy = cv2.findContours(invert1,cv2.RETR_TREE,cv2.CHAIN_APPROX_SIMPLE)
mask = np.zeros(gray.shape, dtype="uint8")
for i in range(len(contours)):
if(hierarchy[0][i][3]==-1): #contour has no parent (most outer contour)
cv2.fillPoly(mask, pts =[contours[i]], color=255)
invert2 = cv2.bitwise_not(mask)
res = invert2 + invert1
cv2.imshow("img", img)
cv2.imshow("gray", gray)
cv2.imshow("invert0", invert0)
cv2.imshow("thresh", thresh)
cv2.imshow("invert1", invert1)
cv2.imshow("invert2", invert2)
cv2.imshow("mask", mask)
cv2.imshow("res", res)
cv2.waitKey()
cv2.destroyAllWindows()

OpenCV - Extracting lines on a graph

I would like to create a program that is able to extract lines from a graph.
For example, if a graph like this is inputted, I would just want the red line to be outputted.
Below I have tried to do this using a hough line transformation, however, I do not get very promising results.
import cv2
import numpy as np
graph_img = cv2.imread("/Users/2020shatgiskessell/Desktop/Graph1.png")
gray = cv2.cvtColor(graph_img, cv2.COLOR_BGR2GRAY)
kernel_size = 5
#grayscale image
blur_gray = cv2.GaussianBlur(gray,(kernel_size, kernel_size),0)
#Canny edge detecion
edges = cv2.Canny(blur_gray, 50, 150)
#Hough Lines Transformation
#distance resoltion of hough grid (pixels)
rho = 1
#angular resolution of hough grid (radians)
theta = np.pi/180
#minimum number of votes
threshold = 15
#play around with these
min_line_length = 25
max_line_gap = 20
#make new image
line_image = np.copy(graph_img)
#returns array of lines
lines = cv2.HoughLinesP(edges, rho, theta, threshold, np.array([]),
min_line_length, max_line_gap)
for line in lines:
for x1,y1,x2,y2 in line:
cv2.line(line_image,(x1,y1),(x2,y2),(255,0,0),2)
lines_edges = cv2.addWeighted(graph_img, 0.8, line_image, 1, 0)
cv2.imshow("denoised image",edges)
if cv2.waitKey(0) & 0xff == 27:
cv2.destroyAllWindows()
This produces the output image below, which does not accurately recognize the graph line. How might I go about doing this?
Note: For now, I am not concerned about the graph titles or any other text.
I would also like the code to work for other graph images aswell, such as:
etc.
If the graph does not have many noises around it (like your example) I would suggest to threshold your image with Otsu threshold instead of looking for edges . Then you simply search the contours, select the biggest one (graph) and draw it on a blank mask. After that you can perform a bitwise operation on image with the mask and you will get a black image with the graph. If you like the white background better, then simply change all black pixels to white. Steps are written in the example. Hope it helps a bit. Cheers!
Example:
import numpy as np
import cv2
# Read the image and create a blank mask
img = cv2.imread('graph.png')
h,w = img.shape[:2]
mask = np.zeros((h,w), np.uint8)
# Transform to gray colorspace and threshold the image
gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
_, thresh = cv2.threshold(gray,0,255,cv2.THRESH_BINARY_INV+cv2.THRESH_OTSU)
# Search for contours and select the biggest one and draw it on mask
_, contours, hierarchy = cv2.findContours(thresh,cv2.RETR_TREE,cv2.CHAIN_APPROX_NONE)
cnt = max(contours, key=cv2.contourArea)
cv2.drawContours(mask, [cnt], 0, 255, -1)
# Perform a bitwise operation
res = cv2.bitwise_and(img, img, mask=mask)
# Convert black pixels back to white
black = np.where(res==0)
res[black[0], black[1], :] = [255, 255, 255]
# Display the image
cv2.imshow('img', res)
cv2.waitKey(0)
cv2.destroyAllWindows()
Result:
EDIT:
For noisier pictures you could try this code. Note that different graphs have different noises and may not work on every graph image since the denoisiation process would be specific in every case. For different noises you can use different ways to denoise it, for example histogram equalization, eroding, blurring etc. This code works well for all 3 graphs. Steps are written in comments. Hope it helps. Cheers!
import numpy as np
import cv2
# Read the image and create a blank mask
img = cv2.imread('graph.png')
h,w = img.shape[:2]
mask = np.zeros((h,w), np.uint8)
# Transform to gray colorspace and threshold the image
gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
_, thresh = cv2.threshold(gray,0,255,cv2.THRESH_BINARY_INV+cv2.THRESH_OTSU)
# Perform opening on the thresholded image (erosion followed by dilation)
kernel = np.ones((2,2),np.uint8)
opening = cv2.morphologyEx(thresh, cv2.MORPH_OPEN, kernel)
# Search for contours and select the biggest one and draw it on mask
_, contours, hierarchy = cv2.findContours(opening,cv2.RETR_TREE,cv2.CHAIN_APPROX_NONE)
cnt = max(contours, key=cv2.contourArea)
cv2.drawContours(mask, [cnt], 0, 255, -1)
# Perform a bitwise operation
res = cv2.bitwise_and(img, img, mask=mask)
# Threshold the image again
gray = cv2.cvtColor(res,cv2.COLOR_BGR2GRAY)
_, thresh = cv2.threshold(gray,0,255,cv2.THRESH_BINARY_INV+cv2.THRESH_OTSU)
# Find all non white pixels
non_zero = cv2.findNonZero(thresh)
# Transform all other pixels in non_white to white
for i in range(0, len(non_zero)):
first_x = non_zero[i][0][0]
first_y = non_zero[i][0][1]
first = res[first_y, first_x]
res[first_y, first_x] = 255
# Display the image
cv2.imshow('img', res)
cv2.waitKey(0)
cv2.destroyAllWindows()
Result:

Resources