使用 cv2/opencv 绘制 "soft" 个具有透明边缘的圆
Drawing "soft" circles with transparent edges with cv2/opencv
我需要使用 cv2 在图像上绘制 "soft" 白色圆圈(半透明边框),但我在文档中只能找到如何绘制带有硬边框的 100% 不透明圆圈。有谁知道我该怎么做,或者至少创造出圆圈 "fade out" 在边缘的错觉?
我想学习一下我的 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 。
糟糕,我刚刚意识到您正在寻找 Python 解决方案。希望我的代码能给你一些生成软圆蒙版的想法,我找到了一篇文章 here,它向你展示了一些 Python 风格的方法,你可以将其与我的代码混搭。
我需要使用 cv2 在图像上绘制 "soft" 白色圆圈(半透明边框),但我在文档中只能找到如何绘制带有硬边框的 100% 不透明圆圈。有谁知道我该怎么做,或者至少创造出圆圈 "fade out" 在边缘的错觉?
我想学习一下我的 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
糟糕,我刚刚意识到您正在寻找 Python 解决方案。希望我的代码能给你一些生成软圆蒙版的想法,我找到了一篇文章 here,它向你展示了一些 Python 风格的方法,你可以将其与我的代码混搭。