openCV3.0 C++ 学习笔记补充(自用 代码+注释)---持续更新 三(61-)

news2024/11/15 1:45:42

环境:OpenCV3.2.0 + VS2017

61、轮廓集合重排序(按轮廓面积从小到大)

//对轮廓集合面积从大到小排序
bool compareValue_bs(const std::vector<cv::Point> & c1, const std::vector<cv::Point> & c2)
{
	int area1 = cv::contourArea(c1);
	int area2 = cv::contourArea(c2);
	return area1 > area2;
}
	std::vector<std::vector<cv::Point>> ENDcontour;
	std::vector<std::vector<cv::Point>> contours;
	std::vector<cv::Vec4i> hierarchy;
	std::vector<std::vector<cv::Point>>::iterator k;    //迭代器,访问容器数据
	cv::findContours(thresholdMat, contours, hierarchy, cv::RETR_EXTERNAL, CV_RETR_LIST); //查找外轮廓,压缩存储轮廓点

	std::sort(contours.begin(), contours.end(), compareValue_bs);
排序前
排序后

62、只删除过小轮廓及挨边轮廓

	bool deleteSmallMat(cv::Mat thresholdMat, cv::Mat &resMat, int minSize = 30, bool debug = false);
bool PlaneRec::deleteSmallMat(cv::Mat thresholdMat, cv::Mat &resMat, int minSize, bool debug)
{
	thresholdMat.copyTo(resMat);

	std::vector<cv::Rect> boundRect;
	std::vector<cv::RotatedRect> minRect;
	cv::Mat visual_bR;
	if (debug) thresholdMat.copyTo(visual_bR);
	if (visual_bR.type() != CV_8UC3) cv::cvtColor(visual_bR, visual_bR, cv::COLOR_GRAY2BGR);

	std::vector<std::vector<cv::Point>> ENDcontour;
	std::vector<std::vector<cv::Point>> contours;
	std::vector<cv::Vec4i> hierarchy;
	std::vector<std::vector<cv::Point>>::iterator k;    //迭代器,访问容器数据
	cv::findContours(thresholdMat, contours, hierarchy, cv::RETR_EXTERNAL, CV_RETR_LIST); //查找外轮廓,压缩存储轮廓点
	std::sort(contours.begin(), contours.end(), compareValue_bs);

	if (debug) printf("\n\n\n\n\n");
	if (contours.size() <= 0)//图为全黑时
	{
		std::cout << __FUNCTION__ << "cons.size() <= 0";
		//return false;
	}
	if (debug) printf("图像处理后 检测到的轮廓数 cons.size() = %d  \n", contours.size());
	int remainNum = 0;//剩余的轮廓数(有重画出来的轮廓数)
	cv::Mat  tmpMat = cv::Mat(thresholdMat.rows, thresholdMat.cols, CV_8UC1, cv::Scalar(0, 0, 0));
	//画出轮廓;
	int  count = 0;
	for (k = contours.begin(); k != contours.end(); ++k, count++) //删除小连通域的
	{
		std::vector<cv::Point> curContours = *k;
		if (curContours.size() < minSize) {
			cv::drawContours(resMat, contours, count, cv::Scalar(0, 0, 0), -1, CV_AA, hierarchy);
			cv::drawContours(resMat, contours, count, cv::Scalar(0, 0, 0), 2, CV_AA, hierarchy);
			continue;
		}
		remainNum++;

		if (1) {
			int area1 = cv::contourArea(curContours);
			if (debug) cout << __FUNCTION__ << "  count: " << count << ", area1=" << area1 << endl;

		}

		minRect.push_back(cv::minAreaRect(curContours));
		boundRect.push_back(cv::boundingRect(curContours));
		if (debug) cv::rectangle(visual_bR, boundRect[boundRect.size() - 1].tl(), boundRect[boundRect.size() - 1].br(), cv::Scalar(0, 255, 0), 1);
		if (debug) cv::putText(visual_bR, std::to_string(boundRect.size() - 1), boundRect[boundRect.size() - 1].tl(), cv::FONT_HERSHEY_COMPLEX, 0.45, cv::Scalar(255, 135, 160), 1);
		if (debug) cv::putText(visual_bR, std::to_string(boundRect.size() - 1), boundRect[boundRect.size() - 1].br(), cv::FONT_HERSHEY_COMPLEX, 0.45, cv::Scalar(255, 135, 160), 1);
		if (debug) cv::drawContours(visual_bR, contours, count, cv::Scalar(255, 135, 160), -1, CV_AA, hierarchy);
		if (debug) {
			cv::circle(visual_bR, minRect.at(minRect.size() - 1).center, 3, cv::Scalar(0, 0, 255), -1, 8);  //绘制最小外接矩形的中心点
			cv::Point2f rect[4];
			minRect.at(minRect.size() - 1).points(rect);  //把最小外接矩形四个端点复制给rect数组
			for (int j = 0; j < 4; j++) {
				cv::line(visual_bR, rect[j], rect[(j + 1) % 4], cv::Scalar(0, 0, 255), 1, 8);  //绘制最小外接矩形每条边
			}
		}
		//if (boundRect.at(boundRect.size() - 1).height < src.rows / 5) continue;//轮廓是长度比图像的1/5短则直接过掉

		if (boundRect.size() > 0) {
			cv::Rect curBR = boundRect.at(boundRect.size() - 1);
			cv::RotatedRect curMR = minRect.at(minRect.size() - 1);
			double whRatio = curBR.width*1.0 / curBR.height;//宽高比
			double longMR = curMR.size.width;
			double shortMR = curMR.size.height;
			longMR = curMR.size.width > curMR.size.height ? curMR.size.width : curMR.size.height;
			shortMR = curMR.size.width < curMR.size.height ? curMR.size.width : curMR.size.height;
			double wh_MR_Ratio = longMR * 1.0 / shortMR;//非垂直的外接矩形框的长边短边比
			int area = cv::contourArea(curContours);
			double curS_tect = longMR * shortMR;//非垂直最小外接矩形面积
			double aRetio = area / curS_tect;//轮廓本身与非垂直最小外接矩形的面积比值
			if (shortMR <= 10) {
				cv::drawContours(resMat, contours, count, cv::Scalar(0, 0, 0), -1, CV_AA, hierarchy);
				cv::drawContours(resMat, contours, count, cv::Scalar(0, 0, 0), 2, CV_AA, hierarchy);
				continue;
			}
			if (curBR.x == 0 ||
				curBR.y == 0 ||
				curBR.x + curBR.width >= thresholdMat.cols ||
				curBR.y + curBR.height >= thresholdMat.rows) {
				cv::drawContours(resMat, contours, count, cv::Scalar(0, 0, 0), -1, CV_AA, hierarchy);
				cv::drawContours(resMat, contours, count, cv::Scalar(0, 0, 0), 2, CV_AA, hierarchy);
				continue;//挨边的删掉
			}
			if (debug) printf("area = %d, curS_rect=%lf, aRetio=%lf  \n", area, curS_tect, aRetio);
			if (aRetio >= 0.9) {
				//continue;
			}
			if (debug) cout << "--- curBR_" << boundRect.size() - 1 << curBR << whRatio << "\t";
			if (debug) cout << "   \tMR.angle=" << curMR.angle << "          \t, MR.center=" << curMR.center << "    \t, MR.points=" << curMR.size << ", wh_MR_Ratio" << wh_MR_Ratio << endl;
			//if (whRatio > 1) continue;//宽高比不满足要求的直接 continue
		}

		ENDcontour.push_back(curContours);
		cv::drawContours(tmpMat, contours, count, cv::Scalar(255, 255, 255), -1, CV_AA, hierarchy);
	}
	if (debug) cv::namedWindow("visual_bR222", cv::NORMCONV_FILTER);
	if (debug) cv::imshow("visual_bR222", visual_bR);
	if (debug) cv::imshow("tmpMat222", tmpMat);

	//tmpMat.copyTo(resMat);
	return true;
}
	deleteSmallMat(thresholdMat, thresholdMat, 30, debug);

63、获取轮廓点集

初始版:

		std::vector<cv::Point> curContours = *k;
		cv::Mat  curMat = cv::Mat(thresholdMat.rows, thresholdMat.cols, CV_8UC1, cv::Scalar(0, 0, 0));

		float peri = cv::arcLength(curContours, true);
		cv::approxPolyDP(curContours, conPoly[count], 0.02 * peri, true);

		cv::drawContours(curMat, contours, count, cv::Scalar(255, 255, 255), -1,cv::LINE_8, hierarchy);
		if (debug)cv::namedWindow("curMat", cv::NORMCONV_FILTER);
		if (debug) cv::imshow("curMat", curMat);

		if (1) {
			//找白的,白占比大于  才认为是小圆角
			cv::Mat imgReadOriPv;//原图上轮廓的对应位置
			imgGray.copyTo(imgReadOriPv, curMat);
			if (debug) cv::imshow("imgReadOriPv", imgReadOriPv);
			cv::threshold(imgMark, imgMark, 105, 255, cv::THRESH_BINARY);

			cv::Mat open;
			int tempk = 3;

			cv::Mat imgDil;
			cv::dilate(curMat, imgDil, cv::getStructuringElement(cv::MORPH_CROSS, cv::Size(tempk, tempk)));
			if (debug)cv::namedWindow("dilate-WriteMat2", cv::NORMCONV_FILTER);
			if (debug) cv::imshow("dilate-WriteMat2", imgDil);
			//cv::erode(dil, open, cv::getStructuringElement(cv::MORPH_CROSS, cv::Size(tempk, tempk)));
			imgDil.copyTo(open);

			open = open ^ curMat;
			//if (debug)cv::namedWindow("open-WriteMat2", cv::NORMCONV_FILTER);
			//if (debug) cv::imshow("open-WriteMat2", open);
			cv::threshold(open, open, 254, 255, cv::THRESH_BINARY);
			if (debug)cv::namedWindow("imgConEdge-WriteMat3", cv::NORMCONV_FILTER);
			if (debug) cv::imshow("imgConEdge-WriteMat3", open);//膨胀后减原轮廓只余下轮廓外圈,以获取边缘点集


			cv::Point startPnt = curContours[0];//第一个点
			for (int row = 0; row < open.rows; row++) {
				for (int col = 0; col < open.cols; col++) {
					cv::Point curp = cv::Point(col, row);
					if (open.at<uchar>(curp) >= 205) {
						startPnt = curp;
						break;
					}
				}
			}
			//if (debug) cout << "----------------- open.size()=" << open.size() << ", startPnt" << startPnt << endl;
			//if (debug) circle(open, startPnt, 2, cv::Scalar(0, 0, 0), cv::FILLED);
			if (1) {//轮廓完整点集 //为了测试轮廓形态对比 先原图像素值判定0914cxl
				vector<cv::Point> conPntLst;
				cv::Mat imgFindPnt;
				curMat.copyTo(imgFindPnt);
				cv::Mat imgFound = cv::Mat::zeros(imgFindPnt.size(), CV_8UC1);
				getPntLst_dfs_clockwise(imgFindPnt, imgFound, conPntLst, startPnt, 140);//以顺时针沿外边缘的顺序存入
				//if (debug) cout << "-----------------conPntLst.size()=" << conPntLst.size() << endl;
				if (debug) cv::imshow("imgFound", imgFound);
				if (0) {//可视化
					cv::Mat imgF111ound = cv::Mat::zeros(imgFindPnt.size(), CV_8UC1);
                    if (debug) circle(imgF111ound, startPnt, 2, cv::Scalar(255, 255, 255), cv::FILLED);
					for (cv::Point curp : conPntLst) {
						if (debug) cout << "*** startPnt=" << curp << endl;
						imgFindPnt.at<uchar>(curp) = 0;
						if (debug) cv::imshow("visual_conPntLst", imgFindPnt);
						imgF111ound.at<uchar>(curp) = 255;
						if (debug) cv::imshow("img--------------ound", imgF111ound);
						//if (debug) cv::waitKey(51);
					}
				}
				double avgPv = 0;
				for (cv::Point curp : conPntLst) {
					int pv = imgReadOriPv.at<uchar>(curp);
					avgPv += pv;
				}
				avgPv /= conPntLst.size();
				if (debug) cout << "-----------------avgPv=" << avgPv << endl;
				if (avgPv <= 200) {//轮廓对应原图上不够白的小轮廓,认为不是小圆角
					cv::drawContours(thresholdMat, contours, count, cv::Scalar(0, 0, 0), -1, CV_AA, hierarchy);
					cv::drawContours(thresholdMat, contours, count, cv::Scalar(0, 0, 0), 3, CV_AA, hierarchy);
					continue;
					//不要了
				}
			}
			if (0) {//轮廓外边缘点集
				//cv::Mat  newMat = cv::Mat(thresholdMat.rows, thresholdMat.cols, CV_8UC1, cv::Scalar(0, 0, 0));
				//drawContours(newMat, conPoly, count, cv::Scalar(255, 255, 255), 1);
				//drawContours(open, conPoly, count, cv::Scalar(255, 255, 255), 1);

				vector<cv::Point> conPntLst;
				cv::Mat imgFindPnt;
				open.copyTo(imgFindPnt);
				//newMat.copyTo(imgFindPnt);
				cv::Mat imgFound = cv::Mat::zeros(imgFindPnt.size(), CV_8UC1);
				getPntLst_dfs_clockwise(imgFindPnt, imgFound, conPntLst, startPnt, 140);//以顺时针沿外边缘的顺序存入
				if (debug) cout << "-----------------conPntAllLst.size()=" << conPntLst.size() << endl;
				if (debug) cv::imshow("imgFound", imgFound);
				if (1) {//可视化
					cv::Mat imgF111ound = cv::Mat::zeros(imgFindPnt.size(), CV_8UC1);
					if (debug) circle(imgF111ound, startPnt, 1, cv::Scalar(255, 255, 255), cv::FILLED);
					for (cv::Point curp : conPntLst) {
						if (debug) cout << "*** startPnt=" << curp << endl;
						imgFindPnt.at<uchar>(curp) = 0;
						if (debug) cv::imshow("visual_conPntLst", imgFindPnt);
						imgF111ound.at<uchar>(curp) = 255;
						if (debug) cv::imshow("img--------------ound", imgF111ound);
						if (debug) cv::waitKey(51);
					}
				}
				if (1) {
					std::vector<double> resList;
					int res = getAngleChange(conPntLst, resList);
					for (int i = 0; i < resList.size(); ++i) {
						if (debug) std::cout << resList[i] / (2.0*PI) * 360 << " -> ";
					}
					std::vector<double> trend;
					trend = get_trendList(resList, 1, debug);
					resList = trend;
					if (1) {
						int smoothCnt = 2;
						std::vector<double> newWLst = resList;
						int curCnt = smoothCnt;
						while (curCnt--) {
							linearSmooth3(newWLst, newWLst, 1);
						}
						resList = newWLst;
					}
					if (1) {
						if (debug) printf("disList.size()= %d \n", resList.size());
						float mean, variance, standard_deviation;
						get_meanCorrelationTest(resList, mean, variance, standard_deviation);
						if (debug) printf("disList 均值: %f  \n", mean); // 均值
						if (debug) printf("disList 方差: %f  \n", variance); // 方差
						if (debug) printf("disList 标准差: %f  \n\n", standard_deviation); // 标准差
					}
					if (debug) showLine(resList);
				}
			}//轮廓外边缘点集
		}

64、获取满足阈值的连通域点集(深搜,顺时针搜索存入)

/*
//以深搜的方式,顺时针方向获取轮廓外边缘点集
#include <stack> 
cv::Mat& src, cv::Mat& matDst, 搜索结果可视化标识
vector<cv::Point> &conPntLst, 获取到的轮廓边缘点集
cv::Point2i startPnt, 起始种子点
int th,像素值大于该值才被认为是轮廓部分
*/
void getPntLst_dfs_clockwise(cv::Mat& src, cv::Mat& matDst, vector<cv::Point> &conPntLst, cv::Point2i startPnt, int th)
{
	//cout << __FUNCTION__ << " conPntLst.size()=" << conPntLst.size() << ", startPnt" << startPnt << endl;
	stack<cv::Point> ptStack;//种子点队列

	//搜索方向顺序数据
	int DIR[8][2] = { { 0, -1 }, { 1, -1 }, { 1, 0 }, { 1, 1 }, { 0, 1 }, { -1, 1 }, { -1, 0 }, { -1, -1 } };//从上往右顺时针搜
	ptStack.push(startPnt);//起始种子点入栈
	conPntLst.clear();
	while (!ptStack.empty()) {
		cv::Point curp = ptStack.top();
		ptStack.pop();
		conPntLst.push_back(curp);
		//分别对八个方向上的点进行生长
		for (int i = 0; i < 8; ++i) {
			cv::Point tmpp;
			tmpp.x = curp.x + DIR[i][0];
			tmpp.y = curp.y + DIR[i][1];
			//检查是否是边缘点
			if (tmpp.x < 0 ||
				tmpp.y < 0 ||
				tmpp.x > (src.cols - 1) ||
				tmpp.y > (src.rows - 1)) {
				continue;
			}
			int nGrowLable = matDst.at<uchar>(tmpp.y, tmpp.x);	//是否已搜过
			if (nGrowLable == 0) {//未搜过
				int nCurValue = src.at<uchar>(tmpp.y, tmpp.x);//是否属于轮廓
				if (nCurValue >= th) {//属于轮廓
					matDst.at<uchar>(tmpp.y, tmpp.x) = 255;	//标记为已搜过

					ptStack.push(tmpp);
				}
			}
		}
	}
}

调用示例:

    cv::Point startPnt = curContours[0];//起始第一个点
    for (int row = 0; row < open.rows; row++) {
        for (int col = 0; col < open.cols; col++) {
            cv::Point curp = cv::Point(col, row);
            if (open.at<uchar>(curp) >= 205) {
                startPnt = curp;
                break;
            }
        }
    }
    vector<cv::Point> conPntLst;
    cv::Mat imgFindPnt;
    open.copyTo(imgFindPnt);
    //newMat.copyTo(imgFindPnt);
    cv::Mat imgFound = cv::Mat::zeros(imgFindPnt.size(), CV_8UC1);
    getPntLst_dfs_clockwise(imgFindPnt, imgFound, conPntLst, startPnt, 140);//以顺时针沿外边缘的顺序存入
    if (debug) cout << "-----------------conPntAllLst.size()=" << conPntLst.size() << endl;
    if (debug) cv::imshow("imgFound", imgFound);
    if (1) {//可视化(点集及其存入顺序)
        cv::Mat imgF111ound = cv::Mat::zeros(imgFindPnt.size(), CV_8UC1);
        if (debug) circle(imgF111ound, startPnt, 2, cv::Scalar(255, 255, 255), cv::FILLED);
        for (cv::Point curp : conPntLst) {
            if (debug) cout << "*** startPnt=" << curp << endl;
            imgFindPnt.at<uchar>(curp) = 0;
            if (debug) cv::imshow("visual_conPntLst", imgFindPnt);
            imgF111ound.at<uchar>(curp) = 255;
            if (debug) cv::imshow("img--------------ound", imgF111ound);
            if (debug) cv::waitKey(51);
        }
    }

65、从轮廓中获取所需点集(轮廓外边缘点集/轮廓完整点集)

/*
//从轮廓中获取所需点集(轮廓外边缘点集/轮廓完整点集)
cv::Mat imgOriginal, 提供图像尺寸大小
std::vector<cv::Point> curContour, 依据轮廓
cv::Mat& matDst, 搜索结果可视化标识
vector<cv::Point> &conPntLst, 获取到的轮廓边缘点集(第一个点是图像最上的轮廓白点)
int mode, 模式:0为获取轮廓外边缘点集(以顺时针沿外边缘的顺序存入),1为获取轮廓完整点集
*/
void getPntLst_fromContour(cv::Mat imgOriginal, std::vector<cv::Point> curContour, cv::Mat& imgFound, vector<cv::Point> &conPntLst, int mode = 1, bool debug = false);
/*
//从轮廓中获取所需点集(轮廓外边缘点集/轮廓完整点集)
cv::Mat imgOriginal, 提供图像尺寸大小
std::vector<cv::Point> curContour, 依据轮廓
cv::Mat& matDst, 搜索结果可视化标识
vector<cv::Point> &conPntLst, 获取到的轮廓边缘点集(第一个点是图像最上的轮廓白点)
int mode, 模式:0为获取轮廓外边缘点集(以顺时针沿外边缘的顺序存入),1为获取轮廓完整点集
*/
void getPntLst_fromContour(cv::Mat imgOriginal, std::vector<cv::Point> curContour, cv::Mat& imgFound, vector<cv::Point> &conPntLst, int mode, bool debug) 
{
	if (curContour.empty()) return;

	imgFound = cv::Mat::zeros(imgOriginal.size(), CV_8UC1);

	std::vector<cv::Point> curContours = curContour;
	cv::Mat  curMat = cv::Mat(imgOriginal.rows, imgOriginal.cols, CV_8UC1, cv::Scalar(0, 0, 0));//当前轮廓
	std::vector<std::vector<cv::Point>> contours;
	contours.push_back(curContours);
	cv::drawContours(curMat, contours, 0, cv::Scalar(255, 255, 255), -1, cv::LINE_8);
	if (debug)cv::namedWindow("curMat", cv::NORMCONV_FILTER);
	if (debug) cv::imshow("curMat", curMat);

	cv::Mat imgFindPnt;
	curMat.copyTo(imgFindPnt);
	if (mode == 0) {
		cv::Mat imgConEdge;//轮廓外边缘
		if (0) {
			//实验证明用形态学梯度回导致轮廓边缘有两层点集
			cv::morphologyEx(curMat, imgConEdge, cv::MORPH_GRADIENT, cv::getStructuringElement(cv::MORPH_CROSS, cv::Size(3, 3)));
		}
		else {
			//为了使轮廓边缘只余一层点集,选择用膨胀后与原图取异或
			cv::Mat imgDil;
			int tempk = 3;
			cv::dilate(curMat, imgDil, cv::getStructuringElement(cv::MORPH_CROSS, cv::Size(tempk, tempk)));
			if (debug)cv::namedWindow("dilate-ConEdge", cv::NORMCONV_FILTER);
			if (debug) cv::imshow("dilate-ConEdge", imgDil);
			imgDil.copyTo(imgConEdge);
			imgConEdge = imgConEdge ^ curMat;
			cv::threshold(imgConEdge, imgConEdge, 254, 255, cv::THRESH_BINARY);
		}
		if (debug)cv::namedWindow("imgConEdge", cv::NORMCONV_FILTER);
		if (debug) cv::imshow("imgConEdge", imgConEdge);//膨胀后减原轮廓只余下轮廓外圈,以获取边缘点集
		imgConEdge.copyTo(imgFindPnt);
	}
	else if (mode == 1) {
		curMat.copyTo(imgFindPnt);
	}

	cv::Point startPnt = curContours[0];//第一个点
	for (int row = 0; row < imgFindPnt.rows; row++) {
		for (int col = 0; col < imgFindPnt.cols; col++) {
			cv::Point curp = cv::Point(col, row);
			if (imgFindPnt.at<uchar>(curp) >= 205) {
				startPnt = curp;
				break;
			}
		}
	}
	//if (debug) cout << "----------------- imgFindPnt.size()=" << imgFindPnt.size() << ", startPnt" << startPnt << endl;
	//if (debug) circle(open, startPnt, 2, cv::Scalar(0, 0, 0), cv::FILLED);

	getPntLst_dfs_clockwise(imgFindPnt, imgFound, conPntLst, startPnt, 140);//以顺时针沿外边缘的顺序存入
	//if (debug) cout << "-----------------conPntLst.size()=" << conPntLst.size() << endl;
	if (debug) cv::imshow("imgFound", imgFound);
	if (0) {//可视化(点集及其存入顺序)
		cv::Mat imgF111ound = cv::Mat::zeros(imgFindPnt.size(), CV_8UC1);
		if (debug) circle(imgF111ound, startPnt, 1, cv::Scalar(255, 255, 255), cv::FILLED);
		for (cv::Point curp : conPntLst) {
			if (debug) cout << "*** startPnt=" << curp << endl;
			imgFindPnt.at<uchar>(curp) = 0;
			if (debug) cv::imshow("visual_conPntLst", imgFindPnt);
			imgF111ound.at<uchar>(curp) = 255;
			if (debug) cv::imshow("img--------------ound", imgF111ound);
			if (debug) cv::waitKey(51);
		}
	}
}

调用示例:

    //轮廓完整点集
	vector<cv::Point> conPntLst;
	cv::Mat imgFound;
	getPntLst_fromContour(imgOriginal, curContours, imgFound, conPntLst, 1, debug);

	//找白的,白占比大于  才认为是小圆角
	cv::Mat imgReadOriPv;//原图上轮廓的对应位置
	imgGray.copyTo(imgReadOriPv, curMat);
	if (debug) cv::imshow("imgReadOriPv", imgReadOriPv);
	double avgPv = 0;
	for (cv::Point curp : conPntLst) {
		int pv = imgReadOriPv.at<uchar>(curp);
		avgPv += pv;
	}
	avgPv /= conPntLst.size();
	if (debug) cout << "-----------------avgPv=" << avgPv << endl;
	if (avgPv <= 200) {//轮廓对应原图上不够白的小轮廓,认为不是小圆角
		cv::drawContours(thresholdMat, contours, count, cv::Scalar(0, 0, 0), -1, CV_AA, hierarchy);
		cv::drawContours(thresholdMat, contours, count, cv::Scalar(0, 0, 0), 3, CV_AA, hierarchy);
		continue;
		//不要了
	}
    //轮廓外边缘点集
	vector<cv::Point> conPntLst;
	cv::Mat imgFound;
	getPntLst_fromContour(imgOriginal, curContours, imgFound, conPntLst, 0, debug);
	if (1) {
		std::vector<double> resList;
		int res = getAngleChange(conPntLst, resList);
		for (int i = 0; i < resList.size(); ++i) {
			if (debug) std::cout << resList[i] / (2.0*PI) * 360 << " -> ";
		}
		std::vector<double> trend;
		trend = get_trendList(resList, 1, debug);
		resList = trend;
		if (1) {
			int smoothCnt = 2;
			std::vector<double> newWLst = resList;
			int curCnt = smoothCnt;
			while (curCnt--) {
				linearSmooth3(newWLst, newWLst, 1);
			}
			resList = newWLst;
		}
		if (1) {
			if (debug) printf("disList.size()= %d \n", resList.size());
			float mean, variance, standard_deviation;
			get_meanCorrelationTest(resList, mean, variance, standard_deviation);
			if (debug) printf("disList 均值: %f  \n", mean); // 均值
			if (debug) printf("disList 方差: %f  \n", variance); // 方差
			if (debug) printf("disList 标准差: %f  \n\n", standard_deviation); // 标准差
		}
		if (debug) showLine(resList);
	static void showLine(std::vector<double>posList, bool debug = false);

void PlaneRec::showLine(std::vector<double> posList, bool debug)
{
	if (posList.size() < 2) {
		return;
	}
	int maxVar = 360 + 1;
	int minVar = -360 - 1;
	cv::Mat canva = cv::Mat::zeros(cv::Size(posList.size()*10 + 1, maxVar - minVar), CV_8UC3);

	cv::Point startPos(0,(int)posList[0] / (2.0*PI) * 360 + maxVar);
	for (int i = 1; i < posList.size(); ++i) {
		double tmp = posList[i] / (2.0*PI) * 360 + maxVar;
		cv::Point posEnd(i*10, (int)tmp);
		//canva.at<cv::Vec3b>(posEnd) = cv::Vec3b(255, 255, 255);
		cv::line(canva, startPos, posEnd, cv::Scalar(255,255,255), 3, 8);
		startPos = posEnd;
	}
	if (debug) cv::namedWindow("showLine", cv::NORMCONV_FILTER);
	if (debug) cv::imshow("showLine", canva);
}

66、删除轮廓(不再会误删被包围在中间的内圈小轮廓)

前提:

直接轮廓查找后,利用cv::drawContours()涂黑。

	//cv::drawContours(resMat, contours, count, cv::Scalar(0, 0, 0), -1, CV_AA, hierarchy);
	//cv::drawContours(resMat, contours, count, cv::Scalar(0, 0, 0), 2, CV_AA, hierarchy);

一旦出现:需要删除的轮廓中 完整包含着 不需要删除的小轮廓 在其内圈,

则会在删除的同时将小轮廓也一起误删。

为避免这种情况,则需按连通域来进行删除。操作如下:

1)获取待删除轮廓对应的连通域,即其完整轮廓点集。

2)然后一个点一个点地去进行涂黑删除。

即可。

如此则不会误删其包含在内部的小轮廓。

	for (k = contours.begin(); k != contours.end(); ++k, count++) //删除小连通域的
	{
		std::vector<cv::Point> curContours = *k;
	    cv::Mat curMat = cv::Mat(imgOriginal.rows, imgOriginal.cols, CV_8UC1, cv::Scalar(0, 0, 0));//当前轮廓
	    cv::drawContours(curMat, contours, count, cv::Scalar(255, 255, 255), -1, cv::LINE_8);
		vector<cv::Point> conPntAllLst;//轮廓完整点集 
		if (1) {//以便将连通域位置删除而不会误删大轮廓包含在内的小轮廓
			cv::Mat imgFindPnt;
			curMat.copyTo(imgFindPnt);
			cv::Mat imgFound = cv::Mat::zeros(imgFindPnt.size(), CV_8UC1);

			cv::Point startPnt = curContours[0];//第一个点
			getPntLst_dfs_clockwise(imgFindPnt, imgFound, conPntAllLst, startPnt, 140);//以顺时针沿外边缘的顺序存入
			//if (debug) cout << "-----------------conPntLst.size()=" << conPntLst.size() << endl;
			if (debug) cv::imshow("imgFound", imgFound);
			if (0) {//可视化
				cv::Mat imgF111ound = cv::Mat::zeros(thresholdMat.size(), CV_8UC1);
				for (cv::Point curp : conPntAllLst) {
					if (debug) cout << "*** startPnt=" << curp << endl;
					thresholdMat.at<uchar>(curp) = 0;
					if (debug) cv::imshow("visual_conPntLst", thresholdMat);
					imgF111ound.at<uchar>(curp) = 255;
					if (debug) circle(imgF111ound, startPnt, 2, cv::Scalar(255, 255, 255), cv::FILLED);
					if (debug) cv::imshow("img--------------ound", imgF111ound);
					//if (debug) cv::waitKey(51);
				}
			}
		}

        //需要删除的轮廓,则轮廓对应位置涂黑
		for (cv::Point curp : conPntAllLst) {
			resMat.at<uchar>(curp) = 0;//轮廓对应位置涂黑
		}
    }

拓展:改一下画轮廓的方式

	if (0) {
		cv::drawContours(tmpMat, contours, i, cv::Scalar(255, 255, 255), -1, CV_AA, hierarchy);
	}
	else {
		//改一下画轮廓的方式
		vector<cv::Point> conPntAllLst;//轮廓完整点集 
		if (1) {//以便将连通域位置删除而不会误删大轮廓包含在内的小轮廓
			cv::Mat imgFindPnt;
			thresholdMat.copyTo(imgFindPnt);
			cv::Mat imgFound = cv::Mat::zeros(imgFindPnt.size(), CV_8UC1);
			cv::Point startPnt = curContours[0];//第一个点
			getPntLst_dfs_clockwise(imgFindPnt, imgFound, conPntAllLst, startPnt, 140);//以顺时针沿外边缘的顺序存入
			//if (debug) cout << "-----------------conPntLst.size()=" << conPntLst.size() << endl;
			if (debug) cv::imshow("imgFound", imgFound);
			if (0) {//可视化
				cv::Mat imgF111ound = cv::Mat::zeros(thresholdMat.size(), CV_8UC1);
				for (cv::Point curp : conPntAllLst) {
					if (debug) cout << "*** startPnt=" << curp << endl;
					thresholdMat.at<uchar>(curp) = 0;
					if (debug) cv::imshow("visual_conPntLst", thresholdMat);
					imgF111ound.at<uchar>(curp) = 255;
					if (debug) circle(imgF111ound, startPnt, 2, cv::Scalar(255, 255, 255), cv::FILLED);
					if (debug) cv::imshow("img--------------ound", imgF111ound);
					//if (debug) cv::waitKey(51);
				}
			}
		}
		for (cv::Point curp : conPntAllLst) tmpMat.at<uchar>(curp) = 255;//轮廓对应位置涂白
	}

 

67、

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2152414.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Vue 组件通信指南:Props 和 $emit,Vuex(状态管理),EventBus(事件总线),Provide/Inject(依赖注入)

引言 在 Vue 中&#xff0c;组件是构建应用的基本单元&#xff0c;而组件通信则是构建复杂应用的关键。组件通信是指在不同的 Vue 组件之间传递数据、交互和共享状态的过程&#xff0c;它在构建大型应用和组织代码方面起着至关重要的作用。 在开发过程中&#xff0c;我们经常…

一招教你挑代理IP的秘诀

逛乎&#xff0c;一直刷到这类问题&#xff1a; 本质上&#xff0c;都是在面对市面上那么多代理IP服务提供商&#xff0c;挑得眼花缭乱了&#xff0c;而代理IP直接影响到我们数据采集任务的效率、安全性和成功率&#xff0c;所以我们在挑选服务提供商的时候都会谨慎一些。索性我…

VScode安装和使用教程,2024最新最全,零基础入门到精通,看完这一篇就够了!

# VSCode 安装使用教程&#xff08;图文版&#xff09; 工欲善其事&#xff0c;必先利其器 对于我们每一位软件工程师来说&#xff0c;都要有自己顺手的 IDE 开发工具&#xff0c;它就是我们的武器。 一个好用的 IDE 不仅能提升我们的开发效率&#xff0c;还能让我们保持愉悦…

推送 Git Remote: 内部服务错误解决方案

Git Remote: 内部服务错误起因 拉取阿里云云效仓库代码的时候&#xff0c;之前一直拉取仓库并且推送都没有任何问题&#xff0c;但是最近在云效里面新建了一个仓库&#xff0c;也能成功拉取下来&#xff0c;但就是推送不上去&#xff0c;但是其它仓库都可以随意推送没有任何问…

IPv6(三)

文章目录 IPv6报文 IPv6报文 IPv6基本报头有8个字段&#xff0c;固定大小为40字节&#xff0c;&#xff0c;每个IPv6数据都必须包含报头&#xff0c;基本报头提供报文转发的基本信息&#xff0c;会被转发路径上面的所有路由器解析 IPv6报头长度为40字节Version&#xff1a;版本…

如何实现一个流畅的滚动列表

如何实现一个流畅的滚动列表 在网页开发中&#xff0c;滚动列表是展示大量数据时常用的交互方式。通过结合CSS动画和视觉设计&#xff0c;我们可以让列表内容自动滚动&#xff0c;为用户提供顺畅的浏览体验。今天&#xff0c;我将带你一步步实现一个流畅、富有视觉吸引力的滚动…

MySQL中的LIMIT与ORDER BY关键字详解

前言 众所周知&#xff0c;LIMIT和ORDER BY在数据库中&#xff0c;是两个非常关键并且经常一起使用的SQL语句部分&#xff0c;它们在数据处理和分页展示方面发挥着重要作用。 今天就结合工作中遇到的实际问题&#xff0c;回顾一下这块的知识点。同时希望这篇文章可以帮助到正…

[备忘]测算.net中对象所占用的内存

.net 基础库中应该是没有直接提供计算某个对象所占内存的方法。简单查了下&#xff0c;找到几种方式&#xff1a; 1、运行态用工具进行内存分析 比如&#xff0c;微软这篇教程中有介绍。《使用 .NET 对象分配工具分析内存使用情况》https://learn.microsoft.com/zh-cn/visuals…

Tomcat CVE-2017-12615漏洞复现

1.开启环境 cd /vulhub/tomcat/CVE-2017-12615 docker-compose up -d 一键启动环境 2.在首页进行抓包 修改为put方式提交 Tomcat允许适用put方法上传任意文件类型&#xff0c;但不允许jsp后缀文件上传&#xff0c;因此我们需要配合 windows的解析漏洞. 使用put /shell.jsp…

着色器ShaderMask

说明 实现一个渐变进度条&#xff0c;要求&#xff1a; 颜色渐变的过程是循序渐进的&#xff0c;而不是看起来像是将渐变条逐渐拉长了。 效果 源码 // 渐变进度条Stack(children: [// 背景色板Container(width: 300,height: 8,decoration: BoxDecoration(borderRadius: Bord…

【华为杯】2024华为杯数模研赛E题 解题思路

题目 高速公路应急车道紧急启用模型 问题背景 高速公路拥堵现象的原因众多&#xff0c;除了交通事故外&#xff0c;最典型的就是部分路段出现瓶颈现象&#xff0c;主要原因是车辆汇聚&#xff0c;而拥堵后又容易蔓延。高速公路一些特定的路段容易形成堵点&#xff0c;如匝道…

(done) 什么是 GMM? Gaussian Mixture Model,高斯混合模型

来源&#xff1a;https://www.bilibili.com/video/BV13b411w7Xj/?spm_id_from333.337.search-card.all.click&vd_source7a1a0bc74158c6993c7355c5490fc600 &#xff08;视频质量很高&#xff0c;一共四个视频&#xff0c;我只看了一个&#xff09; 直接看视频吧&#xff0…

Qt (17)【Qt 文件操作 读写保存】

阅读导航 引言一、Qt文件概述二、输入输出设备类三、文件读写类四、文件和目录信息类五、自定义“记事本” 引言 在上一篇文章中&#xff0c;我们学习了Qt的事件处理机制&#xff0c;知道了如何响应用户的操作。但应用程序常常还需要处理文件&#xff0c;比如读写数据。所以&a…

国内可以使用的ChatGPT服务【9月持续更新】

首先基础知识还是要介绍得~ 一、模型知识&#xff1a; GPT-4o&#xff1a;最新的版本模型&#xff0c;支持视觉等多模态&#xff0c;OpenAI 文档中已经更新了 GPT-4o 的介绍&#xff1a;128k 上下文&#xff0c;训练截止 2023 年 10 月&#xff08;作为对比&#xff0c;GPT-4…

整流电路的有源逆变工作状态

目录 1. 逆变的概念 2. 有源逆变的条件 3. 电流电路的概念 4. 产生逆变的条件 5. 三相桥式全控整流电路的有源逆变工作状态 6. 逆变角的概念 7. 逆变失败的原因 8. 最小逆变角的限制 整流电路的有源逆变状态是指通过控制整流器&#xff0c;使其将直流电源的能量反向送回…

yolo自动化项目实例解析(四)ui页面整理1 (1.85)

我们在上一章整理main.py 的if __name__ __main__: 内容还留下面这一段&#xff0c; from PyQt5.QtWidgets import *from lanrenauto.moni.moni import *from PyQt5.QtGui import *app QApplication(sys.argv) # 初始化Qt应用ratio screen_width / 2560 # 分辨率比例# 设…

python库tenacity最后一次重试忽略异常,并返回None

from tenacity import retry, stop_after_attemptretry(stopstop_after_attempt(3), retry_error_callbacklambda x:None) def my_function():print(retry...)print(1/0)result my_function() print(result)效果如下

【CPU】CPU的物理核、逻辑核、超线程判断及L1、L2、L3缓存、CacheLine和CPU的TBL说明

CPU物理核及L1、L2、L3及缓存 CPU缓存 CPU 缓存是一种用于存储临时数据以提高计算机程序性能的内存层次结构。它通常分为三个层次&#xff1a;L1&#xff08;一级&#xff09;、L2&#xff08;二级&#xff09;和L3&#xff08;三级&#xff09;缓存。缓存大小是CPU的重…

数据结构之算法复杂度

目录 前言 一、复杂度的概念 二、时间复杂度 三、大O的渐进表示法 四、空间复杂度 五、常见复杂度对比 总结 前言 本文主要讲述数据结构中的算法复杂度 一、复杂度的概念 算法在编写成可执行程序后&#xff0c;运行时需要耗费时间资源和空间(内存)资源。因此衡量一个算法的好坏…

【Node】如何关闭node被占用的端口

现象&#xff1a; 服务端口被占用Error: listen EADDRINUSE: address already in use :::10088 解决办法&#xff1a; 解决的思路就是把对应10088端口运行的程序给杀死。 1.去cmd输入 cd c:\Windows\System322.可以查看所有端口获取PID netstat -ano3.也可以用端口准确定位…