Reputation: 69
I have a ton of images like the one shown below. I am trying to detect nanoparticles from transmission electron microscopy images which I have successfully achieved using OpenCV-Python. The image is just a crop of the bottom left corner of a bigger transmission electron microscopy image. I would like to extract the length of the white colored line segment under the '100 nm'. Currently, I am manually measuring the length of this line and I would like to automate this. I think this could be done with OpenCV-Python by contouring, but I have lot of other features which are also contoured in the image. I am new to Python and I am not very sure on how to implement this or if this can be implemented at all. My idea about what the code should do:
Any suggestions, directions or 'where to start' comments are greatly welcome and appreciated.
The line is segment is very unique in its features and there is only one line in every image.
Upvotes: 1
Views: 6729
Reputation: 7985
One approach is using the probabilistic Hough line method for detecting the line.
The result will be:
Now the problem is multiple lines are detected. We know their coordinates. If we print them all:
Pixel Length: 296
Pixel Length: 197
Pixel Length: 308
Pixel Length: 175
Pixel Length: 292
Pixel Length: 229
Pixel Length: 103
Pixel Length: 109
Since there are a lot of lengths detected, maybe finding its average is meaningful:
Average Pixel Length: 109 pixel
Though I'm not sure how to convert 109 pixel to 100nm.
import cv2
import numpy as np
img = cv2.imread("4HQtp.png")
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
edges = cv2.Canny(gray, 50, 150, apertureSize=3)
base = cv2.HoughLinesP(edges, 1, np.pi / 180, 80, minLineLength=1, maxLineGap=6)
pixel_array = []
pixel_length = 0
if base is not None:
for line in base:
x1, y1, x2, y2 = line[0]
cv2.line(img, (x1, y1), (x2, y2), (0, 0, 255), 2)
pixel_length = np.abs(x2 - x1)
pixel_array.append(pixel_length)
print("Pixel Length: {}".format(pixel_length))
cv2.imshow("img", img)
cv2.imwrite("hough_img.png", img)
cv2.waitKey(0)
print("Average Pixel Length: {:.0f} pixel".format(np.average(pixel_array)))
Upvotes: 1
Reputation: 4049
You can binarize your image to find contour of the scale segment:
scale = cv2.imread('scalenanometer.png', cv2.IMREAD_COLOR)
scale_gray = cv2.cvtColor(scale, cv2.COLOR_BGR2GRAY)
# adjust the second value of the next line to tune the detection
ret, thresh = cv2.threshold(scale_gray, 210, 255, cv2.THRESH_BINARY)
contours, hierarchy = cv2.findContours(thresh, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
# filter noisy detection
contours = [c for c in contours if cv2.contourArea(c) > 100]
# sort from by (y, x)
contours.sort(key=lambda c: (cv2.boundingRect(c)[1], cv2.boundingRect(c)[0]))
# work on the segment
cv2.rectangle(scale, cv2.boundingRect(contours[-1]), (0,255,0), 2)
x,y,w,h = cv2.boundingRect(contours[-1])
print(x,y,w,h) # x,y: (39 152) w,h: [304 21]
If you want to detect the value, you can use tesseract ocr.
Upvotes: 3