漫水填充: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, 200);//背景顏色 //讀入圖片
Mat src = imread("C:/Users/齊明洋/Desktop/證件照/7.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);//消除椒鹽噪聲
blur(bin_img, bin_img, Size(5, 5));//使邊緣像素呈梯度分布
imshow("bin_img", bin_img); //邊界處理
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 - 0.25;//原始顏色占比
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
