Drawing rectangle on image opencv?

21,119

Solution 1

You can think of the process as rendering in any other rendering API like OpenGL: to put something on the screen you clean it and then draw upon the clean area.

In this context cleaning means bringing back the original image. Drawing upon it means adding your rectangles or whatever.

So, what i recommend is creating a draw method that only takes care of this. Call it whenever a change to the rendering target is meant to be made.

A quick snippet (not real python, just pseudocode):

source_image = Mat()
image = Mat()
rect = Rect()

# general logic and stuff ...

# set a mouseCallbackListener
def listener(evt):
    rect.x = evt.x   # uptade de rect properly (...)
    draw()

# apply the drawing logic
def draw():
    image = source_image.clone()
    rectangle(image, rect, Scalar(0,255,0), 1, 8, 0)
    imshow(WINDOW_NAME, image)

If you wish to see a real example, here's a C++11 code that let's you draw more than one triangle and then get their centers as output in response to keyboard input: https://gist.github.com/cirocosta/9f7a57bddb40c4e5cbca

Example of Rectangle Selection

Solution 2

I was able to achieve that in black background with the following code:-

import cv2

import numpy as np

drawing = False # true if mouse is pressed
mode = True # if True, draw rectangle. Press 'm' to toggle to curve
ix,iy = -1,-1

# mouse callback function
def draw_circle(event,x,y,flags,param):
  global ix,iy,drawing,mode

  if event == cv2.EVENT_LBUTTONDOWN:
      drawing = True
      ix,iy = x,y

  elif event == cv2.EVENT_MOUSEMOVE:
    if drawing == True:
        if mode == True:
            cv2.rectangle(img,(ix,iy),(x,y),(0,255,0),3)
            q=x
            w=y
            if q!=x|w!=y:
                 cv2.rectangle(img,(ix,iy),(x,y),(0,0,0),-1)
        else:
            cv2.circle(img,(x,y),5,(0,0,255),-1)

  elif event == cv2.EVENT_LBUTTONUP:
    drawing = False
    if mode == True:
        cv2.rectangle(img,(ix,iy),(x,y),(0,255,0),2)

    else:
        cv2.circle(img,(x,y),5,(0,0,255),-1)

img = np.zeros((512,512,3), np.uint8)
cv2.namedWindow('image')
cv2.setMouseCallback('image',draw_circle)

while(1):
 cv2.imshow('image',img)
 k = cv2.waitKey(1) & 0xFF
 if k == ord('m'):
    mode = not mode
 elif k == 27:
    break

cv2.destroyAllWindows()            

This was achieved by overlapping older rectangles with solid fill

Though if u want it over image , I can suggest watermarks

http://www.pyimagesearch.com/2016/03/07/transparent-overlays-with-opencv/

Share:
21,119
iec2011007
Author by

iec2011007

Updated on November 05, 2020

Comments

  • iec2011007
    iec2011007 over 3 years

    I have loaded an image using opencv libraries in python and now i want to draw a rectangle using mouse but while drawing the rectangle the rectangle should be visible so that the person drawing it can place it in a way so that the object of interest is completely in rectangle but my code is producing not the desired output

    my code is

    import os
    import sys
    import numpy as np
    import cv2
    
    baseDir = '/home/aman/Downloads/shirt/'
    
    filenames = next(os.walk(baseDir))[2]
    
    drawing = False # true if mouse is pressed
    mode = True # if True, draw rectangle. Press 'm' to toggle to curve
    ix,iy = -1,-1
    tx,ty = -1,-1
    
    def draw_circle(event,x,y,flags,param):
        global ix,iy,drawing,mode
        print 'aman'
        if event == cv2.EVENT_LBUTTONDOWN:
        drawing = True
        ix,iy = x,y
    
        elif event == cv2.EVENT_MOUSEMOVE:
        if drawing == True:
            if mode == True:
                #cv2.rectangle(img,(ix,iy),(x,y),(0,255,0),-1)
                cv2.line(img, (ix,iy),(ix,y), 255, 1, 8, 0)
                cv2.line(img, (ix,iy),(x,iy), 255, 1, 8, 0)
    
                cv2.line(img, (ix,y),(x,y), 255, 1, 8, 0)
                cv2.line(img, (x,iy),(x,y), 255, 1, 8, 0)
            else:
                cv2.circle(img,(x,y),5,(0,0,255),-1)
    
        elif event == cv2.EVENT_LBUTTONUP:
        drawing = False
        if mode == True:
            #cv2.rectangle(img,(ix,iy),(x,y),(0,255,0),-1)
            cv2.rectangle(img,(ix,y),(x,y),(0,255,0),-1)
            cv2.rectangle(img,(x,iy),(x,y),(0,255,0),-1)
        else:
            cv2.circle(img,(x,y),5,(0,0,255),-1)
    
    img = np.zeros((512,512,3), np.uint8)
    cv2.namedWindow('image')
    cv2.setMouseCallback('image',draw_circle)
    
    while(1):
        cv2.imshow('image',img)
        k = cv2.waitKey(1) & 0xFF
        if k == ord('m'):
        mode = not mode
        elif k == 27:
        break
    
    cv2.destroyAllWindows()        
    

    `

    but the output produced is something like enter image description here

    I dont want this kind of behaviour i just want a single rectangle not for every movement. I know i should modify the code in mouse move event but the problem is how to do that so that when the person is moving the mouse he is able to see the complete rectangle and when he releases the button the rectangle should be fixed ?

    • Mailerdaimon
      Mailerdaimon over 9 years
      OpenCv draws the line directly into your image data. Either copy and restore for every new rectangle or use a proper Gui Toolkit.
    • beaker
      beaker almost 9 years
      I really don't know how to do this in Python (hence the comment), but the idea is to draw each intermediate rectangle by doing XOR between the line color and the image color at each pixel. Then, before you draw the next rectangle, XOR again with the line color to undo the previous rectangle. In C++ you'd do this with LineIterator. Only on the final rectangle do you draw it normally in the final color.