使用opencv实现自定义抠图

使用opencv实现自定义抠图

  • 导语
    • 环境
    • 原理
    • 设计思路
    • 代码实现
    • 原图
    • 运行效果图片

导语

寒假期间也都是基本学些比较基础的东西,也没有做些什么。
这次是突然想换头像,电脑上又没有Photoshop,就想着自己实现一个简单的抠图程序,纯属是好玩而已。
本次程序就直接采用暴力的方法对每个像素点进行修改,如果在算法上有什么指教的也可以相互讨论

环境

Qt Creator 4.8.1(community)
OpenCV 4.0版本

原理

原理也不写了,主要用到的知识点有(想学原理的话CSDN上面很多文章都有,可以自行百度):
--漫水填充
--canny算子
--图像二值化
--回调函数(其实回调函数这技术也是很牛逼的,setMouseCallback和createTrackbar会用到)
--OpenCV库函数([OpenCV官方手册](https://docs.opencv.org/4.0.0/))

设计思路

一、读入一张图片,这里我用哆啦A梦的图片
二、创建如下几个图片副本(大小和原始图片一样大),用于各个阶段显示效果
三、setMouseCallback函数的使用,主要实现的是在经过处理后的二值图上进行边框的描
		绘(勾画出自己想要抠的图的区域),其对应的第二个参数(一个回调函数,代码中)
		实现的是press函数。并在另外的Mat对象中存储勾画图的位置,用于下面第四点使用。
四、createTrackbar函数的使用(整个程序最核心的部分,用法在OpenCV官方手册里面有,也可
	以去CSDN找,会有更详细的解释,主要的是第5个参数,find_contour_demo函数)
	首先是使用canny进行边缘检测(这张是原图的二值化图像),然后使用findContours函数
	找出图片的轮廓并使用drawContours画出轮廓图(画出来的为彩色图,再转为灰度图),接
	着将灰度的轮廓图和自己画的轮廓图进行重合比较,如果既是自己画的轮廓,有事canny算子
	算出来的轮廓,就是自己想要的那部分的轮廓,这样可以提高想要勾画的准确度(这里必须是
	一个封闭的图形,不然无法进行下面的漫水填充)。经过上一步,就可以得到一张黑底的,白
	色边框的轮廓图(下面第5张图片,窗口名称为roi_before),接着对这张图片进行漫水填充
	(OpenCV中的floodFill函数),然后得到自己想抠的区域为白色的图片,将这张图片和原图
	进行对比,如果这张图片值为0(即黑色),那么原图相应的点颜色就为Scalar(255,225,
	255)(白色)。

代码实现

#include 
#include 
#include 
#include 
#include 
using namespace cv;
using namespace std;

void find_contour_demo(int,void*);//回调函数,用于createTrackbar,进行边缘检测,并画出轮廓图
void press(int event,int x,int y,int flag,void* param);//回调函数,用于setMouseCallback,用于鼠标按下左键时画轮廓用的函数
static Mat base;//用于读取图片,作为原始图片
static Mat source,canny,copy1;//分别为资源图片,轮廓线在这张图上显示;canny用于边缘检测的图片,copy1用来显示为二值化的图片,即最初的那张图片。
static int value=100;//阈值变量的初始值,用于createTrackbar函数
static Mat contour;//显示自己用鼠标画出来的轮廓;
static Mat roi;//将canny处理后,画出来的轮廓图和contour自己画的轮廓重合的部分作为要抠选的区域
int main()
{
    //图像二值化,初始化数据
    base=imread("qq.jpg");
    base.copyTo(source);
    base.copyTo(copy1);
    cvtColor(source,source,COLOR_BGR2GRAY);
    contour=Mat::zeros(source.size(),source.type());

    //设置窗口名称
    namedWindow("source"  /*,WINDOW_NORMAL*/);
    namedWindow("roi"   /*,WINDOW_NORMAL*/);
    namedWindow("roi_before");
    namedWindow("result"   /*,WINDOW_NORMAL*/);

    imshow("source",source);

    //设置鼠标事件
    setMouseCallback("source",press,(void*)&source);

    //创建阈值的滑动按键,初始值为100
    createTrackbar("threshold value","source",&value,255,find_contour_demo);
    find_contour_demo(0,nullptr);


    waitKey(0);
    return 0;
}
void find_contour_demo(int,void*)
{
    //使用canny边缘检测的图片
    Canny(copy1,canny,value,value*2,3,false);
    imshow("canny detection", canny);

    //查找轮廓,并显示彩色轮廓,接下来将彩色轮廓转为灰度,用于roi设置
    vector> contours;
    vector> hierachy;
    findContours(canny,contours,hierachy,RETR_TREE, CHAIN_APPROX_SIMPLE, Point(0, 0));
    Mat drawImage = Mat::zeros(source.size(), CV_8UC3);//边缘检测后的轮廓图,用彩色画出来
    RNG rng(12345);
    for(int i=0;i(i)[j]==255) && (drawImage.ptr(i)[j]!=0) )
                roi.ptr(i)[j]=255;

    //设置种子,用于水漫填充,即找到roi区域内的任意一点黑点
    Point seed;
    int flag=0;
    for(int i=0;i(i)[j]==255)&&(contour.ptr(i+5)[j]==0))
            {
                seed.y=5+i;
                seed.x=j;
                flag=1;
                break;
            }
        }
        if(flag==1)
            break;
    }

    //进行漫水填充
    imshow("roi_before",roi);
    floodFill(roi, seed, Scalar(255,255,255), nullptr,Scalar(20, 20, 20),Scalar(20, 20, 20));
    imshow("roi",roi);


    //显示最终效果图片,底色为白色,用漫水填充的图片,如果值为0(黑色),那么就在原图上将原值改为255(白色),即为白底
    for (int i=0;i(i)[j]==0)
            {
                base.ptr(i)[base.channels()*j]=255;
                base.ptr(i)[base.channels()*j+1]=255;
                base.ptr(i)[base.channels()*j+2]=255;
            }
        }
    }
    imshow("result",base);
    return;
}
void press(int event,int y,int x,int flag,void* param)
{
    //左键按下时候的事件,在二值图上画出黑线轮廓
    Mat* pic=(Mat*)param;
    if(flag==EVENT_LBUTTONDOWN)
    {
        if(!(*(pic->ptr(x)+y)==0))
        {
            cout<ptr(x)+y))<ptr(x+i)+y+j)=0;
                    contour.ptr(x+i)[y+j]=255;
                }

        }
    }
//    imshow("contour",contour);
    imshow("source",source);
}

原图

使用opencv实现自定义抠图_第1张图片

运行效果图片

使用opencv实现自定义抠图_第2张图片
使用opencv实现自定义抠图_第3张图片
使用opencv实现自定义抠图_第4张图片
使用opencv实现自定义抠图_第5张图片
使用opencv实现自定义抠图_第6张图片
使用opencv实现自定义抠图_第7张图片

你可能感兴趣的:(Opencv)