0

So i would like to make a program which can detect an object by color, position and sharpness. Now I am there that I could detect the object by color and draw its contour and bounding box. My problem is that i really dont know how to cut out the object from the picture and save it as picture file when the program recognise its contour or bounding box.

here's a picture of what my camera is seeing input

output

I would like to cut out what is inside of the green colored boundig box as many times as fps in the video and as long as you can see it in the video. So if the video is 30 fps and the object is visible for 10 seconds it needs to take 300 pictures.

Here is the code:

i know it looks bad, im just trying to figure out what to use to make it work

import cv2 as cv
import numpy as np
import os
import uuid

cap = cv.VideoCapture(1)
font = cv.FONT_HERSHEY_COMPLEX
path = os.getcwd()
print(path)


def createFolder(directory):
    try:
        if not os.path.exists(directory):
            os.makedirs(directory)
    except OSError:
        print('Error: Creating directory. ' + directory)


createFolder("./data")

# folderName = '%s' % (str(uuid.uuid4()))

while cap.isOpened():
    _, frame = cap.read()
    hsv = cv.cvtColor(frame, cv.COLOR_BGR2HSV)
    # blue is the chosen one for now
    lower_color = np.array([82, 33, 39])
    upper_color = np.array([135, 206, 194])
    mask = cv.inRange(hsv, lower_color, upper_color)

    kernel = np.ones((5, 5), np.uint8)
    mask = cv.erode(mask, kernel)

    contours, hierarchy = cv.findContours(mask, cv.RETR_EXTERNAL, cv.CHAIN_APPROX_SIMPLE)
    # find contour
    for contour in contours:
        area = cv.contourArea(contour)
        x, y, h, w = cv.boundingRect(contour)

        if area > 100:
            # bounding box
            # cv.rectangle(frame, (x - 40, y - 30), (x + h * 3, y + w * 3), (0, 255, 0), 1)
            # cutting and saving
            ext_left = tuple(contour[contour[:, :, 0].argmin()][0] - 20)
            ext_right = tuple(contour[contour[:, :, 0].argmax()][0] + 20)
            ext_top = tuple(contour[contour[:, :, 1].argmin()][0] - 20)
            ext_bot = tuple(contour[contour[:, :, 1].argmax()][0] + 20)

            outfile = '%s.jpg' % (str(uuid.uuid4()))
            cropped_image = frame[ext_top[1]:ext_bot[1], ext_left[0]:ext_right[0]]

            # write images to a specified folder
            cv.imwrite(os.path.join(path, "/data/", outfile), cropped_image)

    # outputs
    cv.imshow("Frame", frame)
    cv.imshow("Mask", mask)

    key = cv.waitKey(1)
    if key == 27:
        break

cap.release()
cv.destroyAllWindows()
Cerberus
  • 51
  • 1
  • 1
  • 7
  • Hi @Cerberus, keep the great work, you are very close! What you are missing is getting the contour and creating a mask, so that you can copy only the object pixels from the frame. I posted an answer that goes over other posts and documentation. You can also crop using the bounding rectangle, as shown in [this post](https://stackoverflow.com/questions/10481411/copy-an-cvmat-inside-a-roi-of-another-one/31282391). Let me know if this works and I can also edit my answer to assist you. – diogoslima Feb 27 '20 at 16:22
  • Thanks for your help, i appraciate it. I m newby in python an these links and codes helps a lot (save a tons of time). I will write if i could implement them. – Cerberus Feb 27 '20 at 19:57
  • @diogoslima Thanks for your help. Now i can write the object to disk. My problem is that i would like to save these images to a specified folder in the current working directory but something not working well with "os.path.join". So i would like save them to /os.getcwd()/data/. I think i have syntax error. – Cerberus Feb 29 '20 at 13:27
  • Hi @Cerberus, my flash reading of your code made me wonder if it is just a path caveat. Have you tried -> cv.imwrite(os.path.join("./data/"), outfile), cropped_image)? – diogoslima Mar 02 '20 at 13:41

1 Answers1

0

Focusing on the question and ignoring the code style, I can say you are close to achieving your goal :)

For cropping the object, you can use the Mat copyTo method. Here is the official OpenCV documentation and here is an example from the OpenCV forums.

Now, for creating the mask from the contours, you can use the same drawCountours method you already use, but provide a negative value for the thickness parameters (for example, thickness=CV_FILLED). You can see a code snippet in this stackoverflow post and check details in the official documentation.

For saving the image to disk you can use imwrite.

So, in a nutshell, draw filled contours to a mask and use that mask to copy only the object pixels from the video frame to another mat that you can save the disk.

Instead of posting code, I will share this very similar question with an accepted answer that may have the code snippet you are looking for.

diogoslima
  • 169
  • 7