[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
流程
- 定义矩阵 src, gray, binary, dst, dist_binary
- 将灰度图矩阵转化为二值图矩阵 gray —> binary,cv threshold()
- 通过二值图矩阵计算距离图矩阵 binary —> dst,cv distanceTransform()
- 归一化距离图矩阵(0-255)dst —> dst,cv normalize()
- 将归一化的距离图矩阵转换成 CV_8U 类型 dst —> dst,cv convertTo()
- 继续对 CV_8U 类型归一化距离图矩阵进行阈值筛选 dst —> dst_binary,cv threshold()
因为此时的 dst 图像中的轮廓之间有连接,
这不利于轮廓的查找,需要通过阈值将图像中各个元素独立开 - 找出上图的轮廓,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可选,处理效果差不多
需求
- 使用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;
}
解释说明
- 鼠标事件(左键按下滑动)
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); }
- inpaint 函数中,参数 inpaintRadius 值越大,处理的区域越多,处理时间越长。
- opencv 计时操作,类型
cv::TickMeter tm;
,头文件#include <opencv2/core/utility.hpp>
运行结果
21_案例_图像切边.cpp
需求流程
将图像中包含的书本封面区域提取出来并将之填充进另一个同尺寸的窗口中
- 读取原始图+转换成灰度图
- 灰度图转化成二值图(二值反转+大津算法)
- 找出二值图中的轮廓(最外层轮廓)
- 在轮廓集中找到最大轮廓(抗干扰),获取最大轮廓的外包矩形进行包裹
判断条件:轮廓外接矩形的宽度超过窗口宽度的四分之三 - 框出ROI区域
- 绘制ROI区域(使用图片的剪切)
- 显示
代码
//
// 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
需求流程
主要步骤:切边和旋转
- 将图像旋转正确
- 切边
将图像中包含的有一定旋转角度的书本封面区域提取出来并将之填充进另一个同尺寸的窗口中
- 读取原始图+转换成灰度图
- 对目标图像进行仿射变换
- 找到目标图像的最大轮廓
- 对最大轮廓区域进行仿射变换
- 对变换后的图像进行切边
- 找到变换后的目标图像的最大轮廓
- 对最大轮廓区域进行切边
代码
//
// 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
解释说明
- 对目标图像进行仿射变换:
void getAffinedImg(const cv::Mat &src, cv::Mat &dst);
- 找到目标图像的最大轮廓
- 得到灰度图
- 得到二值图
- 二值图中找轮廓:
findContours(binary, contours, hierarchy, cv::RETR_EXTERNAL, cv::CHAIN_APPROX_SIMPLE);
- 轮廓集中找到最大轮廓并存储
// 找到最大轮廓, 因为旁边可能会有干扰物体 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) } }
- 对最大轮廓区域进行仿射变换
// 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);
- 找到目标图像的最大轮廓
- 对变换后的图像进行切边:
void getUpRightROI(const cv::Mat &src);
- 找到变换后的目标图像的最大轮廓
- 对最大轮廓区域进行切边
23_提取出直线.cpp
需求流程
提取出图像中填空题的下划线。
流程:
- 生成灰度图:gray
- 二值化 gray,生成二值图:binary
- 定义结构矩阵 kernel(卷积核)用于形态学变换。
- 使用 kernel 对二值图进行腐蚀操作
- 使用 kernel 对二值图进行膨胀操作
- 使用概率霍夫变换找出图像中的线段((x1, y1, x2, y2)线段的两个端点表示)
并存入线段集中 - 对每一条线段绘制直线
代码
/**
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 修饰符去掉