【问题标题】:Drawing "soft" circles with transparent edges with cv2/opencv使用 cv2/opencv 绘制具有透明边缘的“软”圆
【发布时间】:2018-07-18 06:13:38
【问题描述】:

我需要使用 cv2 在图像上绘制“软”白色圆圈(半透明边框),但我在文档中只能找到如何使用硬边框绘制 100% 不透明的圆圈。有谁知道我怎么能做到这一点,或者至少造成圆圈在边缘“淡出”的错觉?

【问题讨论】:

  • 可能有更简单的方法,但您可以在程序开始时在单独的 4 通道 (BGRA) 垫子中创建一个大正方形并用白色填充它。然后使 alpha 通道以径向方式从中间的实心(不透明)变为边缘的透明 - 可能使用 hypot()。当你想要一个柔和的圆圈时,调整它的大小并将它的 alpha 混合到你的图像上jepsonsblog.blogspot.co.uk/2012/10/…
  • 或者在所述的alpha通道中画一个实心圆并模糊它。
  • 谢谢! Alpha 通道中的实心圆圈 + 模糊它似乎是一个不错的策略。
  • 嗯,我试过了,不是很好——第一个想法效果更好。

标签: python opencv cv2


【解决方案1】:

我想稍微提高一下我的 OpenCV 技能 - 并且学到了很多 - 很酷的问题!

我生成了 alpha 值的单通道图像 - 浮点数可减少舍入误差,单通道可节省一些内存。这表示您的圈子有多少部分在背景中可见。

圆有一个外半径——它变得完全透明的点和一个内半径,它停止完全不透明的点。这两者之间的半径将消失。因此,将 IRADIUS 设置为非常靠近 ORADIUS 以实现陡峭、快速的衰减,并将其设置得远离 ORADIUS 以实现较慢的衰减。

我使用 ROI 将圆圈定位在背景上,并通过仅遍历背景的必要矩形来加快速度。

唯一棘手的部分是 Alpha 混合或合成。你只需要知道输出图像中每个像素的公式是:

out = (alpha * foreground) + (1-alpha) * background

这里是代码。我在OpenCV 不是世界上最好的,所以可能有些部分可以优化!


////////////////////////////////////////////////////////////////////////////////
// main.cpp
// Mark Setchell
////////////////////////////////////////////////////////////////////////////////
#include <opencv2/opencv.hpp>
#include <vector>
#include <cstdlib>

using namespace std;
using namespace cv;

#define ORADIUS 100 // Outer radius
#define IRADIUS  80 // Inner radius

int main()
{
   // Create a blue background image
   Mat3b background(400,600,Vec3b(255,0,0));

   // Create alpha layer for our circle normalised to 1=>solid, 0=>transparent 
   Mat alpha(2*ORADIUS,2*ORADIUS,CV_32FC1);

   // Now draw a circle in the alpha channel
   for(auto r=0;r<alpha.rows;r++){
      for(auto c=0;c<alpha.cols;c++){
         int x=ORADIUS-r;
         int y=ORADIUS-c;
         float radius=hypot((float)x,(float)y);
         auto& pixel = alpha.at<float>(r,c);
         if(radius>ORADIUS){ pixel=0.0; continue;}      // transparent
         if(radius<IRADIUS){ pixel=1.0; continue;}      // solid
         pixel=1-((radius-IRADIUS)/(ORADIUS-IRADIUS));  // partial
      }
   }

   // Create solid magenta rectangle for circle
   Mat3b circle(2*ORADIUS,2*ORADIUS,Vec3b(255,0,255));

#define XPOS 20
#define YPOS 120
   // Make an ROI on background where we are going to place circle
   Rect ROIRect(XPOS,YPOS,ORADIUS*2,ORADIUS*2);
   Mat  ROI(background,ROIRect);

   // Do the alpha blending thing
   Vec3b *thisBgRow;
   Vec3b *thisFgRow;
   float *thisAlphaRow;
   for(int j=0;j<ROI.rows;++j)
   {
       thisBgRow    = ROI.ptr<Vec3b>(j);
       thisFgRow    = circle.ptr<Vec3b>(j);
       thisAlphaRow = alpha.ptr<float>(j);
       for(int i=0;i<ROI.cols;++i)
       {
          for(int c=0;c<3;c++){   // iterate over channels, result=circle*alpha + (1-alpha)*background
             thisBgRow[i][c] = saturate_cast<uchar>((thisFgRow[i][c]*thisAlphaRow[i]) + ((1.0-thisAlphaRow[i])*thisBgRow[i][c]));
          }
       }
   }

   imwrite("result.png",background);
   return 0;
}

这是IRADIUS=80:

这是IRADIUS=30:

感谢 @Micka 分享他的代码以迭代 ROI here

糟糕,我刚刚意识到您正在寻找 Python 解决方案。希望我的代码能给你一些关于生成软圆蒙版的想法,我找到了一篇文章here,它向你展示了一些 Python 风格的方法,你可以将它们与我的代码混搭。

【讨论】:

  • 非常感谢!是的,即使我要求提供 python 解决方案,您的代码肯定会有所帮助(并且可能是我在这个网站上得到的最有用的答案:p)
  • 太棒了!祝你的项目好运。
猜你喜欢
  • 1970-01-01
  • 2015-04-08
  • 2016-06-09
  • 1970-01-01
  • 2020-02-15
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-07-01
相关资源
最近更新 更多