opencv —— floodFill 漫水填充,证件照换背景
Posted bjxqmy
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了opencv —— floodFill 漫水填充,证件照换背景相关的知识,希望对你有一定的参考价值。
漫水填充:floodFill 函数
简单来说,漫水填充就是自动选中与种子像素相连的区域,利用指定颜色进行区域颜色填充。Windows 画图工具中的油漆桶功能和 Photoshop 的魔法棒选择工具,都是漫水填充的改进和延伸。
//第一个版本
int floodFill(InputOutputArray image, Point seedPoint, Scalar newVal, Rect* rect = 0, Scalar loDiff = Scalar(), Scalar upDiff = Scalar(), int flags = 4);
//第二个版本,因为 mask 的原因,一般第二个版本效果好
int floodFill(InputOutputArray image, InputOutputArray mask, Point seedPoint, Scalar newVal, Rect* rect = 0, Scalar loDiff = Scalar(), Scalar upDiff = Scalar(), int flags = 4);
-
image,输入输出图像。
-
mask,操作掩膜,应该为单通道,8 位,长和宽上都比输入图像 image 大两个像素点的图像(左右上下各多出一列像素)。需注意的是,漫水填充不会填充掩膜 mask 的非零区域,所以一个边缘检测算子的输出可以用来作为掩膜,以防止填充到边缘。
-
seedPoint,漫水填充算法的起点。
-
newVal,像素点被染色的值,即在重绘区域像素的新值。
-
rect,默认为 0,用于设置 floodFill 函数将要重绘的最小边界矩形区域,即若漫水填充区域 < rect,则不进行填充。
-
loDiff,有默认值 Scalar(),表示当前观察像素值与其邻域像素值或待加入的种子像素值之间的亮度或颜色的最大负差。
-
upDiff ,有默认值 Scalar(),表示当前观察像素值与其邻域像素值或待加入的种子像素值之间的亮度或颜色的最大正差。
-
flags,int 类型操作标识符,默认值为 4,一共 23 位。
- 低八位(0~7):用于控制算法的连通性,可取 4(默认值)或 8。如果设为 4,表示填充算法只考虑当前像素水平或处置方向的相邻点,如果设为 8,除上述相邻点外,还会包含对角线方向的相邻点。
- 高八位(16~23):可以为 0,或者以下两种选择标识符的组合。
FLOODFILL_FIXED_RANGE:如果设置为这个标识符,就会考虑当前像素与种子之间的差,否则就考虑当前像素与其邻域像素的差。
FLOODFILL_MASK_ONLY,如果设置为这个标识符,函数不会去填充改变原始图像,而是去填充掩膜图像(只对第二个版本的函数有用)。
-
-
中间八位(8~15):用于指定填充掩码图像的值的,如果中间八位的值为 0,则掩码会用 1 来填充。
-
所有 falg 可以用 “|” 连接起来。例如想用 8 邻域填充,并填充固定像素范围,填充掩码而不是填充原图,以及设置填充值为 38,那么输入的参数为
flags = 8 | FLOODFILL_MASK_ONLY | FLOODFILL_FIXED_RANGE |(38 << 8)
证件照换背景代码示例:
#include<opencv.hpp>
#include<iostream>
using namespace std;
using namespace cv;
int main() {
//背景填充颜色
Scalar input_color = Scalar(0, 0, 255);
//读入图片
Mat src = imread("C:/Users/齐明洋/Desktop/证件照/9.jpg");
imshow("src", src);
//边缘检测,作为掩膜,防止漫水填充漫过边缘
Mat canny_img;
int thresh_data = 55;
Canny(src, canny_img, thresh_data, thresh_data * 2, 3);
imshow("canny_img", canny_img);
//将掩膜各方向扩充一列像素
Mat maskers = Mat(src.rows + 2, src.cols + 2, CV_8UC1, Scalar(0));
Mat tem_roi = maskers(Rect(Point(1, 1), Point(maskers.cols - 1, maskers.rows - 1)));
canny_img.copyTo(tem_roi);
//分别从原图像左右两侧进行漫水填充
Mat flood_img = src.clone();
floodFill(flood_img, maskers, Point(7, 7), Scalar(0, 0, 0), 0, Scalar(9, 9, 9), Scalar(9, 9, 9));
floodFill(flood_img, maskers, Point(src.cols - 7, 7), Scalar(0, 0, 0), 0, Scalar(9, 9, 9), Scalar(9, 9, 9));
imshow("flood_img", flood_img);
//生成二值图像,方便处理图像毛边
Mat bin_img;
cvtColor(flood_img, bin_img, COLOR_BGR2GRAY);
threshold(bin_img, bin_img, 1, 255, THRESH_BINARY);
//毛边处理
medianBlur(bin_img, bin_img, 13);
Mat erode_kernel = getStructuringElement(MORPH_ELLIPSE, Size(5, 5));
erode(bin_img, bin_img, erode_kernel);
blur(bin_img, bin_img, Size(5, 5));
imshow("bin_img", bin_img);
//边界与背景过渡处理
//二值图像因为 blur(均值滤波)处理,边缘地带像素值成坡状分布
//与 255 的比可作为原图像素占比,实现边界颜色过渡
Mat dst = Mat(src.rows, src.cols, src.type(), input_color);
for (int i = 0; i < src.rows; i++) {
for (int j = 0; j < src.cols; j++) {
int tem_color = bin_img.at<uchar>(i, j);
if (tem_color == 255) {
dst.at<Vec3b>(i, j) = src.at<Vec3b>(i, j);
}
else if (tem_color != 0) {
double alpha = tem_color / 255.0;
double beta = 1 - alpha;
int b = saturate_cast<uchar>(input_color[0] * beta + src.at<Vec3b>(i, j)[0] * alpha);
int g = saturate_cast <uchar>(input_color[1] * beta + src.at<Vec3b>(i, j)[1] * alpha);
int r = saturate_cast <uchar>(input_color[2] * beta + src.at<Vec3b>(i, j)[2] * alpha);
dst.at<Vec3b>(i, j)[0] = b;
dst.at<Vec3b>(i, j)[1] = g;
dst.at<Vec3b>(i, j)[2] = r;
}
}
}
imshow("dst", dst);
waitKey(0);
}
结果展示:
(这个方法并不完美,但是确实是我结合现阶段所掌握的知识,做出的最优解决方案了,加油加油!)
借鉴博客:https://www.cnblogs.com/little-monkey/p/7598529.html
以上是关于opencv —— floodFill 漫水填充,证件照换背景的主要内容,如果未能解决你的问题,请参考以下文章
OpenCV入门教程之十五水漫金山:OpenCV漫水填充算法(Floodfill)