Estoy tratando de obtener las esquinas de la caja en la imagen. Las siguientes son imágenes de ejemplo, sus resultados de umbral y, a la derecha, después de la flecha, están los resultados que necesito. Es posible que también hayas visto estas imágenes antes en Slack porque estoy usando estas imágenes para mis preguntas de ejemplo en Slack.
El siguiente es el código que me permite llegar hasta la imagen del medio.
import cv2 import numpy as np img_file = 'C:/Users/box.jpg' img = cv2.imread(img_file, cv2.IMREAD_COLOR) img = cv2.blur(img, (5, 5)) hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV) h, s, v = cv2.split(hsv) thresh0 = cv2.adaptiveThreshold(s, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C, cv2.THRESH_BINARY_INV, 11, 2) thresh1 = cv2.adaptiveThreshold(v, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C, cv2.THRESH_BINARY_INV, 11, 2) thresh2 = cv2.adaptiveThreshold(v, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C, cv2.THRESH_BINARY_INV, 11, 2) thresh = cv2.bitwise_or(thresh0, thresh1) cv2.imshow('Image-thresh0', thresh0) cv2.waitKey(0) cv2.imshow('Image-thresh1', thresh1) cv2.waitKey(0) cv2.imshow('Image-thresh2', thresh2) cv2.waitKey(0)
¿Hay algún método en opencv que pueda hacerlo por mí? Probé dilatación cv2.dilate()
y erosión cv2.erode()
pero no funciona en mis casos. O si no, ¿cuáles podrían ser formas alternativas de hacerlo? Gracias
Versión astuta de la imagen... A la izquierda con umbral bajo y a la derecha con umbral alto
A continuación se muestra una implementación de Python del enfoque de @dhanushka
import cv2 import numpy as np # load color image im = cv2.imread('input.jpg') # smooth the image with alternative closing and opening # with an enlarging kernel morph = im.copy() kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (1, 1)) morph = cv2.morphologyEx(morph, cv2.MORPH_CLOSE, kernel) morph = cv2.morphologyEx(morph, cv2.MORPH_OPEN, kernel) kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (2, 2)) # take morphological gradient gradient_image = cv2.morphologyEx(morph, cv2.MORPH_GRADIENT, kernel) # split the gradient image into channels image_channels = np.split(np.asarray(gradient_image), 3, axis=2) channel_height, channel_width, _ = image_channels[0].shape # apply Otsu threshold to each channel for i in range(0, 3): _, image_channels[i] = cv2.threshold(~image_channels[i], 0, 255, cv2.THRESH_OTSU | cv2.THRESH_BINARY) image_channels[i] = np.reshape(image_channels[i], newshape=(channel_height, channel_width, 1)) # merge the channels image_channels = np.concatenate((image_channels[0], image_channels[1], image_channels[2]), axis=2) # save the denoised image cv2.imwrite('output.jpg', image_channels)
El código anterior no da buenos resultados si la imagen que está tratando son facturas (o tiene una gran cantidad de texto sobre un fondo blanco). Para obtener buenos resultados en este tipo de imágenes, elimine
gradient_image = cv2.morphologyEx(morph, cv2.MORPH_GRADIENT, kernel)
y pase morph
obj a la función split y elimine el símbolo ~
dentro del bucle for
Puede suavizar la imagen hasta cierto punto aplicando operaciones morfológicas alternativas de cierre y apertura con un elemento estructurante de ampliación. Aquí están las versiones original y suavizada.
Luego tome el gradiente morfológico de la imagen.
Luego aplique el umbral de Otsu a cada uno de los canales y fusione esos canales.
Si los tamaños de sus imágenes son diferentes (más grandes), es posible que desee cambiar algunos de los parámetros del código o cambiar el tamaño de las imágenes aproximadamente a los tamaños utilizados aquí. El código está en c++
pero no será difícil portarlo a python
.
/* load color image */ Mat im = imread(INPUT_FOLDER_PATH + string("2.jpg")); /* smooth the image with alternative closing and opening with an enlarging kernel */ Mat morph = im.clone(); for (int r = 1; r < 4; r++) { Mat kernel = getStructuringElement(MORPH_ELLIPSE, Size(2*r+1, 2*r+1)); morphologyEx(morph, morph, CV_MOP_CLOSE, kernel); morphologyEx(morph, morph, CV_MOP_OPEN, kernel); } /* take morphological gradient */ Mat mgrad; Mat kernel = getStructuringElement(MORPH_ELLIPSE, Size(3, 3)); morphologyEx(morph, mgrad, CV_MOP_GRADIENT, kernel); Mat ch[3], merged; /* split the gradient image into channels */ split(mgrad, ch); /* apply Otsu threshold to each channel */ threshold(ch[0], ch[0], 0, 255, CV_THRESH_BINARY | CV_THRESH_OTSU); threshold(ch[1], ch[1], 0, 255, CV_THRESH_BINARY | CV_THRESH_OTSU); threshold(ch[2], ch[2], 0, 255, CV_THRESH_BINARY | CV_THRESH_OTSU); /* merge the channels */ merge(ch, 3, merged);
No estoy seguro de qué tan robusta será esa solución, pero la idea es bastante simple. Los bordes del cuadro deben ser más pronunciados que todas las demás frecuencias altas en esas imágenes. Por lo tanto, el uso de un preprocesamiento básico debería permitir enfatizarlos.
Usé su código para hacer un prototipo, pero la búsqueda del contorno no tiene que ser el camino correcto. También lo siento por el enmascaramiento iterativo de enfoque: no tuve tiempo de ajustar los parámetros.
import cv2 import numpy as np def unsharp_mask(img, blur_size = (9,9), imgWeight = 1.5, gaussianWeight = -0.5): gaussian = cv2.GaussianBlur(img, (5,5), 0) return cv2.addWeighted(img, imgWeight, gaussian, gaussianWeight, 0) img_file = 'box.png' img = cv2.imread(img_file, cv2.IMREAD_COLOR) img = cv2.blur(img, (5, 5)) img = unsharp_mask(img) img = unsharp_mask(img) img = unsharp_mask(img) hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV) h, s, v = cv2.split(hsv) thresh = cv2.adaptiveThreshold(s, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C, cv2.THRESH_BINARY_INV, 11, 2) _, contours, heirarchy = cv2.findContours(thresh.copy(), cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE) cnts = sorted(contours, key = cv2.contourArea, reverse = True) #for cnt in cnts: canvas_for_contours = thresh.copy() cv2.drawContours(thresh, cnts[:-1], 0, (0,255,0), 3) cv2.drawContours(canvas_for_contours, contours, 0, (0,255,0), 3) cv2.imshow('Result', canvas_for_contours - thresh) cv2.imwrite("result.jpg", canvas_for_contours - thresh) cv2.waitKey(0)