objective c - started - OpenCV detecta blobs en la imagen.
managewindows azure (3)
Aquí está el código que utilicé:
import cv2
from sympy import Point, Ellipse
import numpy as np
x1=''C://Users//Desktop//python//stack_over_flow//XsXs9.png''
image = cv2.imread(x1,0)
image1 = cv2.imread(x1,1)
x,y=image.shape
median = cv2.GaussianBlur(image,(9,9),0)
median1 = cv2.GaussianBlur(image,(21,21),0)
a=median1-median
c=255-a
ret,thresh1 = cv2.threshold(c,12,255,cv2.THRESH_BINARY)
kernel=np.ones((5,5),np.uint8)
dilation = cv2.dilate(thresh1,kernel,iterations = 1)
kernel=np.ones((5,5),np.uint8)
opening = cv2.morphologyEx(dilation, cv2.MORPH_OPEN, kernel)
cv2.imwrite(''D://test12345.jpg'',opening)
ret,contours,hierarchy = cv2.findContours(opening,cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_SIMPLE)
c=np.size(contours[:])
Blank_window=np.zeros([x,y,3])
Blank_window=np.uint8(Blank_window)
for u in range(0,c-1):
if (np.size(contours[u])>200):
ellipse = cv2.fitEllipse(contours[u])
(center,axes,orientation) =ellipse
majoraxis_length = max(axes)
minoraxis_length = min(axes)
eccentricity=(np.sqrt(1-(minoraxis_length/majoraxis_length)**2))
if (eccentricity<0.8):
cv2.drawContours(image1, contours, u, (255,1,255), 3)
cv2.imwrite(''D://marked.jpg'',image1)
Aquí el problema es encontrar un objeto casi circular. Esta solución simple se basa en encontrar la excentricidad para cada contorno. Tales objetos que se detectan es la gota de agua.
Necesito encontrar (y dibujar rect) / obtener blobs de radio máximo y mínimo en la imagen. (muestras abajo)
el problema es encontrar los filtros correctos para la imagen que permitirán que la transformación Canny
o Threshold
resalte las manchas. Luego voy a usar findContours
para encontrar los rectángulos.
He intentado:
Threshold
- con diferente nivelblur->erode->erode->grayscale->canny
Cambiar tono de imagen con variedad de "lineas".
y ect. El mejor resultado fue detectar una pieza (20-30%) de blob. y esta información no permite dibujar rect alrededor de blob. Además, gracias a las sombras, no relacionadas con blob se detectaron puntos, por lo que también evita detectar el área.
Según tengo entendido, necesito encontrar un contador que tenga un contraste fuerte (no suave como en la sombra). ¿Hay alguna manera de hacer eso con openCV?
Actualizar
casos por separado: imagen 1 , imagen 2 , imagen 3 , imagen 4 , imagen 5 , imagen 6 , imagen 7 , imagen 8 , imagen 9 , imagen 10 , imagen 11 , imagen 12
Una actualización más
Creo que la burbuja tiene el área de contraste en el borde. Entonces, he intentado hacer que el borde sea más fuerte: he creado 2 gray scale Mat: A and B
, aplique Gaussian blur
para el segundo - B
(para reducir el ruido un poco) , luego he hecho algunos cálculos: alrededor de cada píxel y encuentra la diferencia máxima entre Xi,Yi
de ''A'' y puntos cercanos de ''B'':
y aplicar la diferencia max
a Xi,Yi
. así que entiendo algo así:
¿Estoy en el camino correcto? por cierto, ¿puedo llegar a algo así como a través de métodos OpenCV?
Update Image Denoising ayuda a reducir el ruido, Sobel
: para resaltar los contornos, luego threshold
+ findContours
y custome convexHull
tiene algo parecido a lo que estoy buscando pero no es bueno para algunos blobs.
Dado que hay grandes diferencias entre las imágenes de entrada, el algoritmo debe poder adaptarse a la situación. Como Canny se basa en la detección de altas frecuencias, mi algoritmo trata la nitidez de la imagen como el parámetro utilizado para la adaptación de preprocesamiento. No quería pasar una semana resolviendo las funciones para todos los datos, así que apliqué una función lineal simple basada en 2 imágenes y luego la probé con una tercera. Aquí están mis resultados:
Tenga en cuenta que este es un enfoque muy básico y solo está demostrando un punto. Necesitará experimentos, pruebas y refinación. La idea es usar Sobel y sumar todos los píxeles adquiridos. Eso, dividido por el tamaño de la imagen, debería darle una estimación básica de alta frecuencia. Respuesta de la imagen. Ahora, experimentalmente, encontré valores de clipLimit para el filtro CLAHE que funcionan en 2 casos de prueba y encontré una función lineal que conecta la alta frecuencia. Respuesta de la entrada con un filtro CLAHE, dando buenos resultados.
sobel = get_sobel(img)
clip_limit = (-2.556) * np.sum(sobel)/(img.shape[0] * img.shape[1]) + 26.557
Esa es la parte adaptativa. Ahora para los contornos. Me tomó un tiempo encontrar una forma correcta de filtrar el ruido. Me conformé con un simple truco: usando contornos para encontrar dos veces. Primero lo uso para filtrar los contornos innecesarios y ruidosos. Luego continúo con algo de magia morfológica para terminar con las manchas correctas para los objetos que se detectan (más detalles en el código). El paso final es filtrar los rectángulos delimitadores en función de la media calculada, ya que, en todas las muestras, las manchas tienen un tamaño relativamente similar.
import cv2
import numpy as np
def unsharp_mask(img, blur_size = (5,5), imgWeight = 1.5, gaussianWeight = -0.5):
gaussian = cv2.GaussianBlur(img, (5,5), 0)
return cv2.addWeighted(img, imgWeight, gaussian, gaussianWeight, 0)
def smoother_edges(img, first_blur_size, second_blur_size = (5,5), imgWeight = 1.5, gaussianWeight = -0.5):
img = cv2.GaussianBlur(img, first_blur_size, 0)
return unsharp_mask(img, second_blur_size, imgWeight, gaussianWeight)
def close_image(img, size = (5,5)):
kernel = np.ones(size, np.uint8)
return cv2.morphologyEx(img, cv2.MORPH_CLOSE, kernel)
def open_image(img, size = (5,5)):
kernel = np.ones(size, np.uint8)
return cv2.morphologyEx(img, cv2.MORPH_OPEN, kernel)
def shrink_rect(rect, scale = 0.8):
center, (width, height), angle = rect
width = width * scale
height = height * scale
rect = center, (width, height), angle
return rect
def clahe(img, clip_limit = 2.0):
clahe = cv2.createCLAHE(clipLimit=clip_limit, tileGridSize=(5,5))
return clahe.apply(img)
def get_sobel(img, size = -1):
sobelx64f = cv2.Sobel(img,cv2.CV_64F,2,0,size)
abs_sobel64f = np.absolute(sobelx64f)
return np.uint8(abs_sobel64f)
img = cv2.imread("blobs4.jpg")
# save color copy for visualizing
imgc = img.copy()
# resize image to make the analytics easier (a form of filtering)
resize_times = 5
img = cv2.resize(img, None, img, fx = 1 / resize_times, fy = 1 / resize_times)
img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# use sobel operator to evaluate high frequencies
sobel = get_sobel(img)
# experimentally calculated function - needs refining
clip_limit = (-2.556) * np.sum(sobel)/(img.shape[0] * img.shape[1]) + 26.557
# don''t apply clahe if there is enough high freq to find blobs
if(clip_limit < 1.0):
clip_limit = 0.1
# limit clahe if there''s not enough details - needs more tests
if(clip_limit > 8.0):
clip_limit = 8
# apply clahe and unsharp mask to improve high frequencies as much as possible
img = clahe(img, clip_limit)
img = unsharp_mask(img)
# filter the image to ensure edge continuity and perform Canny
# (values selected experimentally, using trackbars)
img_blurred = (cv2.GaussianBlur(img.copy(), (2*2+1,2*2+1), 0))
canny = cv2.Canny(img_blurred, 35, 95)
# find first contours
_, cnts, _ = cv2.findContours(canny.copy(), cv2.RETR_LIST, cv2.CHAIN_APPROX_SIMPLE)
# prepare black image to draw contours
canvas = np.ones(img.shape, np.uint8)
for c in cnts:
l = cv2.arcLength(c, False)
x,y,w,h = cv2.boundingRect(c)
aspect_ratio = float(w)/h
# filter "bad" contours (values selected experimentally)
if l > 500:
continue
if l < 20:
continue
if aspect_ratio < 0.2:
continue
if aspect_ratio > 5:
continue
if l > 150 and (aspect_ratio > 10 or aspect_ratio < 0.1):
continue
# draw all the other contours
cv2.drawContours(canvas, [c], -1, (255, 255, 255), 2)
# perform closing and blurring, to close the gaps
canvas = close_image(canvas, (7,7))
img_blurred = cv2.GaussianBlur(canvas, (8*2+1,8*2+1), 0)
# smooth the edges a bit to make sure canny will find continuous edges
img_blurred = smoother_edges(img_blurred, (9,9))
kernel = np.ones((3,3), np.uint8)
# erode to make sure separate blobs are not touching each other
eroded = cv2.erode(img_blurred, kernel)
# perform necessary thresholding before Canny
_, im_th = cv2.threshold(eroded, 50, 255, cv2.THRESH_BINARY)
canny = cv2.Canny(im_th, 11, 33)
# find contours again. this time mostly the right ones
_, cnts, _ = cv2.findContours(canny.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
# calculate the mean area of the contours'' bounding rectangles
sum_area = 0
rect_list = []
for i,c in enumerate(cnts):
rect = cv2.minAreaRect(c)
_, (width, height), _ = rect
area = width*height
sum_area += area
rect_list.append(rect)
mean_area = sum_area / len(cnts)
# choose only rectangles that fulfill requirement:
# area > mean_area*0.6
for rect in rect_list:
_, (width, height), _ = rect
box = cv2.boxPoints(rect)
box = np.int0(box * 5)
area = width * height
if(area > mean_area*0.6):
# shrink the rectangles, since the shadows and reflections
# make the resulting rectangle a bit bigger
# the value was guessed - might need refinig
rect = shrink_rect(rect, 0.8)
box = cv2.boxPoints(rect)
box = np.int0(box * resize_times)
cv2.drawContours(imgc, [box], 0, (0,255,0),1)
# resize for visualizing purposes
imgc = cv2.resize(imgc, None, imgc, fx = 0.5, fy = 0.5)
cv2.imshow("imgc", imgc)
cv2.imwrite("result3.png", imgc)
cv2.waitKey(0)
En general, creo que es un problema muy interesante, un poco demasiado grande para ser respondido aquí. El enfoque que presenté debe tratarse como una señal de tráfico, no como una solución completa. La idea básica es:
Preprocesamiento adaptativo.
Encontrar contornos dos veces: para filtrar y luego para la clasificación real.
Filtrado de las manchas en función de su tamaño medio.
Gracias por la diversión y buena suerte!
Tengo una solución parcial en su lugar.
PRIMERO
Inicialmente convertí la imagen al espacio de color HSV y jugué con el canal de valores . Al hacerlo me encontré con algo único. En casi todas las imágenes, las gotas tienen un pequeño reflejo de luz. Esto se destacó claramente en el canal de valor .
Al invertir esto pude obtener lo siguiente:
Muestra 1:
Muestra 2:
Muestra 3:
SEGUNDO
Ahora tenemos que extraer la ubicación de esos puntos. Para ello realicé la detección de anomalías en el canal de valor invertido obtenido. Por anomalía me refiero al punto negro presente en ellos.
Para hacer esto, calculé la mediana del canal de valor invertido. Asigné un valor de píxel dentro del 70% por encima y por debajo de la mediana para ser tratado como píxeles normales. Pero cada valor de píxel que se encuentra más allá de este rango es una anomalía. Los puntos negros encajan perfectamente allí.
Muestra 1:
Muestra 2:
Muestra 3:
No resultó bien para pocas imágenes.
Como puede ver, el punto negro se debe al reflejo de la luz que es exclusivo de las gotas de agua. Otros bordes circulares pueden estar presentes en la imagen, pero la reflexión distingue la gota de esos bordes.
TERCERO
Ahora que tenemos la ubicación de estos puntos negros, podemos realizar la diferencia de Gaussians (DoG) (también mencionada en la actualización de la pregunta) y obtener información relevante del borde. Si la ubicación obtenida de los puntos negros se encuentra dentro de los bordes descubiertos, se dice que es una gota de agua.
Descargo de responsabilidad: este método no funciona para todas las imágenes. Puedes añadir tus sugerencias a esto.