初级代码游戏的专栏介绍与文章目录-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,没搞清怎么回事,这个代码以前是可以用的,也不知道怎么回事,不过不影响测试代码的主要目的。
(这里是文档结束)