Python OpenCV Вычитание фона и ограничивающая рамка - PullRequest
0 голосов
/ 12 марта 2020

Я новичок в использовании OpenCV и поэтому открыт для альтернативных решений этой проблемы. Я пытаюсь получить ограничивающую рамку периметра печатных плат, чтобы обрезать область интереса от моей камеры RGB-D, чтобы извлекать данные глубины только из области, содержащей печатную плату.

Поскольку камера будет неподвижной, Первоначально я думал использовать cv2.subtract (), используя изображение пустого фона и изображение печатной платы, размещенной на том же фоне. Результат показан ниже.

Теперь я не уверен, как получить ограничивающий прямоугольник печатной платы. Когда я использую findContours, я получаю 373 различных контура, которые, как я полагаю, обусловлены различием в цвете компонентов печатной платы, однако мне нужна только ограничительная рамка по периметру.

Заранее спасибо. Jadon enter image description here

РЕДАКТ. 1: img1 - это фон, а img2 - изображение с печатной платой. Я полагаю, что главная проблема в том, что существует так много контуров, и я не уверен, как выбрать тот, чтобы создать ограничивающую рамку периметра.

diff = cv2.subtract(img1, img2)
imgray = cv2.cvtColor(diff, cv2.COLOR_BGR2GRAY)
ret, thresh = cv2.threshold(imgray, 127, 255, 0)
contours, hierarchy = cv2.findContours(thresh, cv2.RETR_TREE, 
cv2.CHAIN_APPROX_SIMPLE)
print(len(contours))
cnt = contours[0]

x,y,w,h = cv2.boundingRect(cnt)
img = cv2.rectangle(diff, (x,y), (x+w, y+h), (0,255,0),2)

1 Ответ

1 голос
/ 12 марта 2020

Я написал пример, в котором я:

  • увеличиваю яркость изображения: умножаем изображение на (255/img.max())
  • применяем threshold для преобразования в двоичное изображение (черный и белый)
  • применить erode, чтобы стереть маленькие точки и тонкие белые элементы
  • применить dilate, чтобы уменьшить размер черных дыр

На этом этапе Вы можете использовать boundingRect, если ваш интересующий объект - единственная белая область. К сожалению, на вашем изображении фон на левой стороне слишком яркий ... Тогда вы можете продолжить:

  • , альтернативно, примените Canny - вместо порога / размыть / расширить - чтобы найти контуры и получить двоичное изображение
  • применить findContours к двоичному изображению (в приведенном ниже коде я избыточно использую изображение Canny-ed просто для демонстрации примера, но это может быть двоичное изображение с помощью threshold / erode / Расширьте, с похожими результатами)
  • опционально примените approxPolyDP и hull для сглаживания контуров (в приведенном ниже примере я рисую результаты этих операций, но я не использую их на самом деле)
  • вычислить contourArea и boundingRect для каждого контура
  • вернуть только контур, имеющий наибольшую площадь

Результат: original image with contours (красный boundingRect, зеленый approxPolyDP и синий hull)

Почти все эти операции имеют рабочие примеры в разделе учебника на сайте openCV .

Код

import cv2 as cv
import numpy as np
import random as rng

src = cv.imread("1KbH6.png")
src_gray = cv.cvtColor(src,cv.COLOR_BGR2GRAY)

# adjust brightness
src_bright = cv.convertScaleAbs(src_gray, alpha = 255.0/src.max(), beta = 0)
# apply threshold
threshold = 100
_, img_thresh = cv.threshold(src_bright, threshold, 255, 0)
# apply erode
erosion_size = 7
erosion_type = cv.MORPH_ELLIPSE
element = cv.getStructuringElement(erosion_type, (2*erosion_size + 1, 2*erosion_size+1), (erosion_size, erosion_size))
img_erosion = cv.erode(img_thresh, element)
# apply dilate
dilatation_size = 17
dilatation_type = cv.MORPH_ELLIPSE
element = cv.getStructuringElement(dilatation_type, (2*dilatation_size + 1, 2*dilatation_size+1), (dilatation_size, dilatation_size))
img_dilate = cv.dilate(img_erosion, element)

# apply canny and find contours
threshold = 100
canny_output = cv.Canny(img_dilate, threshold, threshold * 2)
contours = cv.findContours(canny_output, cv.RETR_EXTERNAL, cv.CHAIN_APPROX_SIMPLE)

# apply approxPolyDP, hull, boundingRect and calculate areas for each contour
contours_poly = [None]*len(contours[1])
boundRect = [None]*len(contours[1])
areas = [None]*len(contours[1])
hull_list = []
for i, c in enumerate(contours[1]):
    contours_poly[i] = cv.approxPolyDP(c, 3, True)
    hull_list.append(cv.convexHull(contours[1][i]))
    boundRect[i] = cv.boundingRect(contours_poly[i])
    areas[i] = cv.contourArea(c)

# set drawing 
drawing = np.zeros((canny_output.shape[0], canny_output.shape[1], 3), dtype=np.uint8)
# draw only the contour with the greatest area
i = areas.index(max(areas))
color = (0,0,255)
cv.drawContours(drawing, contours_poly, i, (0,255,0),2)
cv.drawContours(drawing, hull_list, i, (255,0,0),2)
cv.rectangle(drawing, (int(boundRect[i][0]), int(boundRect[i][1])), \
  (int(boundRect[i][0]+boundRect[i][2]), int(boundRect[i][1]+boundRect[i][3])), (0,0,255), 3)

# # Alternatively, you can draw contours with area bigger than some value
# for i in range(len(contours[1])):
#     if areas[i] > 1000:
#         color = (rng.randint(0,256), rng.randint(0,256), rng.randint(0,256))
#         cv.drawContours(drawing, contours_poly, i, color)
#         cv.drawContours(drawing, hull_list, i, color)
#         cv.rectangle(drawing, (int(boundRect[i][0]), int(boundRect[i][1])), \
#           (int(boundRect[i][0]+boundRect[i][2]), int(boundRect[i][1]+boundRect[i][3])), color, 2)

cv.imwrite('new.png',cv.add(drawing,src))
cv.imshow('blend',cv.add(drawing,src))
cv.waitKey()

# # If you want to see partial results of image processing
# cv.imshow('src', src)
# cv.imshow('src_bright', src_bright)
# cv.imshow('img_thresh', img_thresh)
# cv.imshow('img_erosion', img_erosion)
# cv.imshow('img_dilate', img_dilate)
# cv.imshow('canny_output', canny_output)
# cv.imshow('drawing',drawing)

Альтернатива: Chroma ke y

Еще один хороший способ улучшить обнаружение - использовать что-то вроде хроматического ключа: выберите определенный c цвет фона, преобразуйте цветное изображение из RGB в HSV и создайте маску (двоичное изображение) с фильтрацией по оттенку. Вот пример: https://docs.opencv.org/master/da/d97/tutorial_threshold_inRange.html

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...