2

I have been trying to make an OpenCV-Py program to draw rectangle, line, and circle on mouse click and drag. I could successfully do it for line and rectangle but the code for the circle is wrong and I need help with that.

import numpy as np
import cv2 as cv
import math
drawing = False # true if mouse is pressed
ix,iy = -1,-1
# mouse callback function
def draw_circle(event,x,y,flags,param):
    global ix,iy,drawing
    if event == cv.EVENT_LBUTTONDOWN:
        drawing = True
        ix,iy = x,y
    elif event == cv.EVENT_MOUSEMOVE:
        if drawing == True:
             k = cv.waitKey(33)

             if k == ord('r'):
                cv.rectangle(img,(ix,iy),(x,y),(0,255,0),-1)
             elif k==ord('c'):
                cv.circle(img,int(((ix+x)/2,(iy+y)/2)),int(math.sqrt( ((ix-x)**2)+((iy-y)**2) )),(0,0,255),-1)
             elif k== ord('l'):
                cv.line(img,(ix,iy),(x,y),(255,0,0),5)
    elif event == cv.EVENT_LBUTTONUP:
        drawing = False
img = np.zeros((512,512,3), np.uint8)
cv.namedWindow('image')
cv.setMouseCallback('image',draw_circle)
while(1):
    cv.imshow('image',img)
    k = cv.waitKey(1) & 0xFF
    if k == 27:
    break
cv.destroyAllWindows()

ERROR: Traceback (most recent call last): File "mouse.py", line 19, in draw_circle cv.circle(img,int(((ix+x)/2,(iy+y)/2)),int(math.sqrt( ((ix-x)**2)+((iy-y)**2) )),(0,0,255),-1) TypeError: int() argument must be a string, a bytes-like object or a number, not 'tuple'

Jeru Luke
  • 20,118
  • 13
  • 80
  • 87
Muskan Bansal
  • 79
  • 2
  • 13

2 Answers2

1

This is how to draw the circle dynamically. As you drag the mouse you can see the size of the circle dynamically change.

import math
import numpy as np
import cv2 as cv

cv.namedWindow('image', cv.WND_PROP_ASPECT_RATIO)
drawing = False  # true if mouse is pressed
 # Coordinate
x1, y1, x2, y2 = -1, -1, -1, -1


def run():
    img = cv.imread(f'image/python.png')        
    cv.imshow('image', img)
    # Create a layer to draw circle. The layer has the same dimension of image
    layer = np.zeros((img.shape[0], img.shape[1], 3), dtype="uint8")

   # mouse callback function
   def draw_circle(event, x, y, flags, param):
      global x1, y1, x2, y2, drawing
      # Manage different button state
      if event == cv.EVENT_LBUTTONDOWN:
          drawing = True
          x1, y1 = x, y
      elif event == cv.EVENT_MOUSEMOVE:
          if drawing == True:
              # Fill all value to 0 to clean layer
              layer.fill(0)
              cv.circle(layer, (x1, y1),calc_radius(x1, y1, x, y), (255, 0, 0), 1)
              # Create a mask of shape
              img2gray = cv.cvtColor(layer, cv.COLOR_BGR2GRAY)
              ret, mask = cv.threshold(img2gray, 0, 255, cv.THRESH_BINARY)
              # Create a copy of original image
              _img = img.copy()
              # Set the value of mask to 0, to avoid color overlap problems
              _img[np.where(mask)] = 0
              cv.imshow('image', np.where(layer == 0, _img, layer))
      elif event == cv.EVENT_LBUTTONUP:
          drawing = False
          layer.fill(0)
          cv.circle(layer, (x1, y1), calc_radius(x1, y1, x, y), (255, 0, 0), 1)
          # Create a mask of shape
          img2gray = cv.cvtColor(layer, cv.COLOR_BGR2GRAY)
          ret, mask = cv.threshold(img2gray, 0, 255, cv.THRESH_BINARY)
          _img = img.copy()
          # Set the value of mask to 0, to avoid color overlap problems
          _img[np.where(mask)] = 0
          # Merge two array using Numpy where function
          cv.imshow('image', np.where(layer == 0, _img, layer))
  # Assig callback 
  cv.setMouseCallback('image', draw_circle)

 # Service function to calculate radius (Pythagorean theorem) 
  def calc_radius(x1, y1, x2, y2):
      delta_x = abs(x2 - x1)
      delta_y = abs(y2 - y1)
      return int(math.sqrt((delta_x**2)+(delta_y**2)))

  while True:
      k = cv.waitKey(1)
      if k == ord("c"):  # c to terminate a program
          cv.destroyAllWindows()
          break

if __name__ == '__main__':
   run()
0

To dynamically draw a circle with OpenCV,

import numpy as np
import cv2
import math
drawing = False # true if mouse is pressed
ix,iy = -1,-1

# Create a function based on a CV2 Event (Left button click)
def draw_circle(event,x,y,flags,param):
    global ix,iy,drawing
    
    if event == cv2.EVENT_LBUTTONDOWN:
        drawing = True
        # we take note of where that mouse was located
        ix,iy = x,y
        
    elif event == cv2.EVENT_MOUSEMOVE:
        drawing == True
        
    elif event == cv2.EVENT_LBUTTONUP:
        radius = int(math.sqrt( ((ix-x)**2)+((iy-y)**2)))
        cv2.circle(img,(ix,iy),radius,(0,0,255), thickness=1)
        drawing = False

# Create a black image
img = np.zeros((512,512,3), np.uint8)

# This names the window so we can reference it
cv2.namedWindow('image')

# Connects the mouse button to our callback function
cv2.setMouseCallback('image',draw_circle)

while(1):
    cv2.imshow('image',img)

    # EXPLANATION FOR THIS LINE OF CODE:
    # https://stackoverflow.com/questions/35372700/whats-0xff-for-in-cv2-waitkey1/39201163
    k = cv2.waitKey(1) & 0xFF
    if k == 27:
        break
# Once script is done, its usually good practice to call this line
# It closes all windows (just in case you have multiple windows called)
cv2.destroyAllWindows()
Vivek Prajapati
  • 109
  • 1
  • 4