Drawing several circles in an additive way

asked 2020-10-09 00:29:10 -0600

Ediolot gravatar image

updated 2020-10-09 14:59:49 -0600

Hello ! I'm using cv2 in python to draw circles (representing stars) into a CV_8U image. I'm drawing on average 5k small circles, radius goes from 1px to 6px with antialiasing.

Im taking ~80ms for drawing ~5k or few circles and (e.g.) ~3200ms for ~80k circles. Because each circle has a different size and color, I'm actually calling cv2.circle each time I want to draw a circle which is for sure slowing the program down but no big deal (is there any other alternative to this anyway for different radius and colors?).

The issue is where some circles, for example, two red circles overlap:

circle A with radius=5px and color rgb=(200,10,30) at xy=(200, 300)
circle B with radius=2px and color rgb=( 20, 2, 1) at xy=(200, 300)

If circle A is drawn before and circle B later, because circle B's brightness is so low I will be left with a circumference in red and a very dark interior instead of the ideal of making the second circle values add to the already existing values at that position (like adding two light sources).

My current solution which makes my program run an order of magnitude slower compared to the timing I wrote before is to draw the circles to a buffer the size of the biggest possible circle and then add that buffer to the specific position in the final image. But I'm wondering if is there a parameter in OpenCV to define how the drawings to the image are made? (to make it additive) and prevent the need for that buffer.

Thanks.

EDIT : ( added by @sturkmen to be more clear ) EDIT2: Update on the edit

import numpy as np
import cv2 as cv

img0 = np.zeros((12, 12, 3), np.uint8)
cv.circle(img0, (5, 5), 5, (30, 10, 200), -1, cv.LINE_AA)  # Big bright
cv.circle(img0, (5, 5), 2, (1, 2, 30), -1, cv.LINE_AA)  # Small dim
img0 = cv.resize(img0, (240, 240), interpolation=cv.INTER_AREA)
cv.imshow('current', img0)

img1 = np.zeros((12, 12, 3), np.uint8)
img2 = np.zeros((12, 12, 3), np.uint8)
cv.circle(img1, (5, 5), 5, (30, 10, 200), -1, cv.LINE_AA)  # Big bright
cv.circle(img2, (5, 5), 2, (1, 2, 20), -1, cv.LINE_AA)  # Small dim
img2 = cv.resize(img1 + img2, (240, 240), interpolation=cv.INTER_AREA)  # Img needs to be clipped [0,255]
cv.imshow('desired', img2)
cv.waitKey()

cv.destroyAllWindows()

current code result:

image description

desired result:

image description

edit retag flag offensive close merge delete

Comments

1

You could do blending over ROIs, maintaining a total light image. So rather than call cv::circle() directly on the image, call it on a small image that fits the circle and then add that small image to the appropriate ROI in the model/additive image. Normalize the additive image when done for viewing.

Der Luftmensch gravatar imageDer Luftmensch ( 2020-10-09 09:41:07 -0600 )edit

@Ediolot i edited your question according to my understanding. let us know if it is correct.

sturkmen gravatar imagesturkmen ( 2020-10-09 11:13:32 -0600 )edit
1

Thanks for the comments ! @sturkmen I made a small edit to your addition to show my current result and desired result. Also, I'm already doing what @der-luftmensch suggested but it gets one order of magnitude slower. I'm wondering if there is another way to write a circle that is not destructive

Ediolot gravatar imageEdiolot ( 2020-10-09 14:40:35 -0600 )edit

just wonder,what is your image size?

sturkmen gravatar imagesturkmen ( 2020-10-09 15:03:07 -0600 )edit

Around 1000x500 pixels

Ediolot gravatar imageEdiolot ( 2020-10-09 18:14:57 -0600 )edit

i guess you have have all coordinates and color values before drawing. if so, maybe sorting them and making a function drawing merged two of them at once etc..

sturkmen gravatar imagesturkmen ( 2020-10-10 13:54:53 -0600 )edit