I found a lot of questions regarding finding βthingsβ in images using openCV et al. In Python, but so far I have not been able to put them together to reliably solve my problem.
I'm trying to use computer vision to count the tiny pieces of electronics on the surface. The idea is that I dump the details onto solid colored paper, click the image, and the software tells me how many items are in it.
βthingsβ differ from one image to another, but will always be the same in any image. It seems I can manually adjust the settings for things like hue / saturation for a specific part, but every time a change to a new part requires a change in settings.
My current, semi-functional code is posted below:
import imutils
import numpy
import cv2
import sys
def part_area(contours, round=10):
"""Finds the mode of the contour area. The idea is that most of the parts in an image will be separated and that
finding the most common area in the list of areas should provide a reasonable value to approximate by. The areas
are rounded to the nearest multiple of 200 to reduce the list of options."""
areas = [cv2.contourArea(contour) for contour in contours]
threshold = (max(areas) - min(areas)) / 100
thresholded = [area for area in areas if area > threshold]
rounded = [int((area + (round / 2)) / round) * round for area in thresholded]
cleaned = [area for area in rounded if area != 0]
counts = {}
for area in cleaned:
if area not in counts:
counts[area] = 0
counts[area] += 1
above = []
for area, count in counts.iteritems():
if count > 2:
for _ in range(count):
above.append(area)
average = sum(above) / len(above)
return average
def find_hue_mode(hsv):
"""Given an HSV image as an input, compute the mode of the list of hue values to find the most common hue in the
image. This is used to determine the center for the background color filter."""
pixels = {}
for row in hsv:
for pixel in row:
hue = pixel[0]
if hue not in pixels:
pixels[hue] = 0
pixels[hue] += 1
counts = sorted(pixels.keys(), key=lambda key: pixels[key], reverse=True)
return counts[0]
if __name__ == "__main__":
image = cv2.imread(sys.argv[1])
hsv = cv2.cvtColor(image, cv2.COLOR_BGR2HSV)
center = find_hue_mode(hsv)
print 'Center Hue:', center
lower = numpy.array([center - 10, 50, 50])
upper = numpy.array([center + 10, 255, 255])
mask = cv2.inRange(hsv, lower, upper)
inverted = cv2.bitwise_not(mask)
blurred = cv2.GaussianBlur(inverted, (5, 5), 0)
edged = cv2.Canny(blurred, 50, 100)
dilated = cv2.dilate(edged, None, iterations=1)
eroded = cv2.erode(dilated, None, iterations=1)
contours = cv2.findContours(eroded.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
contours = contours[0] if imutils.is_cv2() else contours[1]
part_area = part_area(contours)
threshold = part_area * 0.5
part_count = 0
for contour in contours:
if cv2.contourArea(contour) < threshold:
continue
part_count += int((cv2.contourArea(contour) / part_area) + 0.1)
epsilon = 0.1 * cv2.arcLength(contour, True)
approx = cv2.approxPolyDP(contour, epsilon, True)
cv2.drawContours(image, [approx], -1, (0, 255, 0), 2)
print 'Part Count:', part_count
cv2.imshow("Image", image)
cv2.waitKey(0)
Here is an example of the type of input image I'm using:
or this:

And currently I am getting the following results:

The results clearly show that the script is having difficulty defining certain parts, and this is the true Achilles' heel, it seems when the parts touch each other.
So, my question / task: what can I do to increase the reliability of this script?
The script must be integrated into an existing Python tool, so I'm looking for a solution using Python. The solution should not be pure Python, as I am ready to install any third-party libraries.