0

I'm using python 2.7.13 and opencv 3.4.0. I have a videostream with 2 green dots. I'm tracking them using color detection. I need to select a two ROIs(region of interest), each of which will contain one dot, in two separate images for further processing. I wrote a program which detects them and creates ROIs, but problem is that it selects only one region, while there are two dots. Also, it gives me error: "line 47, in cv2.imshow ("area1",area1) NameError: name 'area1' is not defined"

But if I replace img=frame.array line with img=cv2.imread("image.jpg") then it works but only with picture. Here how program works now

import cv2
import numpy as np
from picamera.array import PiRGBArray
from picamera import PiCamera
import time
import sys

lowerBound=np.array([33,80,40])
upperBound=np.array([102,255,255]) #ranges for green color

camera = PiCamera()
camera.rotation = 180
camera.resolution = (320, 240)
camera.framerate = 30
font=cv2.FONT_HERSHEY_SIMPLEX

rawCapture = PiRGBArray(camera, size=(320, 240))

time.sleep(0.1)
kernelOpen=np.ones((5,5))
kernelClose=np.ones((20,20))

for frame in camera.capture_continuous(rawCapture, format="bgr",         use_video_port=True): # capture frames from the camera
    img = frame.array #works only if I change this with img =cv2.imread("image.jpg")
    imgHSV= cv2.cvtColor(img,cv2.COLOR_BGR2HSV)
    mask=cv2.inRange(imgHSV,lowerBound,upperBound)

    maskOpen=cv2.morphologyEx(mask,cv2.MORPH_OPEN,kernelOpen)
    maskClose=cv2.morphologyEx(maskOpen,cv2.MORPH_CLOSE,kernelClose) #apply morphology for greater accuracy
    maskFinal=maskClose
    _, conts, _=cv2.findContours(maskFinal.copy(),cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_NONE)

    for i in range(len(conts)):
        x,y,w,h=cv2.boundingRect(conts[i])
        area1=img[y:y+h, x:x+w] #selecting my ROI
        cv2.rectangle(img,(x,y),(x+w,y+h),(0,0,255), 2)
    cv2.imshow("maskClose",maskClose)
    cv2.imshow("maskOpen",maskOpen)
    cv2.imshow("mask",mask)
    cv2.imshow("cam",img)
    cv2.imshow ("area1",area1) #Showing my ROI

    key = cv2.waitKey(1) & 0xFF
    rawCapture.truncate(0)
    if key == ord("q"):
            break
J Dow
  • 3
  • 1
  • 5

1 Answers1

0

In the for loop you keep setting/overwriting area1, so the output will always be just one image. You can solve this easily by moving the imshow() into the for loop.

At the top of your code add:
prevNrOfContours = 0

Alter the frame-loop with this code:

    # create a window for each roi
    nrOfContours = len(conts)
    for i in range(nrOfContours):
        x,y,w,h=cv2.boundingRect(conts[i])
        area=img[y:y+h, x:x+w] #selecting my ROI
        cv2.rectangle(img,(x,y),(x+w,y+h),(0,0,255), 2)
        cv2.imshow("area" + str(i), area)

    # if there are more open windows then roi's
    # then close the windows that will not be refreshed
    if prevNrOfContours > nrOfContours:
        for i in range(nrOfContours, prevNrOfContours):
            cv2.destroyWindow("area" + str(i))

    # store the number of roi's in this frame, so it can 
    # be used in the next frame
    prevNrOfContours = nrOfContours

Edit: extended code to remove unnecessary opened windows

Edit2: to only select the 2 largest contours:

    # instantiate list
    contour_list = []

    for cnt in contours:
            # get contour size
            area = cv2.contourArea(cnt)
            # add tuple of the contour and its size to the list
            contour_list.append((cnt, area))

    # sort list on size
    sorted_list = sorted(contour_list, key=lambda x: x[1])
    # create a window for the 2 largest contours
    for i in range(-2,0):
            x,y,w,h=cv2.boundingRect(sorted_list[i][0])
            roi=img[y:y+h, x:x+w] #selecting my ROI
            cv2.rectangle(img,(x,y),(x+w,y+h),(0,0,255), 2)
            cv2.imshow("area" + str(i), roi)
J.D.
  • 4,511
  • 2
  • 7
  • 20
  • Thank you, you helped me a lot! It works fine but if you place an object appears in the frame and then disappears, then there remains a window with the last image of this object. How can I destroy such windows automatically? [Here's how it looks like](https://imgur.com/a/VR6vLrd) – J Dow Mar 16 '19 at 07:42
  • You can use the function cv2.destroyWindow(window_name) ([documentation](https://docs.opencv.org/3.4.5/d7/dfc/group__highgui.html#ga851ccdd6961022d1d5b4c4f255dbab34)). I've modified the answer to include this. – J.D. Mar 16 '19 at 09:51
  • Thank you again, I extended my program so now it counts number of pixels in each ROI: `for i in range(len(conts)): x,y,w,h=cv2.boundingRect(conts[i]) area=img[y:y+h, x:x+w] pixNum = cv2.inRange(area,lowerBound,upperBound ) no_black = cv2.countNonZero(pixNum) cv2.imshow("area" + str(i), area) cv2.rectangle(img,(x,y),(x+w,y+h),(0,0,255), 2) print (no_black)` – J Dow Mar 21 '19 at 18:18
  • But I keep getting some false ROIs due to glare on the camera, which pop up as windows and heavily load the system. How can I get only two ROIs with the biggest pixel number? I also want program show only windows with this 2 ROIs. – J Dow Mar 21 '19 at 18:21
  • I updated the answer with a possible implementation – J.D. Mar 21 '19 at 22:25