OpenCV detect and trace darkest lines on image and overlay a grid - python

I am trying to recreate the tracings on an EKG and then overlay them onto a new grid but I am stuck with how to best trace the actual tracings. In the images that follow, there are 6 separate tracings I'd like to recreate on essentially a white background with a grid. Any help would be appreciated.
I have managed to find the edges and crop this from a jpg so all I am left with is this image:
I am trying to detect the tracings with either OpenCV's findContours or Hough Line transformations but my edge findings after a gaussian blur leaves me with: .. which isn't very helpful.
The hough lines look like this:
Can someone point me in the right direction? Thanks in advance.
Edit:
I did the Local Histogram and then a gaussian blur and another Canny edge detection. The local histogram image was:
and then the canny edge detection was:

You can try using the Sobel and Laplacian detectors as follows
img = cv2.imread('experiment.png')
img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
img = cv2.GaussianBlur(img,(3,3),0)
laplacian = cv2.Laplacian(img,cv2.CV_64F)
sobelx = cv2.Sobel(img,cv2.CV_64F,1,0,ksize=1)
figure = plt.figure(figsize=(10,10))
sobel = figure.add_subplot(1,2,1)
sobel.imshow(sobelx,cmap='gray')
sobel.set_title('Sobel in x')
sobel.set_axis_off()
laplacianfig = figure.add_subplot(1,2,2)
laplacianfig.imshow(laplacian,cmap='gray')
laplacianfig.set_title('Laplacian')
laplacianfig.set_axis_off()
give you the following output
As you can see, the Sobel operator can be used to detect the lines. Maybe you can then plot those points where the pixel intensities are below the mean.

Related

Change background color for Thresholderd image

I have been trying to write a code to extract cracks from an image using thresholding. However, I wanted to keep the background black. What would be a good solution to keep the outer boundary visible and the background black. Attached below is the original image along with the threshold image and the code used to extract this image.
import cv2
#Read Image
img = cv2.imread('Original.png')
# Convert into gray scale
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# Image processing ( smoothing )
# Averaging
blur = cv2.blur(gray,(3,3))
ret,th1 = cv2.threshold(blur,145,255,cv2.THRESH_BINARY)
inverted = np.invert(th1)
plt.figure(figsize = (20,20))
plt.subplot(121),plt.imshow(img)
plt.title('Original'),plt.xticks([]), plt.yticks([])
plt.subplot(122),plt.imshow(inverted,cmap='gray')
plt.title('Threshold'),plt.xticks([]), plt.yticks([])
Method 1
Assuming the circle in your images stays in one spot throughout your image set you can manually create a black 'mask' image with a white hole in the middle, then overlay it on the final inverted image.
You can easily make the mask image using your favorite image editor's magic wand tool.
I made this1 by also expanding the circle inwards by one pixel to take into account some of the pixels the magic wand tool couldn't catch.
You would then use the mask image like this:
mask = cv2.imread('/path/to/mask.png')
masked = cv2.bitwise_and(inverted, inverted, mask=mask)
Method 2
If the circle does NOT stay is the same spot throughout your entire image set you can try to make the mask from all the fully black pixels in your original image. This assumes that the 'sample' itself (the thing with the cracks) does not contain fully black pixels. Although this will result in the text on the bottom left to be left white.
# make all the non black pixels white
_,mask = cv2.threshold(gray,1,255,cv2.THRESH_BINARY)
1 The original is not the same size as your inverted image and thus the mask I made won't actually fit, you're gonna have to make it yourself.

Detect very small circle and get their information (Opencv)

I am trying to detect each small circle (it is the bead part of the radial tires from the cross-sectional image)located as shown in the image and get their information(optional). To improve the detection process I have performed a few image processing steps including median blurring and binary thresholding (the general binary thresholding and inverse binary thresholding). I am using HoughCicle transform to detect the circles however I stucked and couldn't be able to detect it yet.
Please, any suggestions? Thank you very much.
This is the original image
Cropped image (it is the area where the circle I want to detect appear)
This is the binary image output and cropped it to remove the unnecessary part
As such, I'm trying to detect each circle from the binary image shown in the image below like marked in red.
Final preprocessed image
I used the following code
import cv2
import numpy as np
import os
import matplotlib.pyplot as plt
############# preprocessing ##################
img = cv2.imread('BD-2021.png')
median_5 = cv2.medianBlur(img, 5) # median filtering
image_masked = cv2.cvtColor(median_5, cv2.COLOR_BGR2GRAY) # converting to grayscael
res,thresh_img=cv2.threshold(image_masked,230,255,cv2.THRESH_BINARY_INV) # inverse binary
# res,thresh_img_b=cv2.threshold(image_masked,200,255,cv2.THRESH_BINARY) # global binary
height, width = thresh_img.shape
img_crop = thresh_img[int(0.7*height):height,:width]
# reverse_thresh = cv2.cvtColor(thresh_img,cv2.COLOR_GRAY2BGR)
############# Hough circle detection ##################
c = cv2.HoughCircles(img_crop, cv2.HOUGH_GRADIENT,
minDist=2, dp=1, param1=70,
param2=12, minRadius=0,maxRadius=5)
c = np.uint16(np.around(c))
for i in c[0,:]:
# draw the outer circle
cv2.circle(img,(i[0],i[1]),i[2],(0,255,0),2)
# cv2.circle(reverse_thresh,(i[0],i[1]),i[2],(0,255,0),2)
# draw the center of the circle
cv2.circle(img,(i[0],i[1]),2,(0,0,255),3)
# cv2.circle(reverse_thresh,(i[0],i[1]),2,(0,0,255),3)
cv2.imshow('circle detected',img)
cv2.waitKey(0)
cv2.destroyAllWindows()
I would appreciate for any recommendation ? Thank you once again.
I would recommend using the Blob Detection Algorithm. This algorithm is the first part of the SIFT descriptor and is fairly easy to implement. The blob detection algorithm involves finding blobs in the picture using the Laplacian of Gaussian convolution (LoG) which can be approximated as a difference of gaussians. A good explanation on the scale space and how to implement the blob detection is given here.

How can I use Blob detection to Isolate region in an image

I want to measure area of land in an aerial view image, so I was adviced to first use blob detection to Isolate region and threshold the image. Here is what I have done, but I am not sure if this is correct.
img = cv2.imread('landarea.jpg', cv2.IMREAD_COLOR)
# Set up the detector with default parameters.
detector = cv2.SimpleBlobDetector_create()
# Detecting blobs.
keypoints = detector.detect(img)
# Draw detected blobs as red circles.
# cv2.DRAW_MATCHES_FLAGS_DRAW_RICH_KEYPOINTS ensures the size of the circle corresponds to the size
im_with_keypoints = cv2.drawKeypoints(img, keypoints, np.array([]), (0, 0,
255),cv2.DRAW_MATCHES_FLAGS_DRAW_RICH_KEYPOINTS)
# Show keypoints
print(im_with_keypoints.size)
# plt.show()
cv2.imshow("Blob",im_with_keypoints )
cv2.waitKey(0)
cv2.destroyAllWindows()
# Convert to gray
gray = cv2.cvtColor(im_with_keypoints, cv2.COLOR_BGR2GRAY)
#Threshold the image
ret3,th3 = cv2.threshold(gray,0,255,cv2.THRESH_BINARY+cv2.THRESH_OTSU)
titles = ["Otsu's Thresholding"]
images = [th3]
plt.figure(figsize=(15, 10))
for i in range(1):
plt.subplot(1,1,i+1), plt.imshow(images[i], 'gray')
plt.title(titles[i])
plt.xticks([]), plt.yticks([])
plt.show()
Image Link
In summary this is what I am trying to achieve
Task : Land Area Measurement:
I am current working on getting the Area, Width and Height Measurement of a Land from aerial Mapping Images. Steps taken to achieve this are listed below:
I was advice that it's better to write a Python code from scratch to do my Image processing.
Also from SO, I was advice to use a blob detector to isolates regions, threshold my image and count the number of white pixels. Then I can calibrate the dimensions of the image with ground truth dimensions.
I have been able to detect blobs, threshold the image and I have also been able to get the count of white pixels. My major challenge is on the last two steps and how to get the measurement from this steps.
Also a friend said that normally the shape of any photo could be square, rectangle, etc. So the area might not vary if I measure area with photos.
I do not think blob detection is going to work well. You would need to threshold the image in some way to separate the land area from everything else. From what part of the image do you want to get the area?

Obtain features inside image and remove boundary

I want to detect features inside an image (retina scan). The image consists of a retina scan inside a rectangular box with black background.
I am working with Python 3.6, and I am using Canny Edge Detection to detect features inside the image. I understand that the algorithm for canny edge detection uses edge gradients to find edges. While Canny Edge Detection gives me features inside the retina scan for a proper choice of threshold values, it always keeps the circular rim between the retina scan and the black background in the output image.
In the output image, I want to have only the features inside the image (retina scan), and not the outer rim. How can I do this? I am searching for solutions which use Python. I am also open to the use of techniques other than Canny Edge Detection if they help to achieve the required task.
Below is the actual image, and the output image that I get from Canny Edge Detection.
Below is the circular rim that I am talking about (highlighted in red.)
Given below is the expected output image:
My code is given underneath:
import cv2
import matplotlib.pyplot as plt
from matplotlib.pyplot import imread as imread
plt.figure(1)
img_DR = cv2.imread('img.tif',0)
edges_DR = cv2.Canny(img_DR,20,40)
plt.subplot(121),plt.imshow(img_DR)
plt.title('Original Image'), plt.xticks([]), plt.yticks([])
plt.subplot(122),plt.imshow(edges_DR,cmap = 'gray')
plt.title('Edge Image'), plt.xticks([]), plt.yticks([])
plt.show()
You can find the image used in this code here.
Thanks in advance.
You could fix this in 3 steps:
1) Threshold your input image at a very low intensity, so your retina is the only foreground region. Looking at your image this should work just fine since you have no real black areas in your foreground region:
img = cv2.imread('retina.jpg')
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
ret,bin = cv2.threshold(gray,5,255,cv2.THRESH_BINARY)
2) Use erosion to remove a small margin from your foreground, you want to remove the part where your outer rim artifacts develop after you apply canny:
kernel = np.ones((5,5),np.uint8)
erosion = cv2.erode(bin,kernel,iterations = 1)
(visualised in red: the eroded area)
3) Use this eroded image as a binary mask on your current result image. This will remove the outer border while keeping all inner structures intact:
edges_DR = cv2.Canny(img,20,40)
result = cv2.bitwise_and(edges_DR,edges_DR,mask = erosion)
You may have to experiment with the kernel size for the erosion to remove the full border but only the border. But generally, this should produce really good and robust results. Even if the orientation or size of your scan is not consistent.

Opencv thresholding code working in python2.7(Windows) but not working Raspberry Pi

I have this python code that applies a series thresholding to an image of an eye so that it would it would be able to detect the pupil. I wrote this code using python 2.7 in windows 10. It actually worked great since I was able to get my desired output.
Here is the code that I wrote in windows 10:
import cv2
import numpy as np
from matplotlib import pyplot as plt
img = cv2.imread('C:\Users\User\Documents\module4\input\left.jpg',0)
image = cv2.medianBlur(img,5)
#Apply Adaptive Threshold with Laplacian
th = cv2.adaptiveThreshold(image,255,cv2.ADAPTIVE_THRESH_MEAN_C,
cv2.THRESH_BINARY,11,2)
laplacian = cv2.Laplacian(th,cv2.CV_64F)
cv2.imwrite('C:\Users\User\Documents\module4\output\output1.jpg', laplacian)
#Apply Inverse Binary Threshold
binthresh = cv2.imread('C:\Users\User\Documents\module4\output\output1.jpg',0)
ret,thresh2 = cv2.threshold(laplacian,127,255,cv2.THRESH_BINARY_INV)
cv2.imwrite('C:\Users\User\Documents\module4\output\output2.jpg', thresh2)
#Apply First Otsu's Threshold
otsuthresh1 = cv2.imread('C:\Users\User\Documents\module4\output\output2.jpg',0)
blur = cv2.GaussianBlur(otsuthresh1,(5,5),0)
ret3,th3 = cv2.threshold(blur,0,255,cv2.THRESH_BINARY+cv2.THRESH_OTSU)
cv2.imwrite('C:\Users\User\Documents\module4\output\output3.jpg', th3)
#Apply Gaussian Blur
gaussblur = cv2.imread('C:\Users\User\Documents\module4\output\output3.jpg',0)
blur2 = cv2.GaussianBlur(gaussblur,(5,5),0)
cv2.imwrite('C:\Users\User\Documents\module4\output\output4.jpg', blur2)
#Apply Second Otsu's Threshold
otsuthresh2 = cv2.imread('C:\Users\User\Documents\module4\output\output4.jpg',0)
blur3 = cv2.GaussianBlur(otsuthresh2,(5,5),0)
ret4,th4 = cv2.threshold(blur3,0,255,cv2.THRESH_BINARY+cv2.THRESH_OTSU)
#Apply Circular Hough Transform
circles = cv2.HoughCircles(th4,cv2.HOUGH_GRADIENT,1,20,param1=50,param2=30,minRadius=0,maxRadius=0)
circles = np.uint16(np.around(circles))
for i in circles[0,:]:
# draw the outer circle
cv2.circle(th4,(i[0],i[1]),i[2],(100,100,0),2)
# draw the center of the circle
cv2.circle(th4,(i[0],i[1]),2,(0,50,100),3)
cv2.imshow('combined', th4)
cv2.imwrite('C:\Users\User\Documents\module4\output\output5.jpg', th4)
cv2.waitKey(0)
cv2.destroyAllWindows()
Here is a screenshot of all the outputs of the code (including the original input image):
I tried running this same code in my raspberry pi, I just changed the file path of the input image as well as where to store the output images.
Here is the code that I ran in my Raspberry Pi:
import cv2
import numpy as np
from matplotlib import pyplot as plt
img = cv2.imread('/home/pi/IPD/images/image1.jpg',0)
image = cv2.medianBlur(img,5)
#Apply Adaptive Threshold with Laplacian
th = cv2.adaptiveThreshold(image,255,cv2.ADAPTIVE_THRESH_MEAN_C,
cv2.THRESH_BINARY,11,2)
laplacian = cv2.Laplacian(th,cv2.CV_64F)
#Apply Inverse Binary Threshold
binthresh = cv2.imread('/home/pi/IPD/temp/output1.jpg',0)
ret,thresh2 = cv2.threshold(binthresh,127,255,cv2.THRESH_BINARY_INV)
cv2.imwrite('/home/pi/IPD/temp/output2.jpg', thresh2)
#Apply First Otsu's Threshold
otsuthresh1 = cv2.imread('/home/pi/IPD/temp/output2.jpg',0)
blur = cv2.GaussianBlur(otsuthresh1,(5,5),0)
ret3,th3 = cv2.threshold(blur,0,255,cv2.THRESH_BINARY+cv2.THRESH_OTSU)
cv2.imwrite('/home/pi/IPD/temp/output3.jpg', th3)
#Apply Gaussian Blur
gaussblur = cv2.imread('/home/pi/IPD/temp/output3.jpg',0)
blur2 = cv2.GaussianBlur(gaussblur,(5,5),0)
cv2.imwrite('/home/pi/IPD/temp/output4.jpg', blur2)
#Apply Second Otsu's Threshold
otsuthresh2 = cv2.imread('C/home/pi/IPD/temp/output4.jpg',0)
blur3 = cv2.GaussianBlur(otsuthresh2,(5,5),0)
ret4,th4 = cv2.threshold(blur3,0,255,cv2.THRESH_BINARY+cv2.THRESH_OTSU)
#Apply Circular Hough Transform
circles = cv2.HoughCircles(th4,cv2.HOUGH_GRADIENT,1,20,param1=50,param2=30,minRadius=0,maxRadius=0)
circles = np.uint16(np.around(circles))
for i in circles[0,:]:
# draw the outer circle
cv2.circle(th4,(i[0],i[1]),i[2],(100,100,0),2)
# draw the center of the circle
cv2.circle(th4,(i[0],i[1]),2,(0,50,100),3)
cv2.imshow('combined', th4)
cv2.imwrite('/home/pi/IPD/images/final.jpg', th4)
cv2.waitKey(0)
cv2.destroyAllWindows()
However I get the following error:
Traceback (most recent call last):
File "/home/pi/IPD/mod4.py", line 18, in
ret,thresh2 = cv2.threshold(binthresh,127,255,cv2.THRESH_BINARY_INV)
error: /build/opencv-ISmtkH/opencv-2.4.9.1+dfsg/modules/core/src/matrix.cpp:269: error: (-215) m.dims >= 2 in function Mat
Actually, I've also encountered this error when I first wrote the code in windows 10 but I solved it by writing the newly thresholded image and just loading it again (as you can see in my code. I know it's an inefficient way) so that I can apply a new threshold to it. I've tried searching for possible explanations why this might be and I figured it has something to do with how many channels the signal I'm inputting is (I think). However, I'm still new to using opencv and image processing in general and I really don't understand the concept really that well (even though I've already researched it).
If you guys can help me and point me in the right direction, I would be really grateful. And also if you guys can suggest how I can avoid storing the newly thresholded image and loading it again (which is really an inefficient way of going about this) without causing any errors, I would really, really appreciate it.
I spotted 2 errors, I hope those are the only 2.
1) In your first code you have:
laplacian = cv2.Laplacian(th,cv2.CV_64F)
cv2.imwrite('C:\Users\User\Documents\module4\output\output1.jpg', laplacian)
#Apply Inverse Binary Threshold
binthresh = cv2.imread('C:\Users\User\Documents\module4\output\output1.jpg',0)
ret,thresh2 = cv2.threshold(laplacian,127,255,cv2.THRESH_BINARY_INV)
here you are doing the laplacian operator and saving it in a CV_64F image (doubles), but threshold ONLY takes CV_8U or CV_32F. Here you have two options, one is to change this 64F to 32F or to use the function normalize and convert it to 8U image. Something like:
cv2.normalize(laplacian, output1, 0, 255, cv2.NORM_MINMAX, cv2.CV_8U)
2) In the second code you are missing:
cv2.imwrite('C:\Users\User\Documents\module4\output\output1.jpg', laplacian)
So, you are not saving such an image, thus you are not loading it either... no image, an error jumps out.
General suggestions, always use imshow to see what is going on until what point. Use relative paths for the saving and loading of the temp images, this way you only change the input path.

Categories

Resources