Ask Your Question
3

How to draw gradient?

asked 2016-06-21 17:35:53 -0600

VanGog gravatar image

updated 2016-09-27 14:11:14 -0600

How can I draw gradient like this?

image description

I want to draw gradient of circle.

First idea, I could create a white line and empty image I. Then to go through loop with angle from 0 to 89, in every iteration do I++; and angle++; rotate this line with fixed origin at x=radius, y=0; This would create one image of gradient. Then I need to copy the image 3 times in a loop rotating it +90° in every iteration.

Now I ask what functions to use to copy the pixels from the original line to the image using rotation and how to do the same with an image. It's basically the same difference is only in image dimensions. That line is radius,0 and image dimensions are radius*2,radius,2

What I tried after Lorena pointed me to try linearPolar:

int radius = 4;
int side = 2*radius;
int size = 1 + 2*side;
Mat I = Mat::zeros(size, size, CV_8UC1);
Point center; 
center.x = 2*radius+1 -1; center.y = 2*radius+1 -1;
int sigma = radius%2==0 ? radius+1 : radius;
cv::Size kernelSize = CvSize(sigma, sigma);

Mat L = Mat::zeros(1, center.x, CV_8UC1);
I = Mat::zeros(size, size, CV_8UC1);
Point a,b;

L = Mat::zeros(center.x, 1, CV_8UC1);
a.x=0; a.y = 0; b.x=0; b.y = radius-1;
line( L, a, b, cv::Scalar( 255, 255, 255 ), 1, 8 );
cv::GaussianBlur( L, L, kernelSize, sigma );

imshow("blured line",L);
waitKey(0);
cv::Size dsize= cv::Size(radius*10, radius*10);

resize(L, I, dsize );
Point2f  fcenter; 
fcenter.x = (float) radius/16 ;
fcenter.y = (float) radius/16 ;
cv::linearPolar(I, I, fcenter, radius, INTER_LINEAR );
imshow("linearPolar",I);
waitKey(0);

The resulting image contains some gradient which is horizontal instead circular.

Edit: New code (radius 25) - same problem:

// Mat::zeros( 5, 5, CV_8UC3 );
int radius = 25;
int side = 2*radius;
int size = 1 + 2*side;
Mat I = Mat::zeros(size, size, CV_8UC1);
Point center; 
center.x = 2*radius+1 -1; center.y = 2*radius+1 -1;
/*
circle( I,
    center,
    radius,
    cv::Scalar( 255, 255, 255 ),
    -1, // when thickness is negative, filled circle is drawn
    8 );
imshow("circle",I);
waitKey(0);*/
int sigma = radius%2==0 ? radius+1 : radius;
cv::Size kernelSize = CvSize(sigma, sigma);
/*
cv::GaussianBlur( I, I, kernelSize, sigma );
imshow("blured circle",I);
waitKey(0);*/

/** ANGLE GRADIENT **/
Mat L = Mat::zeros(1, center.x, CV_8UC1);
I = Mat::zeros(size, size, CV_8UC1);

Point a,b; 
/*
a.x=0; a.y = 0; b.x=radius; b.y = 0;
int c = 255;
line( I, a, b, cv::Scalar( c ), 1, 8 );
*/

/** ANGLE GRADIENT **/
L = Mat::zeros(center.x, 1, CV_8UC1);
a.x=0; a.y = 0; b.x=0; b.y = radius-1;
line( L, a, b, cv::Scalar( 255, 255, 255 ), 1, 8 );
cv::GaussianBlur( L, L, kernelSize, sigma );

imshow("blured line",L);
waitKey(0);
cv::Size dsize= cv::Size(size, size);

resize(L, I, dsize );
Point2f  fcenter; 
fcenter.x = (float) radius ;
fcenter.y = (float) radius ...
(more)
edit retag flag offensive close merge delete

Comments

I have no idea what you're asking. Try providing more detail.

Tetragramm gravatar imageTetragramm ( 2016-06-21 22:11:08 -0600 )edit

@Tetragramm: I updated the answer. Now I need some function, that can copy image pixels using rotation. (Not the same as rotate the image)

VanGog gravatar imageVanGog ( 2016-06-22 01:57:23 -0600 )edit
2

I guess you want something like linearPolar()

LorenaGdL gravatar imageLorenaGdL ( 2016-06-22 02:07:23 -0600 )edit

@LorenaGdL: Do you mean to create horizontal gradient and then send the object to the function LinearPolar? Will the gradient be non-deformed?

VanGog gravatar imageVanGog ( 2016-06-22 03:35:12 -0600 )edit

@LorenaGdL: I tried but it did not worked for me. I added new code to question. I see some gradient which is deformed and over it is another horizontal gradient of cloud shape. How to correct this?

VanGog gravatar imageVanGog ( 2016-06-22 04:33:04 -0600 )edit

2 answers

Sort by » oldest newest most voted
11

answered 2016-06-22 15:52:48 -0600

LorenaGdL gravatar image

cv::linearPolar is the answer, but you need to set proper flags (WARP_INVERSE_MAP). Simple example below

//Set linear gradient (255 gray levels)
Mat lines(255, 255, CV_8U, Scalar(0));
for (int r = 0; r < lines.rows; r++)
{
    lines.row(r).setTo(r);
}
namedWindow("Linear Gradient", CV_WINDOW_NORMAL);
imshow("Linear Gradient", lines);

//Convert to polar (needs WARP_INVERSE_MAP flag)
cv::linearPolar(lines, lines, cv::Point(lines.cols / 2, lines.rows / 2), 255, INTER_CUBIC | WARP_FILL_OUTLIERS | WARP_INVERSE_MAP);
namedWindow("Polar Gradient", CV_WINDOW_NORMAL);
imshow("Polar Gradient", lines);

//Mask out circle section
Mat mask(lines.size(), CV_8U, Scalar(0));
circle(mask, cv::Point(mask.cols / 2, mask.rows / 2), lines.rows/2, Scalar(255), -1);
Mat circle_gradient;
lines.copyTo(circle_gradient, mask);
namedWindow("Circle Gradient", CV_WINDOW_NORMAL);
imshow("Circle Gradient", circle_gradient);
waitKey(0);

linear gradient polar gradient circle gradient

edit flag offensive delete link more

Comments

Thank you very much. This is useful very much.

VanGog gravatar imageVanGog ( 2016-06-23 01:50:22 -0600 )edit

One note though. If I change the size of image to 25px I need to change it a bit.

VanGog gravatar imageVanGog ( 2016-06-23 02:06:34 -0600 )edit
1

Obviously, if you want a smaller image code should be changed. Although I would keep the code as is for simplicity, and then resize the final gradient using cv::resize()

LorenaGdL gravatar imageLorenaGdL ( 2016-06-23 02:09:28 -0600 )edit
1

Yeah, but I need mathematical accurancy. If you use image editor you can see the image is blured in the 358-359° and that is problem. I can correct it in image editing software but it would be useless when I would use it as a build in solution. I have done recalculation for the smallest image (my question been updated). Just it is not perfect because I used floats and divisions of floats so it was a bit complicated.

VanGog gravatar imageVanGog ( 2016-06-23 10:23:59 -0600 )edit
1

Blur/slice at 360º slice is a known problem which has been tried to be solved (with not much success - #6189, #6206). You'll have to find a workaround for your exact problem. Anyway, imho, trying to achieve mathematical accuracy in a circular gradient of 255 levels but 12px radius (i.e. 75.398 px perimeter length) is quite strange/difficult...at least working with 1-pixel accuracy.

LorenaGdL gravatar imageLorenaGdL ( 2016-06-23 10:48:00 -0600 )edit

I will use float, this was for a testing purpose. I will find another way how to do it more exactly using 360 as a maximum value.

VanGog gravatar imageVanGog ( 2016-06-23 11:32:36 -0600 )edit
0

answered 2016-06-22 13:44:45 -0600

Tetragramm gravatar image

I would use the function cv::line. It draws a line between two points. So you set the first point to the center of the circle, and calculate 256 points along the edge of the circle. Then you draw a line between the center and each of those points with a steadily increasing value for the color parameter. You can change the thickness if the radius is too large for thickness one.

edit flag offensive delete link more

Question Tools

2 followers

Stats

Asked: 2016-06-21 17:35:53 -0600

Seen: 11,703 times

Last updated: Jun 23 '16