初级代码游戏的专栏介绍与文章目录-CSDN博客
我的github:codetoys,所有代码都将会位于ctfc库中。已经放入库中我会指出在库中的位置。
这些代码大部分以Linux为目标但部分代码是纯C++的,可以在任何平台上使用。
本例程运行环境为CentOS7,64位。代码很简单,三个函数分别裁剪图片、模糊检测、黑屏检测,最后是测试代码。(这个代码应该也是可以在windows上运行的,不确定是不是要经过修改)
需要预先安装OpenCV4。
目录
一、编译参数
二、代码结构(省略的部分在后面)
三、裁剪图片
四、模糊检测
五、黑屏检测
六、测试代码
七、附加说明
一、编译参数
在OpenCV官网下载安装:

注意,本人所用版本没有这么新,不过应该不影响。
程序使用OpenCV需要头文件和库:
头文件包含目录,按照你的安装位置
-I /usr/local/include/opencv4/
链接库,这是全部,程序本身应该不需要链接这么多
 -lopencv_core -lopencv_imgcodecs -lopencv_highgui -lopencv_calib3d -lopencv_dnn -lopencv_features2d \
 -lopencv_flann -lopencv_imgproc -lopencv_ml -lopencv_objdetect -lopencv_photo -lopencv_stitching 
如果不知道安装到哪里了,用find搜索一下就可以了。
二、代码结构(省略的部分在后面)
#include "opencv2/opencv.hpp"
#include <opencv2/imgproc/types_c.h>
using namespace cv;
//图像处理类
class CMyPic
{
private:
public:
	//裁剪图片
	static bool ClipPic(char const* infile, int x, int y, int width, int heigh, char const* outfile)
	{
		。。。。。。
	}
	//模糊检测
	static double isImageBlurry(cv::Mat& img)
	{
		。。。。。
	}
	// cast 占比值,阈值为0.85,占比大于阈值认为黑屏
	static double detect_blackscreen(cv::Mat input_img)
	{
		。。。。。。
	}
	static bool CMyPic_test()
	{
		。。。。。。
	}
};
三个功能三个函数,还有一个是测试。其实四个函数都是独立的,并不需要包装起来。
三、裁剪图片
	//裁剪图片
	static bool ClipPic(char const* infile, int x, int y, int width, int heigh, char const* outfile)
	{
		Mat pic = imread(infile);
		if (NULL == pic.data)
		{
			thelog << "imread失败" << ende;
			return false;
		}
		Rect rect(x, y, width, heigh);
		Mat newpic = pic(rect);
		if (NULL == newpic.data)
		{
			thelog << "剪切失败" << ende;
			return false;
		}
		if (!imwrite(outfile, newpic))
		{
			thelog << "imwrite失败" << ende;
			return false;
		}
		return true;
	}
裁剪图片不在后面的测试代码中,因为这个代码一直在使用,所以也不用测试。
功能很简单,打开输入文件,读取RECT范围到新图片,输出到outfile,文件格式OpenCV会根据文件后缀名自动处理。
用到的几个OpenCV功能:
- imread(文件名) 读取图片文件到Mat结构,根据文件扩展名自动识别格式
- Mat::Mat(RECT) 剪切图片……这也太简单了
- imwrite(文件名,Mat对象) 写文件,根据文件扩展名自动选择输出格式
四、模糊检测
	//模糊检测
	static double isImageBlurry(cv::Mat& img)
	{
		cv::Mat matImageGray;
		// converting image's color space (RGB) to grayscale
		cv::cvtColor(img, matImageGray, CV_BGR2GRAY);
		cv::Mat dst, abs_dst;
		cv::Laplacian(matImageGray, dst, CV_16S, 3);
		cv::convertScaleAbs(dst, abs_dst);
		cv::Mat tmp_m, tmp_sd;
		double m = 0, sd = 0;
		//int threshold = 1000;
		cv::meanStdDev(dst, tmp_m, tmp_sd);
		m = tmp_m.at<double>(0, 0);
		sd = tmp_sd.at<double>(0, 0);
		return (sd * sd);
	}
这个就有点复杂了,先转换成灰度,再执行拉普拉斯算子,然后……好吧,其实我也不懂。
用到的几个OpenCV功能:
- cvtColor 转换颜色
- Laplacian 拉普拉斯算子
- convertScaleAbs 取绝对值
- meanStdDev 计算平均值和标准差
五、黑屏检测
	// cast 占比值,阈值为0.85,占比大于阈值认为黑屏
	static double detect_blackscreen(cv::Mat input_img)
	{
		cv::Mat gray_img;
		cv::cvtColor(input_img, gray_img, CV_BGR2GRAY);
		long dark_sum = 0;
		for (long i = 0; i < gray_img.rows; ++i)
		{
			for (long j = 0; j < gray_img.cols; ++j)
			{
				if (gray_img.at<uchar>(i, j) < 20)
				{
					++dark_sum;
				}
			}
		}
		return dark_sum / double(gray_img.total());
	}先转换成灰度,再计算灰度小于20的点的占比。 返回的是占比,跟阈值比较即可。
这个算法只用到了转换颜色cvtColor。
六、测试代码
	static bool CMyPic_test()
	{
		struct dirent* drip;
		DIR* dp;
		string dirname="目录";
		if ((dp = opendir(dirname.c_str())) == NULL)
		{
			thelog << "Error open dir " << dirname << " : " << strerror(errno) << ende;
			return false;
		}
		int ret = 0;
		while ((drip = readdir(dp)) != NULL)
		{
			if (strcmp(drip->d_name, ".") == 0 || strcmp(drip->d_name, "..") == 0)
			{
				continue;
			}
			string fullname;
			fullname = dirname + "/" + drip->d_name;
			//thelog<<fullname<<endi;
			bool isdir = IsDir(fullname.c_str());
			if (isdir)
			{
				thelog << fullname << "是个目录" << endi;
				continue;
			}
			string ext = ".jpg";//任务文件扩展名
			int d_name_len = strlen(drip->d_name);
			if (0 != strcmp(ext.c_str(), drip->d_name + d_name_len - ext.size()))
			{
				continue;
			}
			Mat pic = imread(fullname.c_str());
			if (NULL == pic.data)
			{
				thelog << "imread失败" << ende;
				return false;
			}
			
			//检测模糊
			double Blurry = isImageBlurry(pic);
			if (Blurry < 1000)
			{
				thelog << fullname << "模糊 " << Blurry << endi;
			}
			else
			{
				//thelog << fullname<< "不模糊" << endi;
			}
			//检测黑屏
			double Black = detect_blackscreen(pic);
			if (Black > 0.85)
			{
				thelog << fullname << "黑屏 " << Black << endi;
			}
			else
			{
				//thelog << fullname << "不黑屏" << endi;
			}
			char buf[128];
			sprintf(buf, ".%ld.%ld.jpg", (long)Blurry, (long)(Black*100));
			if (!imwrite(((string)drip->d_name + buf).c_str(), pic))
			{
				thelog << "imwrite失败" << ende;
				return false;
			}
		}
		closedir(dp);
		thelog << dirname << " 处理完毕" << endi;
		return true;
	}这个测试代码读取目录下的所有文件输出判断结果。
七、附加说明
测试代码用到了thelog,功能就是日志输出,替换为cout,ende和endi替换为endl即可。
操作目录的部分需要包含头文件dirent.h。IsDir的实现是这样的:
		bool IsDir(char const * strPathName)
		{
			struct stat statBuf;
			if (stat(strPathName, &statBuf) == -1)
			{//这里总是返回-1,不知道是什么原因
				cout << "stat error : " << strPathName << " " << strerror(errno) << endl;
				return false;
			}
			return(statBuf.st_mode & S_IFDIR);
		}
这个函数似乎有BUG,没搞清怎么回事,这个代码以前是可以用的,也不知道怎么回事,不过不影响测试代码的主要目的。
(这里是文档结束)



















