0

I am making a object detection project.

I have my code. And I have written it by following a tutorial. In the tutorial, the guy drew a rectangle in opencv for every single object which is detected.

But I want to change the rectangle to triangle or Arrow.

let me explain with code===>

In my function, I detect objects. And here I draw rectangle for detected objects==>

 cv2.rectangle(img, (x, y), (x+w,y+h), (255, 0 , 255), 2)

But I want to change this rectangle to a triangle.(And I want to set position of triangle to above of object. Just like in these images:::

This is the object detection with triangle [![enter image description here][1]][1]

This is the thing that what I want to make instead of rectangle::: [![enter image description here][2]][2]

How Can I make a triangle/arrow with positions of my detected objects? All of my code is here==>

from os.path import sep
import cv2 as cv2
import numpy as np
import json

# Camera feed
cap_cam = cv2.VideoCapture(0)

ret, frame_cam = cap_cam.read()

hey = 0

print(cv2. __version__)

whT = 320
confThreshold =0.5
nmsThreshold= 0.2

classesFile = "coco.names"
classNames = []
with open(classesFile, 'rt') as f:
    classNames = f.read().rstrip('\n').split('\n')
print(classNames)
## Model Files
modelConfiguration = "custom-yolov4-tiny-detector.cfg"
modelWeights = "custom-yolov4-tiny-detector_last.weights"
net = cv2.dnn.readNetFromDarknet(modelConfiguration, modelWeights)
net.setPreferableBackend(cv2.dnn.DNN_BACKEND_OPENCV)
net.setPreferableTarget(cv2.dnn.DNN_TARGET_CPU)
def findObjects(outputs,img):
    global hey
    global previousHey  
    hT, wT, cT = img.shape
    bbox = []
    classIds = []
    confs = []
    for output in outputs:
        for det in output:
            scores = det[5:]
            classId = np.argmax(scores)
            confidence = scores[classId]
            if confidence > confThreshold:
                w,h = int(det[2]*wT) , int(det[3]*hT)
                x,y = int((det[0]*wT)-w/2) , int((det[1]*hT)-h/2)
                bbox.append([x,y,w,h])
                classIds.append(classId)
                confs.append(float(confidence))
    global indicates
    indices = cv2.dnn.NMSBoxes(bbox, confs, confThreshold, nmsThreshold)
    hey = 0
    for i in indices:
        i = i[0]
        box = bbox[i]
        x, y, w, h = box[0], box[1], box[2], box[3]
        # print(x,y,w,h)
        cv2.rectangle(img, (x, y), (x+w,y+h), (255, 0 , 255), 2)
        
        #cv2.line(img, (350,400), (x, y), (255,0,0), 4)
        #cv2.line(img, (400,400), (x + 50 , y), (255,0,0), 4)
        #cv.putText(img,f'{classNames[classIds[i]].upper()} {int(confs[i]*100)}%',
                  #(x, y-10), cv.FONT_HERSHEY_SIMPLEX, 0.6, (255, 0, 255), 2)
        print('success')
        hey = 1
            
        
video_frame_counter = 0
while cap_cam.isOpened():
    img = cv2.imread('photos' + sep + 'lutfen.jpg')
    #BURADA OK VİDEOSU OYNATILACAK
    #if not decetiona diye dene yarın.
    blob = cv2.dnn.blobFromImage(img, 1 / 255, (whT, whT), [0, 0, 0], 1, crop=False)
    net.setInput(blob)
    layersNames = net.getLayerNames()
    outputNames = [(layersNames[i[0] - 1]) for i in net.getUnconnectedOutLayers()]
    outputs = net.forward(outputNames)
    findObjects(outputs,img)
    cv2.imshow('Image', img)
    
    # Video feed
    if hey == 1:
        filename = 'photos' + sep + 'Baslksz-3.mp4'
        cap_vid = cv2.VideoCapture(filename)
        
    if hey == 0:
        filename = 'photos' + sep + 'vid2.mp4'
        cap_vid = cv2.VideoCapture(filename)    
    print(hey)
    ret, frame_vid = cap_vid.read()

#cap_cam.set(cv2.CAP_PROP_FRAME_WIDTH, 1920)
#cap_cam.set(cv2.CAP_PROP_FRAME_HEIGHT, 1080)

# Resize the camera frame to the size of the video
    height = int(cap_vid.get(cv2.CAP_PROP_FRAME_HEIGHT))
    width = int(cap_vid.get(cv2.CAP_PROP_FRAME_WIDTH))
    # Capture the next frame from camera
    ret, frame_cam = cap_cam.read()
    video_frame_counter += 1
    if video_frame_counter == cap_vid.get(cv2.CAP_PROP_FRAME_COUNT):
            video_frame_counter = 0
            cap_vid.set(cv2.CAP_PROP_POS_FRAMES, 0)
    frame_cam = cv2.resize(frame_cam, (width, height), interpolation = cv2.INTER_AREA)

    #ret = cap_vid.set(cv2.CAP_PROP_POS_MSEC, time_passed)

    ret, frame_vid = cap_vid.read()
    if not ret:
        print('Cannot read from video stream')
        break

    # Blend the two images and show the result
    tr = 0.4 # transparency between 0-1, show camera if 0
    frame = ((1-tr) * frame_cam.astype(np.float) + tr * frame_vid.astype(np.float)).astype(np.uint8)
    cv2.imshow('Transparent result', frame)
    if cv2.waitKey(1) == 27: # ESC is pressed
        break
    
cap_cam.release()
cap_vid.release()
cv2.destroyAllWindows()

1 Answers1

4

The easy way

You can use the cv.arrowedLine() function that will draw something similar to what you want. For example, to draw a red arrow above your rectangle:

center_x = x + w//2
cv2.arrowedLine(img, (center_x, y-50), (center_x, y-5), (0,0,255), 2, 8, 0, 0.5)

which should give a result similar to the image below. Take a look at the OpenCV documentation for the description of the parameters of the function. You can change its size, thickness, color, etc.

box with arrow

Custom arrow shape

If you want more control over the shape of your arrow, you can define a contour (vertex by vertex) and use cv.drawContours() to render it. For example:

# define the arrow shape
shape = np.array([[[0,0],[-25,-25],[-10,-25],[-10,-50],
                   [10,-50],[10,-25],[25,-25]]])

# move it to the desired position
cx = x + w // 2
cy = y - 5
shape[:,:,0] += cx
shape[:,:,1] += cy

# draw it
cv2.drawContours(img, shape, -1, (0, 255, 0), -1)

This snippet will give you the image below. You can adjust the shape by altering the vertices in the shape array, or look at the documentation to change the way OpenCV draws it.

enter image description here

vvanpelt
  • 791
  • 1
  • 6
  • 13
  • Thanks for answering but Can I ask something? How can I make the arrows width bigger? – Ö. ALP EREN GÜL Feb 20 '21 at 17:39
  • Like now it is so tiny – Ö. ALP EREN GÜL Feb 20 '21 at 17:39
  • Yes but instead of doing with arrowedline If I want to use and triangle(not arrow) How can I set the position of triangle to top of detected object – Ö. ALP EREN GÜL Feb 20 '21 at 21:59
  • @Ö.ALPERENGÜL I edited the answer, it gives now two ways of solving to problem (new one more complex but should be closer to what you want). – vvanpelt Feb 21 '21 at 08:36
  • thanks for your answers, yes you are right it is more complex but I can make some predictions about how it is being made. – Ö. ALP EREN GÜL Feb 21 '21 at 10:40
  • hmm so sir, Can I get the np array of a video instead of creating shapeds? Can I do that.(Video will be played at the top of rectangle again as you done) – Ö. ALP EREN GÜL Feb 21 '21 at 10:48
  • 1
    "get the np array of a video" does not really make sense. Also, playing video over video is out of the scope of your question... I would recommend you to search for related questions like [this one](https://stackoverflow.com/questions/11334443/how-to-overlay-small-animation-on-camera-stream-in-opencv). – vvanpelt Feb 21 '21 at 10:56
  • hmm, oket I will search for it but also is there any animation property or is there any way to make a top-down movement for arrow....? – Ö. ALP EREN GÜL Feb 21 '21 at 11:00
  • You'll need to animate the movement yourself. It should not be too difficult, if you have the number of the frame in a variable `i`, you can recompute `cy` like this: `cy = y - 5 + int(10 * np.sin(0.1 * i))`. I let you change the values to make the movement you desire. – vvanpelt Feb 21 '21 at 11:04
  • Can you tell me a source inwhich I can learn making shapes with np array? Just like you did. How can I learn it? – Ö. ALP EREN GÜL Feb 24 '21 at 06:59
  • @Ö.ALPERENGÜL It is just vertex positions, in pixel coordinates. Try to keep only the three first, like this: `[[[0,0],[-25,-25],[-10,-25]]]` and then add the others one by one, you'll understand how it works. – vvanpelt Feb 24 '21 at 10:13
  • Yes you are right but; I just wonder, when I take an objects pixel coordinate in a image it doesnt work properly. So do you know the method name if I want to convert an object to np array in a image – Ö. ALP EREN GÜL Feb 26 '21 at 22:57