2

I'm doing a simple project to detect QR codes and draw bounding boxes from a webcam capture.

import cv2
import numpy as np
import sys 
import time
#
# Sanity Check
print("QR Scanner initialized.")


# Utility function to get a video frame from webcam.
# @param: cap is a cv2.videoCapture object.
def captureFrame(cap):
    ret, frame = cap.read()
    if ret == False:
        print("Capture failed.")
    return frame

# Utility function to draw bounding box on frame.
def display(img, bbox):
    n = len(bbox)
    
    for j in range(n):
        cv2.line(img, 
                 tuple(bbox[j][0]), 
                 tuple(bbox[ (j+1) % n][0]), 
                 (255,0,0), 
                 3)
        cv2.imshow("Video", img)
    
# Function to detect QR code in an input image
def qrDetect(inputImage):
    # Create a qrCodeDetector Object.
    qrDecoder = cv2.QRCodeDetector()

    # Look for a qr code.    
    t = time.time()
    
    data, bbox, rectifiedImage = qrDecoder.detectAndDecode(inputImage)
    print("Time Taken for Detect and Decode : {:.3f} seconds.".format(time.time() - t))
    
    # Print output if applicable.
    if len(data) > 0:
        print("Decoded Data : {}".format(data))
        return 1, inputImage, bbox
    else:
        print("QR Code not detected")
        return 0, inputImage, 0


# Main function.  
def main():
    print("I'm alive!")
    
    # Stream webcam frames until 'q' is pressed.
    cap = cv2.VideoCapture(0, cv2.CAP_DSHOW) 
    while True:
        frame = captureFrame(cap)
        ret, img, bbox = qrDetect(frame)
        
        if ret:
            display(img, bbox)
        else:
            cv2.imshow("Video", img)
        
        if cv2.waitKey(1) & 0xFF == ord('q'):
            break
        
    cap.release()
    cv2.destroyAllWindows()
    
#
if __name__ == "__main__":
    main()

The error I'm getting is as follows:

cv2.error: OpenCV(4.5.5) :-1: error: (-5:Bad argument) in function 'line'
> Overload resolution failed:
>  - Can't parse 'pt1'. Sequence item with index 0 has a wrong type
>  - Can't parse 'pt1'. Sequence item with index 0 has a wrong type

From what I've read from another thread, the tuples cv2.line is being passed have to be comprised of ints.

I've tried:

  1. iterating through bbox and casting each value as ints
for j in range(n):
    cv2.line(img, 
             tuple(int(bbox[j][0])), 
             tuple(int(bbox[ (j+1) % n][0])), 
             (255,0,0), 
             3)
  1. using .astype(int)
bbox = bbox.astype(int)
for j in range(n):
    cv2.line(img, 
             tuple(bbox[j][0]), 
             tuple(bbox[ (j+1) % n][0]), 
             (255,0,0), 
             3)

Edit: the contents of bbox (four corners of a single bounding box) are as follows:

[[[134.13043 150.     ]
  [362.3125  150.     ]
  [360.64886 362.94632]
  [143.58028 367.34634]]]

Any suggestions would be greatly appreciated!

Christoph Rackwitz
  • 11,317
  • 4
  • 27
  • 36
jhe4x
  • 41
  • 1
  • 6
  • cv2.line wants arguments 1 and 2 to be tuples containing the x and y coordinates of the start and end points of the line. Even if the cast is removed, the same error occurs. – jhe4x Mar 09 '22 at 08:54
  • thanks for getting me to investigate this again. OpenCV learns new things all the time and this is one such instance. practical advice: update your version of OpenCV. – Christoph Rackwitz Mar 09 '22 at 11:01
  • since you appear new (in terms of activity) on this site, please take the [tour]. if my answer has solved your problem, please mark it as accepted. – Christoph Rackwitz Mar 16 '22 at 11:20

1 Answers1

7

OpenCV wants a tuple of integers there, while you give it a tuple of numpy floats.

Yes, the error message doesn't clearly say that it wants integers.

This error happens with many drawing functions of OpenCV. The usual ones are cv.line, cv.rectangle, cv.circle, ...

The fix: convert the array to integer with the_array.astype(int), i.e. say something like:

cv.line(..., pt1=bbox[j][0].astype(int), ...)

You could also add proper rounding and say the_array.round().astype(int)

That seems to work on current OpenCV (tested with 4.5.4) because its bindings generation seems to have learned a few new tricks. Previously, one had to do a few more things:

  • give it an actual tuple, not just a numpy array
  • make sure the integers are python ints, not numpy ints

Like so:

(x,y) = bbox[j][0]
cv.line(..., pt1=((int(x), int(y)), ...)
Christoph Rackwitz
  • 11,317
  • 4
  • 27
  • 36