[opencv][cpp] 学习手册3:案例数量统计、修复旋转切边、直线提取

[opencv][cpp] 学习手册3:案例数量统计、修复旋转切边、直线提取

00_Canny_多滑动条案例.cpp
18_距离变换.cpp
19_案例_统计物品的数量.cpp
20_案例_修复图像.cpp
21_案例_图像切边.cpp
22_案例_图像旋转和切边.cpp
23_提取出直线.cpp




00_Canny_多滑动条案例.cpp

代码

//
// Created by jacob on 1/2/21.
//

#include <iostream>
#include <opencv2/opencv.hpp>
#include <opencv2/core/utils/logger.hpp>


using namespace std;
namespace cvlog = cv::utils::logging;

/***-------------------- global variable --------------------***/
cv::Mat src, gray, binary;
int threshVal_1 = 0;
int threshVal_2 = 0;
int aperture_S = 0;
int L2gradient = 0;
struct Userdata {
    int sliderId;
    std::string sliderName;
};


/***-------------------- callback function --------------------***/
void onTrackBar(int, void *userdata) {
    const auto &slider = *static_cast<Userdata *>(userdata);
    auto sliderId = slider.sliderId;
    auto sliderName = slider.sliderName;

    bool b_g = false;
    int i_aps = 3;

    CV_LOG_INFO(NULL, "-----------------------")
    CV_LOG_INFO(NULL, "v_1: " << threshVal_1)
    CV_LOG_INFO(NULL, "v_2: " << threshVal_2)
    CV_LOG_INFO(NULL, "v_A: " << aperture_S)
    CV_LOG_INFO(NULL, "v_L2: " << L2gradient)
    if (sliderId == 0) {
        CV_LOG_INFO(NULL, "ID: " << sliderId)
        cv::Canny(gray, binary, threshVal_1, threshVal_2, i_aps, b_g);
    } else if (sliderId == 1) {
        CV_LOG_INFO(NULL, "ID: " << sliderId)
        cv::Canny(gray, binary, threshVal_1, threshVal_2, i_aps, b_g);
    } else if (sliderId == 2) {
        CV_LOG_INFO(NULL, "ID: " << sliderId)
        if (aperture_S == 1) {
            i_aps = 7;
        } else {
            i_aps = 3;
        }
        cv::Canny(gray, binary, threshVal_1, threshVal_2, i_aps, b_g);
    } else if (sliderId == 3) {
        CV_LOG_INFO(NULL, "ID: " << sliderId)
        if (L2gradient == true) {
            b_g = true;
        } else {
            b_g = false;
        }
        cv::Canny(gray, binary, threshVal_1, threshVal_2, i_aps, b_g);
    }

    imshow("binary", binary);
}


/***-------------------- entry function --------------------***/
int main(int argc, char **argv) {
    cvlog::setLogLevel(cvlog::LOG_LEVEL_INFO);

    // read image
    string filename = "../img/lena.jpg";
    src = cv::imread(filename, cv::IMREAD_COLOR);
    cv::imshow("src", src);

    // 将图像转成灰图
    cvtColor(src, gray, cv::COLOR_BGR2GRAY);
    imshow("gray", gray);

    // 二值图
    binary = cv::Mat::zeros(src.size(), CV_8U);
    cv::namedWindow("binary", cv::WINDOW_AUTOSIZE);

    Userdata userdata0{0, "T_1"};
    Userdata userdata1{1, "T_2"};
    Userdata userdata2{2, "T_A"};
    Userdata userdata3{3, "T_L2"};

    cv::createTrackbar("T_1", "binary", &threshVal_1, 255, onTrackBar, &userdata0);
    cv::createTrackbar("T_2", "binary", &threshVal_2, 255, onTrackBar, &userdata1);
    cv::createTrackbar("T_A", "binary", &aperture_S, 1, onTrackBar, &userdata2);
    cv::createTrackbar("T_L2", "binary", &L2gradient, 1, onTrackBar, &userdata3);
    onTrackBar(threshVal_1, &userdata0);
//    onTrackBar(threshVal_2, &userdata1);
//    onTrackBar(aperture_S, &userdata2);
//    onTrackBar(L2gradient, &userdata3);

    // pending
    cv::waitKey(0);
    cv::destroyAllWindows();
    return 0;
}

运行结果

/home/jacob/CVWS/studyopencv002/cmake-build-debug/18_distance_transform_2
[ INFO:0] -----------------------
[ INFO:0] v_1: 0
[ INFO:0] v_2: 0
[ INFO:0] v_A: 0
[ INFO:0] v_L2: 0
[ INFO:0] ID: 0
[ INFO:0] -----------------------
[ INFO:0] v_1: 1
[ INFO:0] v_2: 0
[ INFO:0] v_A: 0
[ INFO:0] v_L2: 0
[ INFO:0] ID: 0
[ INFO:0] -----------------------

在这里插入图片描述


18_距离变换.cpp

概念
Opencv中distanceTransform方法用于计算图像中每一个非零点距离自己最近的零点的距离
函数输出的信息为距离而不是颜色值,图像上越亮的点,代表了离零点的距离越远
可以用来细化轮廓,或者寻找物体的质心!

方法参数说明:

void cv::distanceTransform(
	InputArray   src, 			//输入图像
    OutputArray     dst,   		//输出图像
    int     distanceType,   	//计算距离的方法
    int     maskSize,       	//掩膜的大小,一般为3x3 或者 5x5
    int     dstType = CV_32F 	//选用默认值即可
)   

在这里插入图片描述

有时间选一到两个距离算法自己实现。

代码

//
// Created by jacob on 1/2/21.
//

#include <iostream>
#include <opencv2/opencv.hpp>
#include <opencv2/core/utils/logger.hpp>


using namespace std;
namespace cvlog = cv::utils::logging;

/***-------------------- global variable --------------------***/
cv::Mat src, gray, binary, dst;
int threshVal = 0;


/***-------------------- callback function --------------------***/
void onTrackBar(int, void *) {
   	// 对灰度图二值化
    cv::threshold(gray, binary, threshVal, 255, cv::THRESH_BINARY_INV);
    // 对二值图进行距离计算,结果矩阵类型为8或32位浮点
    cv::distanceTransform(binary, dst, cv::DIST_L2, 5);
    CV_LOG_INFO(NULL, "dst:\n" << dst)
   	// 将距离结果矩阵进行归一化,范围区间(0-255)
    cv::normalize(dst, dst, 0, 255, cv::NORM_MINMAX);
    CV_LOG_INFO(NULL, "dst:\n" << dst)
    // 将归一化结果矩阵转化成 CV_8U 类型
    dst.convertTo(dst, CV_8U);
    CV_LOG_INFO(NULL, "dst:\n" << dst)
    
    imshow("binary", binary);
}


/***-------------------- entry function --------------------***/
int main(int argc, char **argv) {
    cvlog::setLogLevel(cvlog::LOG_LEVEL_INFO);

    // read image
    string filename = "../img/waternet7.png";
    src = cv::imread(filename, cv::IMREAD_COLOR);
    cv::imshow("src", src);

    // 将图像转成灰图
    cvtColor(src, gray, cv::COLOR_BGR2GRAY);
    imshow("gray", gray);

    // 二值图
    binary = cv::Mat::zeros(src.size(), CV_8U);
    cv::namedWindow("binary", cv::WINDOW_AUTOSIZE);

    cv::createTrackbar("T_1", "binary", &threshVal, 255, onTrackBar);
    onTrackBar(threshVal, 0);

    // pending
    cv::waitKey(0);
    cv::destroyAllWindows();
    return 0;
}

运行结果
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

在这里插入图片描述


19_案例_统计物品的数量.cpp

流程

  1. 定义矩阵 src, gray, binary, dst, dist_binary
  2. 将灰度图矩阵转化为二值图矩阵 gray —> binary,cv threshold()
  3. 通过二值图矩阵计算距离图矩阵 binary —> dst,cv distanceTransform()
  4. 归一化距离图矩阵(0-255)dst —> dst,cv normalize()
  5. 将归一化的距离图矩阵转换成 CV_8U 类型 dst —> dst,cv convertTo()
  6. 继续对 CV_8U 类型归一化距离图矩阵进行阈值筛选 dst —> dst_binary,cv threshold()
    因为此时的 dst 图像中的轮廓之间有连接,
    这不利于轮廓的查找,需要通过阈值将图像中各个元素独立开
  7. 找出上图的轮廓,cv findContours() dst — EOP

代码

//
// Created by jacob on 1/2/21.
//

#include <iostream>
#include <opencv2/opencv.hpp>
#include <opencv2/core/utils/logger.hpp>


using namespace std;
namespace cvlog = cv::utils::logging;

/***-------------------- global variable --------------------***/
cv::Mat src, gray;

struct UserData {
    cv::Mat binary;
    cv::Mat dst;
    cv::Mat dst_binary;

    int binary_tVal;
};


/***-------------------- callback function --------------------***/
void onTrackBar(int, void *userdata) {

    auto &u = *static_cast<UserData *>(userdata);
    // gray 2 binary
    cv::threshold(gray, u.binary, u.binary_tVal, 255, cv::THRESH_BINARY);
    // binary 2 distance_32F
    cv::distanceTransform(u.binary, u.dst, cv::DIST_L2, 5);
    // distance_32F to 0-255
    cv::normalize(u.dst, u.dst, 0, 255, cv::NORM_MINMAX);
    // distance_32F(0-255) to CV_8U
    u.dst.convertTo(u.dst, CV_8U);
    // dst 2 dst_binary
    threshold(u.dst, u.dst_binary, 120, 255, cv::THRESH_BINARY);
    // find contours
    vector<vector<cv::Point>> contours;
    vector<cv::Vec4i> hierarchy;
    findContours(u.dst_binary, contours, hierarchy, cv::RETR_EXTERNAL, cv::CHAIN_APPROX_SIMPLE);

    cv::imshow("binary", u.binary);
    cv::imshow("dst", u.dst);
    cv::imshow("dst_binary", u.dst_binary);
    CV_LOG_INFO(NULL, "contours.size(): " << contours.size())
}


/***-------------------- entry function --------------------***/
int main(int argc, char **argv) {
    cvlog::setLogLevel(cvlog::LOG_LEVEL_INFO);

    UserData u{
            cv::Mat::zeros(src.size(), CV_8U),
            cv::Mat::zeros(src.size(), CV_32FC1),
            cv::Mat::zeros(src.size(), CV_8U),
            0
    };

    // read image
//    string filename = "../img/shape.jpg";
//    string filename = "../img/waternet7.png";
    string filename = "../img/lingjian.png";
    src = cv::imread(filename, cv::IMREAD_COLOR);
    cv::imshow("src", src);

    // 将图像转成灰图
    cvtColor(src, gray, cv::COLOR_BGR2GRAY);
    imshow("gray", gray);

    // 二值图
    u.binary = cv::Mat::zeros(src.size(), CV_8U);
    cv::namedWindow("binary", cv::WINDOW_AUTOSIZE);

    cv::createTrackbar("T_1", "binary", &u.binary_tVal, 255, onTrackBar, &u);
    onTrackBar(u.binary_tVal, &u);

    // pending
    cv::waitKey(0);
    cv::destroyAllWindows();
    return 0;
}

运行结果

在这里插入图片描述

思考:在 onTrackBar 回调函数中调用了 threshold() 函数,这个函数中的阈值在此示例中是固定的,但是在通常情况下,这个阈值需要多次调试才能得出,如何在回调函数中再创建一个滑动条?(探讨可行性)或者从回调函数中返回特定值?


20_案例_修复图像.cpp

cv函数
在这里插入图片描述

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

需求

  1. 使用opencv 鼠标事件,鼠标左键按住滑动进行图像的修补操作

代码

//
// Created by jacob on 1/2/21.
//

#include <iostream>
#include <opencv2/opencv.hpp>
#include <opencv2/core/utils/logger.hpp>
//#include <opencv2/core/ocl.hpp>             // open computing language


using namespace std;
namespace cvlog = cv::utils::logging;

/***-------------------- global variable --------------------***/
cv::Mat src;
cv::Mat inpaintMask;
cv::Point prePoint;

/***-------------------- callback function --------------------***/
void onMouseMove(int event, int x, int y, int flags, void *userdata) {
    if (event == cv::EVENT_LBUTTONDOWN) { // 记录鼠标按下的位置
        CV_LOG_INFO(NULL, "LBD: (x, y): " << x << ", " << y)
        prePoint.x = x;
        prePoint.y = y;
    } else if (event == cv::EVENT_MOUSEMOVE && (flags & cv::EVENT_FLAG_LBUTTON)) {
//        CV_LOG_INFO(NULL, "LBD_M: (x, y): " << x << ", " << y)
        cv::line(inpaintMask, prePoint, cv::Point(x, y), cv::Scalar(255), 5, cv::LINE_AA);
        prePoint = cv::Point(x, y);

        cv::TickMeter tm;
        tm.start();
        inpaint(src, inpaintMask, src, 3, cv::INPAINT_TELEA);
        tm.stop();
        CV_LOG_INFO(NULL, "inpaint done at: " << tm.getTimeSec() << " s")
        tm.reset();

        imshow("inpaintMask", inpaintMask);
        imshow("src", src);
    }
}


/***-------------------- entry function --------------------***/
int main(int argc, char **argv) {
    cvlog::setLogLevel(cvlog::LOG_LEVEL_INFO);

    // read image
//    string filename = "../img/itheima_inpaint.jpg";
    string filename = "../img/fruits_inpaint.jpg";
    src = cv::imread(filename, cv::IMREAD_COLOR);
    cv::imshow("src", src);

    // 掩膜大小需要和原图保持一致 , 用于需要修复的路径
    inpaintMask = cv::Mat::zeros(src.size(), CV_8U);
    cv::setMouseCallback("src", onMouseMove);
    cv::imshow("inpaintMask", inpaintMask);

    // pending
    cv::waitKey(0);
    cv::destroyAllWindows();
    return 0;
}

解释说明

  1. 鼠标事件(左键按下滑动)
    void onMouseMove(int event, int x, int y, int flags, void *userdata) {
    if (event == cv::EVENT_LBUTTONDOWN) { // 记录鼠标按下的位置
        CV_LOG_INFO(NULL, "LBD: (x, y): " << x << ", " << y)
        prePoint.x = x;
        prePoint.y = y;
    } else if (event == cv::EVENT_MOUSEMOVE && (flags & cv::EVENT_FLAG_LBUTTON)) {
    //        CV_LOG_INFO(NULL, "LBD_M: (x, y): " << x << ", " << y)
    }
    
    int main(){
    	cv::setMouseCallback("src", onMouseMove);
    }
    
  2. inpaint 函数中,参数 inpaintRadius 值越大,处理的区域越多,处理时间越长。
  3. opencv 计时操作,类型 cv::TickMeter tm;,头文件 #include <opencv2/core/utility.hpp>
    在这里插入图片描述

运行结果
在这里插入图片描述


21_案例_图像切边.cpp

需求流程
将图像中包含的书本封面区域提取出来并将之填充进另一个同尺寸的窗口中

  1. 读取原始图+转换成灰度图
  2. 灰度图转化成二值图(二值反转+大津算法)
  3. 找出二值图中的轮廓(最外层轮廓)
  4. 在轮廓集中找到最大轮廓(抗干扰),获取最大轮廓的外包矩形进行包裹
    判断条件:轮廓外接矩形的宽度超过窗口宽度的四分之三
  5. 框出ROI区域
  6. 绘制ROI区域(使用图片的剪切)
  7. 显示

代码

//
// Created by jacob on 1/2/21.
//

#include <iostream>
#include <opencv2/opencv.hpp>
#include <opencv2/core/utils/logger.hpp>


using namespace std;
namespace cvlog = cv::utils::logging;

/***-------------------- global variable --------------------***/
cv::Mat src;
cv::Mat inpaintMask;
cv::Point prePoint;

/***-------------------- prototype --------------------***/

void imgCutOut(const cv::Mat &src);


/***-------------------- entry function --------------------***/
int main(int argc, char **argv) {
    cvlog::setLogLevel(cvlog::LOG_LEVEL_INFO);

    // read image
//    string filename = "../img/01_qiebian.jpg";
    string filename = "../img/id_draw.jpg";
    cv::Mat src = imread(filename, cv::IMREAD_COLOR);
    cv::imshow("src", src);

    // do image cutout
    imgCutOut(src);

    // pending
    cv::waitKey(0);
    cv::destroyAllWindows();
    return 0;
}

void imgCutOut(const cv::Mat &src) {
    // 灰度图
    cv::Mat gray;
    cvtColor(src, gray, cv::COLOR_BGR2GRAY);
    imshow("gray", gray);

    // 二值图
    cv::Mat binary;
    threshold(gray, binary, 0, 255, cv::THRESH_BINARY_INV | cv::THRESH_OTSU);
    imshow("binary", binary);

    // 轮廓 -> 矩形
    vector<vector<cv::Point>> contours;
    vector<cv::Vec4i> hierarchy;
    findContours(binary, contours, hierarchy, cv::RETR_EXTERNAL, cv::CHAIN_APPROX_SIMPLE);
    CV_LOG_INFO(NULL, "contours: " << contours.size())

    // 找到最大轮廓, 因为旁边可能会有干扰物体
    cv::RotatedRect maxRect;
    for (auto &contour : contours) {
        // 获取它的包围矩形、外切矩形
        cv::RotatedRect rect1 = minAreaRect(contour);   // angle, center, size(w, h)
        // 若外切矩形宽度超过屏幕3/4 就认为满足条件
        if (rect1.size.width > src.cols * 0.75) {
            maxRect = rect1;
        }
    }

    cv::Rect rect = maxRect.boundingRect();             // returns the minimal up-right integer rectangle containing the rotated rectangle
    cv::Mat src_rect = src.clone();
    cv::rectangle(src_rect, rect, cv::Scalar(255, 255, 0), 2);
    cv::imshow("rect", src_rect);

    // 按照矩形抠图 ,
    drawContours(src, contours, -1, cv::Scalar(0, 255, 255), 5);
    cv::Mat dst = src(rect);                            // img. cutout ---> cv::Mat src(cv::Rect)
    cv::imshow("dst", dst);
}

运行结果
在这里插入图片描述


22_案例_图像旋转和切边.cpp

需求流程

主要步骤:切边和旋转

  1. 将图像旋转正确
  2. 切边

将图像中包含的有一定旋转角度的书本封面区域提取出来并将之填充进另一个同尺寸的窗口中

  1. 读取原始图+转换成灰度图
  2. 对目标图像进行仿射变换
    1. 找到目标图像的最大轮廓
    2. 对最大轮廓区域进行仿射变换
  3. 对变换后的图像进行切边
    1. 找到变换后的目标图像的最大轮廓
    2. 对最大轮廓区域进行切边

代码

//
// Created by jacob on 1/2/21.
//

#include <iostream>
#include <opencv2/opencv.hpp>
#include <opencv2/core/utils/logger.hpp>


using namespace std;
namespace cvlog = cv::utils::logging;


/***-------------------- prototype --------------------***/

void getUpRightROI(const cv::Mat &src);

void
getMaxContour(const cv::Mat &src, vector<vector<cv::Point>> &contours, cv::RotatedRect &maxRect, std::string winName);

void getAffinedImg(const cv::Mat &src, cv::Mat &dst);


/***-------------------- entry function --------------------***/
int main(int argc, char **argv) {
    cvlog::setLogLevel(cvlog::LOG_LEVEL_INFO);

    // read image
    string filename = "../img/02_qiebian.jpg";
//    string filename = "../img/book_abs_2.png";
    cv::Mat src = imread(filename, cv::IMREAD_COLOR);
    cv::imshow("src", src);

    // do max contour
    cv::Mat src_mc;
    getAffinedImg(src, src_mc);
    cv::imshow("src_mc", src_mc);

    // do up-right roi
    getUpRightROI(src_mc);

    // pending
    cv::waitKey(0);
    cv::destroyAllWindows();
    return 0;
}

void getUpRightROI(const cv::Mat &src) {
    vector<vector<cv::Point>> contours;
    cv::RotatedRect maxRect;
    getMaxContour(src, contours, maxRect, "upRight");

    // do image cutout
    cv::Rect rect = maxRect.boundingRect();             // returns the minimal up-right integer rectangle containing the rotated rectangle
    cv::Mat src_rect = src.clone();
    cv::rectangle(src_rect, rect, cv::Scalar(255, 255, 0), 2);
    cv::imshow("rect", src_rect);

    // 按照矩形抠图 ,
    drawContours(src_rect, contours, -1, cv::Scalar(0, 255, 255), 5);
    cv::Mat dst = src(rect);                            // img. cutout ---> cv::Mat src(cv::Rect)
    cv::imshow("dst", dst);
}

void getAffinedImg(const cv::Mat &src, cv::Mat &dst) {
    vector<vector<cv::Point>> contours;
    cv::RotatedRect maxRect;
    getMaxContour(src, contours, maxRect, "affine");

    // do image affine
    cv::Mat m = getRotationMatrix2D(cv::Point2f(src.rows / 2, src.cols / 2), maxRect.angle + 90, 1);
    CV_LOG_INFO(NULL, "rMtrx: " << m)

    warpAffine(src, dst, m, src.size());
    imshow("dst", dst);
}

void
getMaxContour(const cv::Mat &src, vector<vector<cv::Point>> &contours, cv::RotatedRect &maxRect, std::string winName) {
    // 灰度图
    cv::Mat gray;
    cvtColor(src, gray, cv::COLOR_BGR2GRAY);
    imshow("gray_" + winName, gray);

    // 二值图
    cv::Mat binary;
    threshold(gray, binary, 0, 255, cv::THRESH_BINARY_INV | cv::THRESH_OTSU);
    imshow("binary_" + winName, binary);

    // 轮廓 -> 矩形
    vector<cv::Vec4i> hierarchy;
    findContours(binary, contours, hierarchy, cv::RETR_EXTERNAL, cv::CHAIN_APPROX_SIMPLE);
    CV_LOG_INFO(NULL, "contours: " << contours.size())

    // 找到最大轮廓, 因为旁边可能会有干扰物体
    for (auto &contour : contours) {
        // 获取它的包围矩形、外切矩形
        cv::RotatedRect rect1 = minAreaRect(contour);   // angle, center, size(w, h)
        // 若外切矩形宽度超过屏幕3/4 就认为满足条件
        if (rect1.size.width > src.cols * 0.75) {
            maxRect = rect1;
            CV_LOG_INFO(NULL, "maxRect: " << maxRect.angle << ", " << maxRect.center << ", " << maxRect.size)
        }
    }
}


运行结果

b/CVWS/studyopencv002/cmake-build-debug/22_img_cutout_rotate
[ INFO:0] contours: 1
[ INFO:0] maxRect: -76.6075, [226.455, 335.489], [452.864 x 342.52]
[ INFO:0] rMtrx: [0.9728062290448376, 0.2316204669962543, -43.97054408696346;
 -0.2316204669962543, 0.9728062290448376, 77.48489607071303]
[ INFO:0] contours: 5
[ INFO:0] maxRect: -90, [254, 351], [452 x 342]

Process finished with exit code 0

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

解释说明

  1. 对目标图像进行仿射变换:
    void getAffinedImg(const cv::Mat &src, cv::Mat &dst);
    1. 找到目标图像的最大轮廓
      1. 得到灰度图
      2. 得到二值图
      3. 二值图中找轮廓:
        findContours(binary, contours, hierarchy, cv::RETR_EXTERNAL, cv::CHAIN_APPROX_SIMPLE);
      4. 轮廓集中找到最大轮廓并存储
        // 找到最大轮廓, 因为旁边可能会有干扰物体
        for (auto &contour : contours) {
        	// 获取它的包围矩形、外切矩形
        	cv::RotatedRect rect1 = minAreaRect(contour);   // angle, center, size(w, h)
        	// 若外切矩形宽度超过屏幕3/4 就认为满足条件
        	if (rect1.size.width > src.cols * 0.75) {
        		maxRect = rect1;
        		CV_LOG_INFO(NULL, "maxRect: " << maxRect.angle << ", " << maxRect.center << ", " << maxRect.size)
        	}
        }
        
    2. 对最大轮廓区域进行仿射变换
      // do image affine
      // 计算仿射矩阵:m
      cv::Mat m = getRotationMatrix2D(cv::Point2f(src.rows / 2, src.cols / 2), maxRect.angle + 90, 1);
      CV_LOG_INFO(NULL, "rMtrx: " << m)
      
      // 通过仿射矩阵m,对图像进行仿射变换
      warpAffine(src, dst, m, src.size());
      imshow("dst", dst);
      
  2. 对变换后的图像进行切边: void getUpRightROI(const cv::Mat &src);
    1. 找到变换后的目标图像的最大轮廓
    2. 对最大轮廓区域进行切边

23_提取出直线.cpp

需求流程
提取出图像中填空题的下划线。
流程:

  1. 生成灰度图:gray
  2. 二值化 gray,生成二值图:binary
  3. 定义结构矩阵 kernel(卷积核)用于形态学变换。
  4. 使用 kernel 对二值图进行腐蚀操作
  5. 使用 kernel 对二值图进行膨胀操作
  6. 使用概率霍夫变换找出图像中的线段((x1, y1, x2, y2)线段的两个端点表示)
    并存入线段集中
  7. 对每一条线段绘制直线

代码

/**
Created by jacob on 1/4/21.
彩图 --
 灰度
  霍夫直线
   可能需要用到滤波
 */
#include <iostream>
#include <opencv2/opencv.hpp>

using namespace std;
using namespace cv;

int main() {
    Mat src = imread("../img/engline.jpg", IMREAD_COLOR);
    imshow("src", src);
    Mat gray;
    cvtColor(src, gray, COLOR_BGR2GRAY);
    imshow("gray", gray);

    Mat binary;
    threshold(gray, binary, 0, 255, THRESH_BINARY_INV | THRESH_TRIANGLE);
    imshow("binary", binary);


    // 定义一个结构元素 只有1 行 20列
    const Mat &kernel = getStructuringElement(MORPH_RECT, Size(20, 1));

    erode(binary, binary, kernel);
    imshow("erode", binary);

    const Mat &kernel2 = getStructuringElement(MORPH_RECT, Size(20, 1));
    dilate(binary, binary, kernel2);
    imshow("dilate", binary);

    vector<Vec4i> lines;
    HoughLinesP(binary, lines, 1, CV_PI / 180, 30, 20, 0);

    for (int i = 0; i < lines.size(); ++i) {
        int x1 = lines[i][0];
        int y1 = lines[i][1];
        int x2 = lines[i][2];
        int y2 = lines[i][3];

        line(src, Point(x1, y1), Point(x2, y2), Scalar(0, 255, 255), 2);
    }

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

运行结果
在这里插入图片描述


&&_参考

链接:opencv双滑动条
链接:Manage multiple trackbars with the same callback function opencv c++
链接:How to Add Trackbar
链接:How to create a class for trackbars in general?
链接:!fixedSize() error edit
链接:OpenCV计时方法
链接:c++从callback中获取数据在回调函数以外使用


&&_问题解决

error: (-215:Assertion failed) !fixedSize() || ((Mat*)obj)->size.operator()() == _sz in function ‘create’

对应案例:19_案例_统计物品的数量.cpp
在这里插入图片描述
参考链接:!fixedSize() error edit
解决办法:将 矩阵变量中的 const 修饰符去掉
在这里插入图片描述

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值