对图像边缘不均匀进行平滑

功能

对检测的轮廓边缘有锯齿以及凹凹凸凸的情况,最大程度的去对边缘进行平滑

流程

  1. 从已经通过深度学习模型分割后的视频以一定帧率截图出来,保存在文件夹中;
  2. 从该文件夹中读取所有图片;
  3. 先对图片 预处理 ,转为灰度图,阈值化,中值滤波,开运算;
  4. 平滑操作 检测轮廓凸包 ,对凸包进行填充,这样可以得到一个平滑的标准多边形,但是此处会丢失过多大面积区域;
  5. 对大面积区域进行筛选(大面积通过原图与凸包填充图相减),当面积大于某个限度值时,则保留这部分区域,小于则丢弃,小于这个限度值尽量保证是那些不平滑的锯齿块;
  6. 再将上一步得到的图像应该保留的区域与凸包填充图相减,这样就可以得到不丢失大部分区域的平滑图像;
  7. 对检测的图片进行保存至文件夹中。

对比图

:在这里插入图片描述

代码

部分代码被注释,计算两点距离函数,计算线段的斜率,有尝试用三次贝兹对轮廓进行圆滑操作,方法2是通过对轮廓找线,筛选线,但是线段的顺序不一定,未使用该方法

#include"opencv2/core/core.hpp"
#include"opencv2/highgui/highgui.hpp"
#include"opencv2/imgproc/imgproc.hpp"
#include<iostream>
#include <string>
#include <stdio.h>
#include <stdlib.h>
#include "opencv2/core/utility.hpp"
#include "opencv2/imgcodecs.hpp"
#include <opencv2/opencv.hpp>
#include <assert.h> 
#include <cmath>
#include <cstdint>
#include <exception>
#include <vector>
#include <math.h>
using namespace cv;
using namespace std;
#define PI acos(-1)

float QuadraticBezier(float p0, float p1, float p2, float p3, float t) {
	float u = 1.0f - t;
	float tt = t * t;
	float uuu = u * u*u;
	float p = uuu * p0 + 3.0f * u*u * t * p1 +3*u* tt * p2+tt*t*p3;
	return p;
}

vector<float> DrawQuadraticBezier(float x0, float y0, float x1, float y1, float x2, float y2, float x3, float y3, float precision) {
	for (float t = 0.0f; t <= 1.0f; t += precision) {
		float x = QuadraticBezier(x0, x1, x2,x3, t);
		float y = QuadraticBezier(y0, y1, y2,y3, t);
		// 在此处处理插值点,例如绘制像素或保存到图像中
		//std::cout << "(" << x << ", " << y << ")" << std::endl;
		return { x,y };
	}
}

double getDistance(Point2f point1, Point2f point2)
{
	double distance = sqrtf(powf((point1.x - point2.x), 2) + powf((point1.y - point2.y), 2));
	return distance;
}

double CalculateAngle(Point Mar1Point, Point Mar2Point)
{
	double k = (double)(Mar2Point.y - Mar1Point.y) / (Mar2Point.x - Mar1Point.x);  //计算斜率
	double arcLength1 = atan(k);    //弧度
	double current_angle = arcLength1 * 180 / PI;  //角度
	return current_angle;
}

int main()
{
	try
	{
		system("color 5E");  //设置控制台颜色

		//直接读取视频截成图像
		
		
		//VideoCapture::VideoCapture("C:/Users/J24050360/Desktop/test/video/model2_original_test2_out.avi");
		//VideoCapture capture("C:/Users/J24050360/Desktop/test/video/model2_original_test2_out.avi");
		//if (!capture.isOpened())
		//	cout << "fail to open!" << endl;
  //      //获取整个帧数
  //      long totalFrameNumber = capture.get(CAP_PROP_FRAME_COUNT);
  //      cout << "整个视频共" << totalFrameNumber << "帧" << endl;

  //      //设置开始帧()
  //      long frameToStart = 1000;
  //      capture.set(CAP_PROP_POS_FRAMES, frameToStart);
  //      cout << "从第" << frameToStart << "帧开始读" << endl;

  //      //设置结束帧
  //      int frameToStop = 3000;
  //      if (frameToStop < frameToStart)
  //      {
  //          cout << "结束帧小于开始帧,程序错误,即将退出!" << endl;
  //          return -1;
  //      }
  //      else
  //      {
  //          cout << "结束帧为:第" << frameToStop << "帧" << endl;
  //      }
  //      //获取帧率
  //      double rate = capture.get(CAP_PROP_FPS);
  //      cout << "帧率为:" << rate << endl;
  //      //定义一个用来控制读取视频循环结束的变量
  //      bool stop = false;
  //      //承载每一帧的图像
  //      Mat frame;
  //      //显示每一帧的窗口
  //      //namedWindow( "Extractedframe" );
  //      //两帧间的间隔时间:
  //      //int delay = 1000/rate;
  //      double delay = 1000 / rate;
  //      //利用while循环读取帧
  //      //currentFrame是在循环体中控制读取到指定的帧后循环结束的变量
  //      long currentFrame = frameToStart;

  //      while (!stop)
  //      {
  //          //读取下一帧
  //          if (!capture.read(frame))
  //          {
  //              cout << "读取视频失败" << endl;
  //              return -1;
  //          }
  //          //cout << "正在读取第" << currentFrame << "帧" << endl;
  //          //imshow( "Extractedframe", frame );

  //          //此处为跳帧操作
  //          if (currentFrame % 10 == 0) //此处为帧数间隔,修改这里就可以了
  //          {
  //              cout << "正在写第" << currentFrame << "帧" << endl;
  //              stringstream str;
  //              str << "C:/Users/J24050360/Desktop/test/video/resultimage" << currentFrame << ".png";        /*图片存储位置*/

  //              cout << str.str() << endl;
  //              imwrite(str.str(), frame);
  //          }
  //          //waitKey(intdelay=0)当delay≤ 0时会永远等待;当delay>0时会等待delay毫秒
  //          //当时间结束前没有按键按下时,返回值为-1;否则返回按键
  //          int c = waitKey(delay);
  //          //按下ESC或者到达指定的结束帧后退出读取视频
  //          if ((char)c == 27 || currentFrame > frameToStop)
  //          {
  //              stop = true;
  //          }
  //          //按下按键后会停留在当前帧,等待下一次按键
  //          if (c >= 0)
  //          {
  //              waitKey(0);
  //          }
  //          currentFrame++;

  //      }
  //      //关闭视频文件
  //      capture.release();


		//遍历文件夹下面的所有图片
        //Mat src = imread("C:/Users/J24050360/Desktop/resultimage1820.png");
		string path = "C:/Users/J24050360/Desktop/test/videoimage/*.png";
		vector<String> fn;
		glob(path, fn, false);  //false表示不遍历子文件夹
		size_t count = fn.size();
		for (int i = 0; i < count; i++)
		{
			Mat src = imread(fn[i]);
			Mat image = src.clone();

			//对视频中截取出来的图片进行预处理
			cv::cvtColor(image, image, COLOR_RGB2GRAY);
			cv::threshold(image, image, 200, 255, THRESH_BINARY);
			//二值化之后做一次中值滤波
			medianBlur(image, image, 7);
			Mat element = getStructuringElement(MORPH_RECT, Size(7, 7));
			morphologyEx(image, image, MORPH_OPEN, element); //开运算--先腐蚀后膨胀,平滑大物体边界

			vector<vector<Point>> contours;
			vector<Vec4i> hierarchy;
			findContours(image, contours, hierarchy, RETR_EXTERNAL, CHAIN_APPROX_NONE);
			sort(contours.begin(), contours.end(), [](const auto& d1, const auto& d2) {return arcLength(d1, true) > arcLength(d2, true); });
			// 绘制轮廓
			Mat result(image.size(), CV_8UC1, Scalar(0, 0, 0));
			vector<double>ContoursAreas;
			if (contours.size() < 1)
			{
				return 0;
			}
			int contoursize = contours.size();
			for (size_t i = 0; i < contoursize; i++)
			{
				Scalar color(255, 255, 255);
				drawContours(result, contours, static_cast<int>(i), color, 2, LINE_8, hierarchy, 0);
				ContoursAreas.push_back(contourArea(contours[i]));
			}

			//方法一:凸包检测,再多边形填充,填充后对损失的区域进行筛选留下细节
			cv::Mat anti_aliasing_image(image.size(), CV_8U, cv::Scalar(0));
			cv::Mat sub_image(image.size(), CV_8U, cv::Scalar(0));
			vector<vector<Point>> hull(contours.size());
			for (int i = 0; i < contours.size(); i++)
			{
				convexHull(Mat(contours[i]), hull[i], false);  //凸包检测
			}
			for (int i = 0; i < contours.size(); i++)
			{
				cv::fillConvexPoly(anti_aliasing_image, hull[i], (255, 255, 255), cv::LINE_AA);  //多边形填充
			}
			for (int i = 0; i < image.rows; i++)
			{
				for (int j = 0; j < image.cols; j++)
				{
					if (((anti_aliasing_image.at<uchar>(i, j)) != 255))
					{
						anti_aliasing_image.at<uchar>(i, j) = 0;
					}
				}
			}

			subtract(anti_aliasing_image, image, sub_image);
			vector<vector<Point>> contours_sub;
			vector<Vec4i> hierarchy_sub;
			findContours(sub_image, contours_sub, hierarchy_sub, RETR_EXTERNAL, CHAIN_APPROX_NONE);
			sort(contours_sub.begin(), contours_sub.end(), [](const auto& d1, const auto& d2) {return contourArea(d1, true) > contourArea(d2, true); });
			// 绘制轮廓
			Mat result_subTemp(image.size(), CV_8UC1, Scalar(0, 0, 0));
			Mat result_sub(image.size(), CV_8UC1, Scalar(0, 0, 0));
			vector<double>ContoursAreas_sub;
			if (contours_sub.size() < 1)
			{
				return 0;
			}
			for (size_t i = 0; i < contours_sub.size(); i++)
			{
				Scalar color(255, 255, 255);
				if (contourArea(contours_sub[i]) > 100)
				{
					drawContours(result_subTemp, contours_sub, static_cast<int>(i), color, -1, LINE_8, hierarchy, 0);
					ContoursAreas_sub.push_back(contourArea(contours_sub[i]));
				}
			}
			subtract(anti_aliasing_image, result_subTemp, result_sub);



			//图像处理结束,对这些图片进行保存处理
			String ImgName = fn[i];
			string::size_type iPos = ImgName.find_last_of('\\') + 1;
			string filename = ImgName.substr(iPos, ImgName.length() - iPos);
			string name = filename.substr(0, filename.rfind("."));
		    if (ImgName.find(".png") != String::npos)
			{
				string::size_type ipos = ImgName.length() - filename.length();
				string::size_type ipos1 = ImgName.length();
				//string path = ImgName.substr(0, ipos);
				string path = "C:/Users/J24050360/Desktop/test/videoimage_result\\";
				imwrite(path + name + ".png", result_sub);//文件夹必须提前创建,否则代码执行没有效果
				//imwrite("C:/Users/J24050360/Desktop/1/resultimage360_sub.png", result_sub);
			}
	
			
		}

		//方法二,形态学变换,开运算后找直线,通过直线来拟合,将多个小段直线作为一条直线----线段排序无规则,无法判定上条直线与下条直线是相邻的
		//Mat Hough_image(image.size(), CV_8U, cv::Scalar(0));
		//vector<Vec4i> lines_temp; // 存储检测到的直线
		//vector<Vec4i> lines1,lines2; //存储筛选后的线段
		//HoughLinesP(result, lines_temp, 1, CV_PI / 180, 10, 5, 30); // 应用霍夫直线检测算法
		//for (size_t i = 1; i < lines_temp.size(); i++)
		//{
		//	Vec4i L = lines_temp[i];
		//	Vec4i L_first = lines_temp[i-1];
		//	if (fabs(CalculateAngle(Point(L_first[2], L_first[3]), Point(L[0], L[1])) < 2))  //计算角度,上一条直线与下一条直线角度之差在范围内时则作为一条直线
		//	{
		//		line(Hough_image, Point(L_first[0], L_first[1]), Point(L[2], L[3]), Scalar(255, 255, 255), 1, LINE_AA);
		//		Vec4i L_result1 = { L_first[0], L_first[1], L[2], L[3] };
		//		lines1.push_back(L_result1);
		//	}
		//	else
		//	{
		//		line(Hough_image, Point(L_first[0], L_first[1]), Point(L_first[2], L_first[3]), Scalar(255, 255, 255), 1, LINE_AA);
		//		Vec4i L_result2 = { L_first[0], L_first[1], L_first[2], L_first[3] };
		//		lines2.push_back(L_result2);
		//	}
		//	
		//}		
		//vector<vector<Point>> contours_line;
		//vector<Vec4i> hierarchy_line;
		//findContours(Hough_image, contours_line, hierarchy_line, RETR_EXTERNAL, CHAIN_APPROX_NONE);
		//sort(contours_line.begin(), contours_line.end(), [](const auto& d1, const auto& d2) {return arcLength(d1, true) > arcLength(d2, true); });
		 绘制轮廓
		//Mat contours_lineimage(image.size(), CV_8UC1, Scalar(0, 0, 0));
		//if (contours_line.size() < 1)
		//{
		//	return 0;
		//}
		//for (size_t i = 0; i < contours_line.size(); i++)
		//{
		//	Scalar color(255, 255, 255);
		//	drawContours(contours_lineimage, contours_line, static_cast<int>(i), color, 2, LINE_8, hierarchy, 0);
		//}			
	}
	catch (cv::Exception& e)
	{

		std::cout << e.what() << std::endl;
	}
	waitKey(0);
	return 0;
}
  • 9
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值