OpenCV-14-一些案例

1. 统计零件数量,可以采用腐蚀,但很多时候距离变换更适用

在这里插入图片描述

/**
    统计零件的数量
*/
#include <opencv2/opencv.hpp>
#include <iostream>

using namespace std;
using namespace cv;

int main(){
    // 图片路径
    string path = "/home/kaijun/Pictures/lingjian.png";
    //1. 读取图片
    Mat src = imread(path,IMREAD_COLOR);
    imshow("src",src);
    //2. 将图片转成灰度
    Mat gray;
    cvtColor(src,gray,COLOR_BGR2GRAY);

    // 将灰度图转成而制图,并且去掉背景
    Mat binary;
    threshold(gray,binary,0,255,THRESH_BINARY|THRESH_TRIANGLE);
    imshow("no background",binary);

    // 距离变换
    Mat dist;
    distanceTransform(binary,dist,DIST_L2,3);
    normalize(dist, dist, 0, 1.0, NORM_MINMAX);
    imshow("dist",dist);

    // 将距离变换的结果,通过阈值让图像粘连断开
    threshold(dist, dist, 0.5, 1.0, THRESH_BINARY);
    imshow("dist2",dist);

    // 统计图像的轮廓
    vector<vector<Vec2i>> contours;
    vector<Vec4i> hierarchy;

    cout<<dist.type()<<endl;
    dist.convertTo(dist,CV_8UC1);  //数据类型一定要留意
    findContours(dist,contours,hierarchy,RETR_LIST,CHAIN_APPROX_NONE);
	//获得零件数量
    cout<<contours.size()<<endl;

    // 绘制轮廓
    RNG rng(123456);
    Mat result(dist.size(),CV_8UC3);
    for (int i = 0; i < contours.size(); ++i) {
    drawContours(result,contours,i,Scalar(rng.uniform(0,255),rng.uniform(0,255),rng.uniform(0,255)),-1);
    }

    imshow("result",result);
    waitKey(0);
    return 0;
}

在这里插入图片描述
在这里插入图片描述

2. 图像修复、鼠标绘制区域作为mask,注意鼠标事件的使用

void inpaint( InputArray src, InputArray inpaintMask,OutputArray dst, double inpaintRadius, int flags );
src : 表示输入的图像
inpaintMask: 掩膜,其实就是要修复哪些区域
dst : 表示修复输出的图像
inpaintRadius: 表示修复半径
flags: 表示修复时所使用的算法,有CV_INPAINT_TELEA和CV_INPAINT_NS可选,处理效果差不多

在这里插入图片描述

/**
图片修复
 */
#include <opencv2/opencv.hpp>
#include <iostream>

using namespace cv;
using namespace std;

Point prevPt;
Mat src,inpaintMask;

void onMouse(int event, int x, int y, int flags, void* userdata){
    if (event == CV_EVENT_LBUTTONUP || !(flags & CV_EVENT_FLAG_LBUTTON))
        prevPt = Point(-1, -1);
    else if (event == CV_EVENT_LBUTTONDOWN)  //鼠标按下
        prevPt = Point(x, y);
        //或 (flags & EVENT_FLAG_LBUTTON),鼠标移动并且左键被按下
    else if (event == CV_EVENT_MOUSEMOVE && (flags & CV_EVENT_FLAG_LBUTTON))
    {
        Point pt(x, y);
        if (prevPt.x < 0)
            prevPt = pt;
        line(inpaintMask, prevPt, pt, Scalar::all(255), 5, 8, 0);//mask

        // 使用inpaint函数进行图片修复
        inpaint(src, inpaintMask, src, 5, CV_INPAINT_TELEA);

        prevPt = pt;  // 更新坐标点
        imshow("inpaint", src);
    }
}

int main(int argc,char** argv){
    string path = "/home/kaijun/Documents/resource/opencv/img/itheima_inpaint.jpg";
    src = imread(path,IMREAD_COLOR);
    // 定义窗口
    namedWindow("inpaint",WINDOW_NORMAL);
    // 定义鼠标事件
    setMouseCallback("inpaint",onMouse);
    // 显示原图
    imshow("inpaint",src);
    // 定义掩膜
    inpaintMask = Mat(src.size(),CV_8UC1);

    waitKey(0);
    return 0;
}
3. 图像切边,注意矩形的绘制及抠图方法

在这里插入图片描述

/**
 去除图片多余的背景
*/
#include <opencv2/opencv.hpp>
#include <iostream>

using namespace std;
using namespace cv;

void fetchROI(Mat &img);

int main(){
    string path = "/home/kaijun/Documents/resource/opencv/01_qiebian.jpg";
    Mat src = imread(path,IMREAD_COLOR);
    // 显示图片
    imshow("src",src);
    // 提取感兴趣的区域
    fetchROI(src);

    waitKey(0);
    return 0;
}

void fetchROI(Mat &img){
    // 将彩色图转成灰度图
    Mat gray(img.size(),CV_8UC1);
    cvtColor(img,gray,COLOR_BGR2GRAY);
    imshow("gray",gray);

    // 将图像二值化处理
    Mat binary(img.size(),CV_8UC1);
    threshold(gray,binary,0,255,THRESH_BINARY_INV|THRESH_OTSU);
    imshow("binary",binary);
    // 查找轮廓
    vector<vector<Vec2i>> contours;
    vector<Vec4i> hierarchy;
    findContours(binary,contours,hierarchy,CV_RETR_EXTERNAL,CV_CHAIN_APPROX_NONE);


    RotatedRect resultRect;

    // 遍历轮廓,求轮廓的最大外切矩形
    for(int i = 0; i<contours.size();i++){
        // 取到当前遍历轮廓
        RotatedRect rect = minAreaRect(contours[i]);
        if(rect.size.width > img.size().width*0.75){
            // 绘制轮廓
            //drawContours(img,contours,i,Scalar(0,255,255),2,LINE_AA);
            // 记录外切矩形
            resultRect = rect;
        }
    }

    Mat dst = img(resultRect.boundingRect());

    imshow("dst123",dst);
}
4. 图像的旋转和切边,注意图片旋转的方法,借助getRotationMatrix2D()函数生成旋转矩阵,再进行仿射变换
  1. 图像的旋转
 // 打印图片需要旋转的角度
cout<<resultRect.angle<<endl;

// 先对图片进行旋转
Point2f center(img.cols/2,img.rows/2);
Mat matrix = getRotationMatrix2D(center,(resultRect.angle)+90,1);

warpAffine(img,outputImg,matrix,img.size());
  1. 图像的切边

在这里插入图片描述

/**
 去除图片多余的背景,切边并且旋转
*/
#include <opencv2/opencv.hpp>
#include <iostream>

using namespace std;
using namespace cv;

void verifyRotation(Mat &img,Mat &outputImg);
void fetchROI(Mat &img);

int main(){
    string path = "/home/kaijun/Documents/resource/opencv/02_qiebian.jpg";
    Mat src = imread(path,IMREAD_COLOR);
    // 显示图片
    imshow("src",src);

    Mat rotationImg;

    verifyRotation(src,rotationImg);

    fetchROI(rotationImg);

    waitKey(0);
    return 0;
}

//裁剪图片
void fetchROI(Mat &img){
    // 将彩色图转成灰度图
    Mat gray(img.size(),CV_8UC1);
    cvtColor(img,gray,COLOR_BGR2GRAY);
    imshow("gray",gray);

    // 将图像二值化处理
    Mat binary(img.size(),CV_8UC1);
    threshold(gray,binary,0,255,THRESH_BINARY_INV|THRESH_OTSU);
    imshow("binary",binary);
    // 查找轮廓
    vector<vector<Vec2i>> contours;
    vector<Vec4i> hierarchy;
    findContours(binary,contours,hierarchy,CV_RETR_EXTERNAL,CV_CHAIN_APPROX_NONE);


    RotatedRect resultRect;

    // 遍历轮廓,求轮廓的最大外切矩形
    for(int i = 0; i<contours.size();i++){
        // 取到当前遍历轮廓
        RotatedRect rect = minAreaRect(contours[i]);
        if(rect.size.width > img.size().width*0.75){
            // 绘制轮廓
            //drawContours(img,contours,i,Scalar(0,255,255),2,LINE_AA);
            // 记录外切矩形
            resultRect = rect;
        }
    }

    Mat dst = img(resultRect.boundingRect());

    imshow("dst123",dst);
}

//旋转图片
void verifyRotation(Mat &img,Mat &outputImg){
    // 将彩色图转成灰度图
    Mat gray(img.size(),CV_8UC1);
    cvtColor(img,gray,COLOR_BGR2GRAY);
    imshow("gray1",gray);

    // 将图像二值化处理
    Mat binary(img.size(),CV_8UC1);
    threshold(gray,binary,0,255,THRESH_BINARY_INV|THRESH_OTSU);
    imshow("binary1",binary);
    // 查找轮廓
    vector<vector<Vec2i>> contours;
    vector<Vec4i> hierarchy;
    findContours(binary,contours,hierarchy,CV_RETR_EXTERNAL,CV_CHAIN_APPROX_NONE);


    RotatedRect resultRect;

    // 遍历轮廓,求轮廓的最大外切矩形
    for(int i = 0; i<contours.size();i++){
        // 取到当前遍历轮廓
        RotatedRect rect = minAreaRect(contours[i]);
        if(rect.size.width > img.size().width*0.75){
            // 绘制轮廓
            //drawContours(img,contours,i,Scalar(255,0,255),2,LINE_AA);
            // 记录外切矩形
            resultRect = rect;
        }
    }

    // 打印图片需要旋转的角度
    cout<<resultRect.angle<<endl;

    // 先对图片进行旋转
    Point2f center(img.cols/2,img.rows/2);
    Mat matrix = getRotationMatrix2D(center,(resultRect.angle)+90,1);

    warpAffine(img,outputImg,matrix,img.size());

    imshow("rotateimg",outputImg);
}
5. 直线检测,通过构造一行20列的结构元素将字母去掉,值保留横线

在这里插入图片描述

/**
 检测填空题中的直线: 采用形态学方式
*/
#include <opencv2/opencv.hpp>
#include <iostream>

using namespace std;
using namespace cv;

Mat src,result;
int threshold_value=95;
int max_count=255;

void detectline(int,void*);

int main(){
    string path = "/home/kaijun/Documents/resource/opencv/engline.jpg";

    src = imread(path,IMREAD_COLOR);
    imshow("src0",src);
    detectline();
    createTrackbar("threshold1","src",&threshold_value,max_count,detectline);

    waitKey(0);
    return 0;
}

void detectline(int ,void*){

    // 将彩色图转成灰度图
    Mat gray;
    cvtColor(src,gray,COLOR_BGR2GRAY);

    // 1. 将图像进行二值化处理
    Mat binary;
    threshold(gray,binary,0,255,THRESH_BINARY_INV|THRESH_OTSU);
    imshow("binary",binary);

    // 2. 形态学操作
    Mat morph;
    Mat kernel = getStructuringElement(MORPH_RECT, Size(20, 1));
    morphologyEx(binary,morph,MORPH_OPEN,kernel);
    imshow("morphology",morph);

    // 3. 膨胀一下
    Mat kernel2 = getStructuringElement(MORPH_RECT,Size(3,3));
    dilate(morph,morph,kernel2);
    imshow("morphology2",morph);

    // 4. 寻找霍夫直线
    vector<Vec4i> lines;
    HoughLinesP(morph,lines,1,CV_PI/180,30,20,0);

    for(int i=0; i < lines.size();i++){
        Vec4i ln = lines[i];
        line(src,Point(ln[0],ln[1]),Point(ln[2],ln[3]),Scalar(0,0,255),1);
    }

    imshow("src",src);
}
6. kmeans聚类换背景,注意,这里可以直接对三维数据进行聚类。证件照换背景示例如下:
#include <opencv2/opencv.hpp>
#include <iostream>

using namespace std;
using namespace cv;

Scalar colors[]={
        Scalar(255,0,0),
        Scalar(255,255,0),
        Scalar(0,0,255),
        Scalar(255,0,255),
        Scalar(0,255,0),

};

void useKmeans(Mat &src, Mat &distImg);
void changeBackground(Mat&src ,Mat &img,Vec3b color);
int main(int argc, char** argv){
    //string path = "/home/kaijun/Documents/resource/opencv/zhengjianzhao.png";
    //string path = "/home/kaijun/Pictures/zjz.jpeg";
    string path = "/home/kaijun/Pictures/zjz3.jpg";
    // 读取原图
    Mat src = imread(path);
    // 显示原图像
    imshow("src",src);

    // 创建一张空白的图片, 用于接收kmeans聚类结果
    Mat distImg(src.size(),src.type());
    // 使用聚类算法 对图像进行处理
    useKmeans(src, distImg);

    changeBackground(src,distImg,Vec3b(125,125,120));

    waitKey(0);

    return 0;
}

/**
 * 更换背景
 * @param img
 * @param result
 */
void changeBackground(Mat &src, Mat &img,Vec3b color){
    // 1. 提取到背景颜色值
    Vec3b backgroundColor = img.at<Vec3b>(0,0);

    // 2. 遍历背景,创建一个蒙版
    Mat mask(img.size(),CV_8UC1);
    for (int row = 0; row < img.rows; ++row) {
        for (int col = 0; col < img.cols; ++col) {
            Vec3b currentColor = img.at<Vec3b>(row,col);
            if(currentColor == backgroundColor){
                mask.at<uchar>(row,col) = 0;
            }else{
                mask.at<uchar>(row,col) = 255;
            }
        }
    }

    // 3. 形态学变换,去掉一些细小的边缘
    erode(mask,mask,Mat::ones(3,3,CV_8UC1),Point(-1,-1),3);

    // 创建一个蒙版
    imshow("mask",mask);

    // 5. 从原图中抠出人像
    Mat copyImg;
    src.copyTo(copyImg,mask);  //利用掩膜拷贝图像

    imshow("copy",copyImg);
    // 6. 遍历抠出来的人像
    for (int row = 0; row < copyImg.rows; ++row) {
        for (int col = 0; col < copyImg.cols; ++col) {
            int value = mask.at<uchar>(row,col);
            if(value == 0){
                copyImg.at<Vec3b>(row,col)=Vec3b(125,125,125);
            }
        }
    }
    imshow("result",copyImg);
}


void useKmeans(Mat &src, Mat &distImg) {// 封装数据点
    int sampleCount = src.rows*src.cols;
    // N 行 3列数据
    Mat sampleData(sampleCount,src.channels(),CV_32F);
    // 将图片的像素数据封装到样本数据中
    for(int row = 0; row< src.rows;row++){
        for(int col=0; col<src.cols;col++){
            int index = row*src.cols + col;
            // 从原图从取出颜色信息
            Vec3b bgr = src.at<Vec3b>(row,col);
            // 将数据填到数据列表中
            sampleData.at<Vec3f>(index)=bgr;
        }
    }

    // 调用kmeans函数
    int clusterNum = 4;
    Mat labels;
    TermCriteria termCriteria(TermCriteria::EPS|TermCriteria::COUNT,10,0.1);
    Mat centers;
    kmeans(sampleData,clusterNum,labels,termCriteria,3,KMEANS_PP_CENTERS,centers);

    //取出每一个样本数据,根据它的标签 填入颜色
    for (int row = 0; row < src.rows; ++row) {
        for (int col = 0; col < src.cols; ++col) {
            int index = row*src.cols + col;

            int label = labels.at<int>(index,0);

            distImg.at<Vec3b>(row,col)[0] = colors[label][0];
            distImg.at<Vec3b>(row,col)[1] = colors[label][1];
            distImg.at<Vec3b>(row,col)[2] = colors[label][2];
        }
    }

    imshow("kmeans",distImg);
}

在这里插入图片描述

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值