#python #opencv #matplotlib #roi
#python #opencv #matplotlib #roi
Вопрос:
У меня возникают проблемы с пониманием областей интереса в opencv. У меня есть некоторый код, который выполняет простое вычитание фона из моего first_frame
. Я также могу нарисовать прямоугольник с помощью своей mouse_draw_rect
функции.
Теперь я хотел бы, чтобы вычитание фона происходило только внутри прямоугольника, который я рисую, потому что я хотел бы ускорить обработку алгоритма. Я знаю, что мне нужно установить какую-то область интереса, но я пробовал все это вчера и сегодня, и ничего из того, что я пробовал из учебников, не сработало. Может ли кто-нибудь помочь мне в этом процессе, пожалуйста?
РЕДАКТИРОВАТЬ: попытка исправить код
import numpy as np
import cv2
import matplotlib.pyplot as plt
cap = cv2.VideoCapture(0)
_, first_frame = cap.read()
def mouse_draw_rect(event, x, y, flags, params):
global point1, point2, drawing, first_frame, x1, x2, y1, y2
if event == cv2.EVENT_LBUTTONDOWN:
if drawing is False:
drawing = True
point1 = (x, y)
x1 = (x)
y1 = (y)
else:
drawing = False
elif event == cv2.EVENT_MOUSEMOVE:
if drawing is True:
point2 = (x, y)
x2 = (x)
y2 = (y)
elif event == cv2.EVENT_MBUTTONDOWN:
first_frame = frame
drawing = False
point1 = ()
point2 = ()
x1 = ()
x2 = ()
y1 = ()
y2 = ()
cv2.namedWindow('Original')
cv2.setMouseCallback("Original", mouse_draw_rect)
while True:
ret, frame = cap.read( )
if point1 and point2:
cv2.rectangle(frame, point1, point2, (0, 0, 0),5)
difference = cv2.absdiff(first_frame[y1:y2, x1:x2], frame[y1:y2, x1:x2])
difference = cv2.GaussianBlur(difference, (3, 3), 0)
_, difference = cv2.threshold(difference, 18, 255, cv2.THRESH_BINARY)
cv2.imshow('first frame (1)', first_frame)
cv2.imshow('Original', frame)
cv2.imshow('difference', difference)
key = cv2.waitKey(30) amp; 0xff
if key == 27:
break
cap.release()
cv2.destroyAllWindows()
Ответ №1:
Основная проблема связана с событием выбора ROI и тем, как оно вызывается в настоящее время. Текущая реализация не является динамической, что означает, что мы не можем визуализировать, какой ROI мы пытаемся выбрать. Кроме того, мы начали обработку еще до выбора ROI.
Правильный способ выбора ROI заключается в том, что, как только мы захватили первый кадр, зарегистрируйте событие щелчка мыши и визуализируйте кадр до бесконечности с imshow
помощью и waitKey(n)
до тех пор, пока не будет нажата определенная клавиша. В качестве альтернативы, мы можем достичь того же эффекта без бесконечного цикла, используя waitKey(0)
(не проверено).
На этом этапе мы должны быть в состоянии нарисовать желаемый прямоугольник ROI. Ключевым фактором здесь является то, что выполнение должно быть остановлено либо с помощью бесконечного цикла, либо waitKey(0)
. Просто регистрации события недостаточно. После того, как выбор ROI будет выполнен, перейдите к остальной части кода.
Некоторые рекомендации заключаются в следующем:
- Избегайте использования глобальных переменных, где это возможно
- Создайте отдельное окно для выбора ROI и удалите его впоследствии
- Создайте отдельные функции для каждой отдельной задачи
Ниже приведен полный код, демонстрирующий правильное использование события щелчка мыши для выбора ROI для обработки видео:
import numpy as np
import cv2
import matplotlib.pyplot as plt
ORIGINAL_WINDOW_TITLE = 'Original'
FIRST_FRAME_WINDOW_TITLE = 'First Frame'
DIFFERENCE_WINDOW_TITLE = 'Difference'
canvas = None
drawing = False # true if mouse is pressed
#Retrieve first frame
def initialize_camera(cap):
_, frame = cap.read()
return frame
# mouse callback function
def mouse_draw_rect(event,x,y,flags, params):
global drawing, canvas
if drawing:
canvas = params[0].copy()
if event == cv2.EVENT_LBUTTONDOWN:
drawing = True
params.append((x,y)) #Save first point
elif event == cv2.EVENT_MOUSEMOVE:
if drawing:
cv2.rectangle(canvas, params[1],(x,y),(0,255,0),2)
elif event == cv2.EVENT_LBUTTONUP:
drawing = False
params.append((x,y)) #Save second point
cv2.rectangle(canvas,params[1],params[2],(0,255,0),2)
def select_roi(frame):
global canvas
canvas = frame.copy()
params = [frame]
ROI_SELECTION_WINDOW = 'Select ROI'
cv2.namedWindow(ROI_SELECTION_WINDOW)
cv2.setMouseCallback(ROI_SELECTION_WINDOW, mouse_draw_rect, params)
roi_selected = False
while True:
cv2.imshow(ROI_SELECTION_WINDOW, canvas)
key = cv2.waitKey(10)
#Press Enter to break the loop
if key == 13:
break;
cv2.destroyWindow(ROI_SELECTION_WINDOW)
roi_selected = (3 == len(params))
if roi_selected:
p1 = params[1]
p2 = params[2]
if (p1[0] == p2[0]) and (p1[1] == p2[1]):
roi_selected = False
#Use whole frame if ROI has not been selected
if not roi_selected:
print('ROI Not Selected. Using Full Frame')
p1 = (0,0)
p2 = (frame.shape[1] - 1, frame.shape[0] -1)
return roi_selected, p1, p2
if __name__ == '__main__':
cap = cv2.VideoCapture(0)
#Grab first frame
first_frame = initialize_camera(cap)
#Select ROI for processing. Hit Enter after drawing the rectangle to finalize selection
roi_selected, point1, point2 = select_roi(first_frame)
#Grab ROI of first frame
first_frame_roi = first_frame[point1[1]:point2[1], point1[0]:point2[0], :]
#An empty image of full size just for visualization of difference
difference_image_canvas = np.zeros_like(first_frame)
while cap.isOpened():
ret, frame = cap.read()
if ret:
#ROI of current frame
roi = frame[point1[1]:point2[1], point1[0]:point2[0], :]
difference = cv2.absdiff(first_frame_roi, roi)
difference = cv2.GaussianBlur(difference, (3, 3), 0)
_, difference = cv2.threshold(difference, 18, 255, cv2.THRESH_BINARY)
#Overlay computed difference image onto the whole image for visualization
difference_image_canvas[point1[1]:point2[1], point1[0]:point2[0], :] = difference.copy()
cv2.imshow(FIRST_FRAME_WINDOW_TITLE, first_frame)
cv2.imshow(ORIGINAL_WINDOW_TITLE, frame)
cv2.imshow(DIFFERENCE_WINDOW_TITLE, difference_image_canvas)
key = cv2.waitKey(30) amp; 0xff
if key == 27:
break
else:
break
cap.release()
cv2.destroyAllWindows()
Совет для профессионалов:
Иногда при инициализации камеры требуется некоторое время для прогрева в зависимости от окружающего освещения, присутствующего в комнате. Вы можете рассмотреть возможность пропуска нескольких начальных кадров, чтобы камера установилась на этапе инициализации. Это можно сделать, определив initialize_camera
функцию в приведенном выше коде следующим образом:
def initialize_camera(cap):
for i in range(0,60): #Skip first 60 frames
_, frame = cap.read()
return frame
Комментарии:
1. Это было удивительное объяснение, спасибо. Я прочесаю ваш код и узнаю из него столько, сколько смогу.
2. @sentientnativeplant… Отлично. Вы можете рассмотреть возможность принятия ответа, если он решит вашу проблему, чтобы этот пост мог выйти из списка вопросов без ответов и помочь читателям, которые сталкиваются с той же проблемой. 🙂
Ответ №2:
Просто обрезайте область в прямоугольнике, который вы нарисовали. Вместо
difference = cv2.absdiff(first_frame, frame)
используйте
difference = cv2.absdiff(first_frame[y1:y2, x1:x2], frame[y1:y2, x1:x2])
Комментарии:
1. Вероятно, я совершаю серьезную ошибку, но я попытался применить то, что вы предложили, и он возвращается с ошибкой, что y1 не определен. Я изменил код в своем основном тексте, чтобы вы могли видеть, что я добавил.
2. (x1, y1) и (x2, y2) — это ваши point1 и point2 . Не используйте x и y, используйте свои имена
3. Итак, я извлекаю значения вот так? разница = cv2.absdiff(first_frame[point1[1]:point2[1],point1[0]:point2[0]], кадр[point1[1]:point2[1], point1[0]:point2[0]])
4. К сожалению, когда я добавляю ее в свою функцию, она по-прежнему не применяется к прямоугольнику. Я, вероятно, сделаю перерыв и попытаюсь заняться этим завтра, спасибо за ваше время.
Ответ №3:
Для каждого кадра вы можете создать субизображение, используя subimage = image[y1:y2,x1:x2]
, а затем использовать субизображение для обработки.
Быстрая и грязная реализация в вашем коде:
Заменить
elif event == cv2.EVENT_MOUSEMOVE:
с
elif event == cv2.EVENT_LBUTTONUP:
И добавьте субизображение:
if point1 and point2:
cv2.rectangle(frame, point1, point2, (0, 0, 0),5)
subimg = frame[point1[1]:point2[1],point1[0]:point2[0]]
cv2.imshow("Subimage",subimg)