用渐变填充圆圈

时间:2017-09-28 10:12:14

标签: c++ opencv

我想要用渐变色填充圆圈,就像我在底部显示的那样。我无法找到简单的方法,如何做到这一点。 我可以制作更多圆圈,但过渡是可见的。

cv::circle(img, center, circle_radius * 1.5, cv::Scalar(1.0, 1.0, 0.3), CV_FILLED);
cv::circle(img, center, circle_radius * 1.2, cv::Scalar(1.0, 1.0, 0.6), CV_FILLED);
cv::circle(img, center, circle_radius, cv::Scalar(1.0, 1.0, 1.0), CV_FILLED);

enter image description here

3 个答案:

答案 0 :(得分:5)

您需要做的就是创建一个函数,该函数接收中心点和新点,计算距离,并返回该点的灰度值。或者,您可以返回距离,在该点存储距离,然后使用cv::normalize()缩放整个内容。

因此,假设您在(50, 50)图片中的中心点为(100, 100)。这是你想要做的伪代码:

function euclideanDistance(center, point)  # returns a float
    return sqrt( (center.x - point.x)^2 + (center.y - point.y)^2 )

center = (50, 50)
rows = 100
cols = 100

gradient = new Mat(rows, cols) # should be of type float

for row < rows:
    for col < cols:
        point = (col, row)
        gradient[row, col] = euclideanDistance(center, point)

normalize(gradient, 0, 255, NORM_MINMAX, uint8)
gradient = 255 - gradient

请注意以下步骤:

  1. 创建欧几里德距离函数以计算距离
  2. 创建一个浮点矩阵来保存距离值
  3. 遍历所有行和列并指定距离值
  4. 标准化到你想要的范围(你可以在这里坚持使用浮点数而不是强制转换为uint8,但是你做到了)
  5. 翻转二进制渐变,因为距离更远的距离会更亮 - 但你想要相反。
  6. 现在,对于您的确切示例图像,圆圈中存在渐变,而此方法仅将整个图像创建为渐变。在您的情况下,如果您想要一个特定的半径,只需修改计算欧几里德距离的函数,如果它超出一定距离,则将其设置为0(圆心的值,最终将翻转为白色) :

    function euclideanDistance(center, point, radius)  # returns a float
        distance = sqrt( (center.x - point.x)^2 + (center.y - point.y)^2 )
        if distance > radius:
            return 0
        else
            return distance
    

    以上是实际的C ++代码:

    #include <opencv2/opencv.hpp>
    #include <opencv2/highgui/highgui.hpp>
    #include <cmath>
    
    float euclidean_distance(cv::Point center, cv::Point point, int radius){
        float distance = std::sqrt(
            std::pow(center.x - point.x, 2) + std::pow(center.y - point.y, 2));
        if (distance > radius) return 0;
        return distance;
    }
    
    int main(){
    
        int h = 400;
        int w = 400;
        int radius = 100;
        cv::Mat gradient = cv::Mat::zeros(h, w, CV_32F);
    
        cv::Point center(150, 200);
        cv::Point point;
    
        for(int row=0; row<h; ++row){
            for(int col=0; col<w; ++col){
                point.x = col;
                point.y = row;
                gradient.at<float>(row, col) = euclidean_distance(center, point, radius);
            }
        }
    
        cv::normalize(gradient, gradient, 0, 255, cv::NORM_MINMAX, CV_8U);
        cv::bitwise_not(gradient, gradient);
    
        cv::imshow("gradient", gradient);
        cv::waitKey();
    
    }
    

    Gradient image

    完全不同的方法(虽然做同样的事情)是使用distanceTransform()。此函数将从白色斑点中心到最近的黑色值的距离映射到灰度值,就像我们上面所做的那样。这段代码更简洁,做同样的事情。但是,它可以处理任意形状,而不仅仅是圆形,所以很酷。

    #include <opencv2/opencv.hpp>
    #include <opencv2/highgui/highgui.hpp>
    
    int main(){
    
        int h = 400;
        int w = 400;
        int radius = 100;
        cv::Point center(150, 200);
        cv::Mat gradient = cv::Mat::zeros(h, w, CV_8U);
        cv::rectangle(gradient, cv::Point(115, 100), cv::Point(270, 350), cv::Scalar(255), -1, 8 );
    
        cv::Mat gradient_padding;
        cv::bitwise_not(gradient, gradient_padding);
    
        cv::distanceTransform(gradient, gradient, CV_DIST_L2, CV_DIST_MASK_PRECISE);
        cv::normalize(gradient, gradient, 0, 255, cv::NORM_MINMAX, CV_8U);
    
        cv::bitwise_or(gradient, gradient_padding, gradient);
    
        cv::imshow("gradient-distxform.png", gradient);
        cv::waitKey();
    
    }
    

    Gradient from distance transform

答案 1 :(得分:5)

你必须绘制很多圆圈。每个圆的颜色取决于距中心的距离。这是一个简单的例子:

void printGradient(cv::Mat &_input,const cv::Point &_center, const double radius)
{
   cv::circle(_input, _center, radius, cv::Scalar(0, 0, 0), -1);

   for(double i=1; i<radius; i=i++)
   {
       const int color = 255-int(i/radius * 255); //or some another color calculation
       cv::circle(_input,_center,i,cv::Scalar(color, color, color),2);
   }  
}

结果:

enter image description here

答案 2 :(得分:0)

另一种未提及的方法是预先计算圆形渐变图像(使用所提到的方法之一,如接受的解决方案),并使用线性插值的仿射变形来创建其他此类圆形(不同大小)。如果优化翘曲和插值并且可能由硬件加速,则这可以更快。 结果可能比完美更糟糕。

我曾经用它来为每帧内窥镜成像创建一个单独的渐晕遮罩圆。比计算距离快#34;手动&#34;。