user2894356
user2894356

Reputation: 139

Can the OpenCv circle function be used to draw a circle with an odd diameter?

I would like to draw a circle with a diameter of 15 into a matrix of 15 by 15. For this I tried the OpenCv circle function and the shift function.

I am not sure whether I use the function in a correct way or wheather it is not possible what I would like to do.

The best I achieve is the following unsymmetric circle with a size of 16 pixels:

Unsymmetric circle

How can I get a symmetric circle with a diameter of 15 pixels?

The code I used:

    import cv2
    import numpy as np  

    circle_diameter = 15
    circular_mask = np.zeros((circle_diameter, circle_diameter, 1), dtype='uint8')
    #Draw circle with subpixel accuracy
    shift = 4
    factor = (1 << shift)
    cv2.circle(circular_mask, (int(round((circle_diameter/2) * factor)), int(round((circle_diameter/2) * factor))), int(round((circle_diameter/2) * factor)), (255), -1, shift=shift)

    circular_mask = cv2.resize(circular_mask,None,fx=5,fy=5)
    cv2.imshow("mask", circular_mask)

Thanx

Upvotes: 1

Views: 1895

Answers (2)

user2894356
user2894356

Reputation: 139

Mickas Code in Python:

import cv2
import numpy as np
circle_diameter = 15
circular_mask = np.zeros((circle_diameter+2, circle_diameter+2, 1), dtype='uint8') #Plus two for visualization
shift = 5
radius_low = np.floor(circle_diameter/2)
radius_shifted = (np.uint64(radius_low) << np.uint64(shift)) + (np.uint64(1)<<np.uint64((shift-1))) -1; # 7+ 2^-1 - 2^-5
circle_x = 8 << shift
circle_y = 8 << shift

cv2.circle(circular_mask, (circle_x,circle_y), int(radius_shifted), (255), -1, shift=shift)
circular_mask = cv2.resize(circular_mask,None,fx=5,fy=5)
cv2.imshow("mask", circular_mask)

Upvotes: 1

Micka
Micka

Reputation: 20130

Ok, here you go.

I'll write in C++ syntax, but should be same for Python.

It seems like the pixel coordinates in cv::circle refers to the pixel center.

    cv::Mat img = cv::Mat::zeros(15, 15, CV_8UC1);
    // in this code, shift must be >= 1 because of the later line (1<<(shift-1)) to add the .5 for an arbitrary shift size
    const int shift = 2;

    int radiusLow = 7;
    int radiusShift = (radiusLow << shift) + (1<<(shift-1)); // + .5

    //int x = (7 << shift) + (1<<(shift-1)); // wrong, because the pixel position seems to be the pixel center already. 7.5 would be the right ede of the pixel
    //int y = (7 << shift) + (1<<(shift-1)); // wrong, because the pixel position seems to be the pixel center already. 7.5 would be the right ede of the pixel

    int x = 7<<shift;
    int y = 7<<shift;

    cv::circle(img, cv::Point(x, y), radiusShift, cv::Scalar::all(255), -1, 8, shift);

    //cv::resize(img, img, cv::Size(), 50, 50); // for visualization

    cv::imshow("img", img);
    cv::waitKey(0);

But the result seems to have some pixel disrectization problems, though looks like as beeing centered and with 7.5 radius. Result is resized for visualization.

enter image description here

Same code (but smaller resize factor) with radius 6.5 gives this image (looks like some rounding fragments during drawing).

enter image description here

Another test, using more bits to represent a number close to 7.5 radius, but a few bits smaller, to reduce rounding fragments in drawing:

    cv::Mat img = cv::Mat::zeros(17, 17, CV_8UC1); // 2 pixels bigger for visualization of possible artifacts

    const int shift = 5; // more bits for fraction
    int radiusLow = 7;
    int radiusShift = (radiusLow << shift) + (1<<(shift-1)) -1; // 7+ 2^-1 - 2^-5 

    // center of the 17x17 image
    int x = 8<<shift;
    int y = 8<<shift;

enter image description here

Upvotes: 2

Related Questions