rotate - opencv python tutorial pdf
Resultados no confiables con cv2.HoughCircles (1)
Tengo un video con 5 gotas de aceite, y estoy tratando de usar cv2.HoughCircles para encontrarlos.
Este es mi código:
import cv, cv2
import numpy as np
foreground1 = cv2.imread("foreground1.jpg")
vid = cv2.VideoCapture("NB14.avi")
cv2.namedWindow("video")
cv2.namedWindow("canny")
cv2.namedWindow("blur")
while True:
ret, frame = vid.read()
subtract1 = cv2.subtract( foreground1, frame)
framegrey1 = cv2.cvtColor(subtract1, cv.CV_RGB2GRAY)
blur = cv2.GaussianBlur(framegrey1, (0,0), 2)
circles = cv2.HoughCircles(blur, cv2.cv.CV_HOUGH_GRADIENT, 2, 10, np.array([]), 40, 80, 5, 100)
if circles is not None:
for c in circles[0]:
cv2.circle(frame, (c[0],c[1]), c[2], (0,255,0),2)
edges = cv2.Canny( blur, 40, 80 )
cv2.imshow("video", frame)
cv2.imshow("canny", edges)
cv2.imshow("blur", blur)
key = cv2.waitKey(30)
Yo diría que el detector de borde astuto se ve muy bien, mientras que los resultados de la transformada son muy inestables, cada cuadro proporcionará resultados diferentes.
Ejemplo:
He estado jugando con los parámetros y, sinceramente, no sé cómo obtener resultados más estables.
Inicialmente pensé que no habría superposición en sus gotitas de aceite, pero las hay. Entonces, Hough podría usar un buen método para usar aquí, pero he tenido una mejor experiencia al combinar RANSAC con él. Sugeriría explorar eso, pero aquí proporcionaré algo diferente de eso.
En primer lugar, no pude realizar la resta de fondo que haces ya que no tenía esta imagen "foreground1.jpg" (por lo que los resultados pueden mejorarse fácilmente). Tampoco me importó dibujar círculos, pero puedes hacerlo, simplemente dibujo el borde de los objetos que considero círculo.
Entonces, primero supongamos que no hay superposición. Luego, encontrar los bordes en su imagen (fácil), binarizar la respuesta del detector de bordes mediante Otsu, rellenar los agujeros y finalmente medir la circularidad es suficiente. Ahora bien, si hay superposiciones, podemos usar la transformación de la Cuenca hidrográfica combinada con la Transformada de distancia para separar las gotitas. El problema entonces es que no obtendrás objetos realmente circulares, y eso no me importó demasiado, pero puedes adaptarte a eso.
En el siguiente código, también tuve que usar scipy
para etiquetar componentes conectados (importante para construir el marcador para la Cuenca), ya que falta OpenCV en eso. El código no es exactamente corto, pero debe ser fácil de entender. Además, dado el código actual completo, no hay necesidad de la verificación de circularidad porque después de la segmentación por Cuenca, solo quedan los objetos que busca. Por último, hay un seguimiento simplista basado en la distancia aproximada al centro del objeto.
import sys
import cv2
import math
import numpy
from scipy.ndimage import label
pi_4 = 4*math.pi
def segment_on_dt(img):
border = img - cv2.erode(img, None)
dt = cv2.distanceTransform(255 - img, 2, 3)
dt = ((dt - dt.min()) / (dt.max() - dt.min()) * 255).astype(numpy.uint8)
_, dt = cv2.threshold(dt, 100, 255, cv2.THRESH_BINARY)
lbl, ncc = label(dt)
lbl[border == 255] = ncc + 1
lbl = lbl.astype(numpy.int32)
cv2.watershed(cv2.cvtColor(img, cv2.COLOR_GRAY2RGB), lbl)
lbl[lbl < 1] = 0
lbl[lbl > ncc] = 0
lbl = lbl.astype(numpy.uint8)
lbl = cv2.erode(lbl, None)
lbl[lbl != 0] = 255
return lbl
def find_circles(frame):
frame_gray = cv2.cvtColor(frame, cv2.COLOR_RGB2GRAY)
frame_gray = cv2.GaussianBlur(frame_gray, (5, 5), 2)
edges = frame_gray - cv2.erode(frame_gray, None)
_, bin_edge = cv2.threshold(edges, 0, 255, cv2.THRESH_OTSU)
height, width = bin_edge.shape
mask = numpy.zeros((height+2, width+2), dtype=numpy.uint8)
cv2.floodFill(bin_edge, mask, (0, 0), 255)
components = segment_on_dt(bin_edge)
circles, obj_center = [], []
contours, _ = cv2.findContours(components,
cv2.RETR_LIST, cv2.CHAIN_APPROX_SIMPLE)
for c in contours:
c = c.astype(numpy.int64) # XXX OpenCV bug.
area = cv2.contourArea(c)
if 100 < area < 3000:
arclen = cv2.arcLength(c, True)
circularity = (pi_4 * area) / (arclen * arclen)
if circularity > 0.5: # XXX Yes, pretty low threshold.
circles.append(c)
box = cv2.boundingRect(c)
obj_center.append((box[0] + (box[2] / 2), box[1] + (box[3] / 2)))
return circles, obj_center
def track_center(objcenter, newdata):
for i in xrange(len(objcenter)):
ostr, oc = objcenter[i]
best = min((abs(c[0]-oc[0])**2+abs(c[1]-oc[1])**2, j)
for j, c in enumerate(newdata))
j = best[1]
if i == j:
objcenter[i] = (ostr, new_center[j])
else:
print "Swapping %s <-> %s" % ((i, objcenter[i]), (j, objcenter[j]))
objcenter[i], objcenter[j] = objcenter[j], objcenter[i]
video = cv2.VideoCapture(sys.argv[1])
obj_center = None
while True:
ret, frame = video.read()
if not ret:
break
circles, new_center = find_circles(frame)
if obj_center is None:
obj_center = [(str(i + 1), c) for i, c in enumerate(new_center)]
else:
track_center(obj_center, new_center)
for i in xrange(len(circles)):
cv2.drawContours(frame, circles, i, (0, 255, 0))
cstr, ccenter = obj_center[i]
cv2.putText(frame, cstr, ccenter, cv2.FONT_HERSHEY_COMPLEX, 0.5,
(255, 255, 255), 1, cv2.CV_AA)
cv2.imshow("result", frame)
cv2.waitKey(10)
if len(circles[0]) < 5:
print "lost something"
Esto funciona para todo tu video, y aquí hay dos ejemplos: