直接上源码,首先是头文件:
/* ******* OpencvUser.h **********
********* opencv常用操作函数声明 ********** */
/* author: autumoon */
#pragma once
#include "opencv.hpp"
class COpencvUser
{
public:
public:
COpencvUser(){};
~COpencvUser(){};
public:
//nMergeMethod 0自动 1垂直 2水平
static cv::Mat MergeMat(cv::Mat mFirst, cv::Mat mSecond, const int& nMergeMethod = 0);
//将Buffer转换为Mat(bTrans2BGR为true时,假设输入为灰度或者RGB(A))
static cv::Mat TransBufferToMat(unsigned char* pBuffer, int nWidth, int nHeight, int nBandNum, int nBPB, bool bTrans2BGR = false, bool bYReverse = true);
//取得平均值
static cv::Scalar GetAvgValue(const cv::Mat& mSrc);
//自动阈值Canny
static int AutoCanny(cv::Mat mSrcGray, cv::Mat& mEdgesGray, double& threshold1, double& threshold2, int apertureSize = 3, bool L2gradient = false);
//带遮罩的直方图均衡化
static int EqualizeHistMask(cv::Mat& mImageBGR, cv::Mat mMaskGray);
//nPos小于0腐蚀,大于0膨胀
static int ErodeDilate(cv::Mat mSrc, cv::Mat& mDst, int nPos);
//nPos小于0开运算,大于0闭运算
static int OpenClose(cv::Mat mSrc, cv::Mat& mDst, int nPos);
//将Mat转换为Buffer,注意释放*ppBuffer
static int TransMatToBuffer(cv::Mat mSrc, unsigned char** ppBuffer, int& nWidth, int& nHeight, int& nBandNum, int& nBPB, int nAlign = -1, bool bYReverse = false);
//使得roi区域合法
static int ValidRect(cv::Rect& roi, const int& nWidth, const int& nHeight);
private:
//根据直方图自动确定高低阈值
static int AdaptiveFindThreshold(cv::Mat dx, cv::Mat dy, double& low, double& high);
static int EqualizeHistGray(cv::Mat& mImgGray, cv::Mat& mMaskGray);
};
然后是cpp文件:
/* ******* OpencvUser.cpp **********
********* opencv常用操作函数实现 ********** */
/* author: autumoon */
#include "OpencvUser.h"
cv::Mat COpencvUser::MergeMat(cv::Mat mFirst, cv::Mat mSecond, const int& nMergeMethod /*= 0*/)
{
if (mFirst.type() != mSecond.type())
{
return mSecond;
}
//分界线的宽度
const int nGapPixs = 2;
//首先决定是水平方向合并,还是垂直方向合并
int nTmpWidth = mFirst.cols + mSecond.cols + nGapPixs;
int nTmpHeight = mFirst.rows + mSecond.rows + nGapPixs;
//判断是否水平方向合并
bool bHorizontal = nMergeMethod == 0 || nMergeMethod == 2;
if (nMergeMethod == 0)
{
//根据宽高来决定合并方式
bHorizontal = bHorizontal && nTmpHeight > nTmpWidth;
}
if (bHorizontal)
{
//水平方向合并
int nNewWidth = nTmpWidth;
int nNewHeight = MAX(mFirst.rows, mSecond.rows);
cv::Mat mDst = cv::Mat::zeros(cv::Size(nNewWidth, nNewHeight), mFirst.type());
//加入第一张影像
int nOffsetY = 0;
if (mFirst.rows < nNewHeight)
{
nOffsetY = (nNewHeight - mFirst.rows) / 2;
}
cv::Mat mDstFirst = mDst(cv::Rect(0, nOffsetY, mFirst.cols, mFirst.rows));
mFirst.copyTo(mDstFirst);
//加入第二张影像
nOffsetY = 0;
if (mSecond.rows < nNewHeight)
{
nOffsetY = (nNewHeight - mSecond.rows) / 2;
}
cv::Mat mDstSecond= mDst(cv::Rect(mFirst.cols + nGapPixs, nOffsetY, mSecond.cols, mSecond.rows));
mSecond.copyTo(mDstSecond);
return mDst;
}
else
{
//垂直方向合并
int nNewWidth = MAX(mFirst.cols, mSecond.cols);
int nNewHeight = nTmpHeight;
cv::Mat mDst = cv::Mat::zeros(cv::Size(nNewWidth, nNewHeight), mFirst.type());
//加入第一张影像
int nOffsetX = 0;
if (mFirst.cols < nNewWidth)
{
nOffsetX = (nNewWidth - mFirst.cols) / 2;
}
cv::Mat mDstFirst = mDst(cv::Rect(nOffsetX, 0, mFirst.cols, mFirst.rows));
mFirst.copyTo(mDstFirst);
//加入第二张影像
nOffsetX = 0;
if (mSecond.cols < nNewWidth)
{
nOffsetX = (nNewWidth - mSecond.cols) / 2;
}
cv::Mat mDstSecond= mDst(cv::Rect(nOffsetX, mFirst.rows + nGapPixs, mSecond.cols, mSecond.rows));
mSecond.copyTo(mDstSecond);
return mDst;
}
}
cv::Mat COpencvUser::TransBufferToMat(unsigned char* pBuffer, int nWidth, int nHeight, int nBandNum, int nBPB, bool bTrans2BGR /*= false*/, bool bYReverse /*= true*/)
{
cv::Mat mDst;
if (nBandNum == 4)
{
if (nBPB == 1)
{
mDst = cv::Mat::zeros(cv::Size(nWidth, nHeight), CV_8UC4);
}
else if (nBPB == 2)
{
mDst = cv::Mat::zeros(cv::Size(nWidth, nHeight), CV_16UC4);
}
}
else if (nBandNum == 3)
{
if (nBPB == 1)
{
mDst = cv::Mat::zeros(cv::Size(nWidth, nHeight), CV_8UC3);
}
else if (nBPB == 2)
{
mDst = cv::Mat::zeros(cv::Size(nWidth, nHeight), CV_16UC3);
}
}
else if (nBandNum == 1)
{
if (nBPB == 1)
{
mDst = cv::Mat::zeros(cv::Size(nWidth, nHeight), CV_8UC1);
}
else if (nBPB == 2)
{
mDst = cv::Mat::zeros(cv::Size(nWidth, nHeight), CV_16UC1);
}
}
if (bYReverse)
{
for (int j = 0; j < nHeight; ++j)
{
unsigned char* data = mDst.ptr<unsigned char>(j);
unsigned char* pSubBuffer = pBuffer + (nHeight - 1 - j) * nWidth * nBandNum * nBPB;
memcpy(data, pSubBuffer, nWidth * nBandNum * nBPB);
}
}
else
{
for (int j = 0; j < nHeight; ++j)
{
unsigned char* data = mDst.ptr<unsigned char>(j);
unsigned char* pSubBuffer = pBuffer + (j) * nWidth * nBandNum * nBPB;
memcpy(data, pSubBuffer, nWidth * nBandNum * nBPB);
}
}
if (bTrans2BGR)
{
if (nBandNum == 1)
{
cv::cvtColor(mDst, mDst, CV_GRAY2BGR);
}
else if (nBandNum == 3)
{
cv::cvtColor(mDst, mDst, CV_RGB2BGR);
}
else if (nBandNum == 4)
{
cv::cvtColor(mDst, mDst, CV_RGBA2BGR);
}
}
return mDst;
}
cv::Scalar COpencvUser::GetAvgValue(const cv::Mat& mSrc)
{
IplImage gray = mSrc;
return cvAvg(&gray);
}
int COpencvUser::AutoCanny(cv::Mat mSrcGray, cv::Mat& mEdgesGray, double& threshold1, double& threshold2, int apertureSize /*= 3*/, bool L2gradient /*= false*/)
{
if (mSrcGray.cols <= 0 || mSrcGray.rows <= 0)
{
//默认值
threshold1 = 100.0;
threshold2 = 200.0;
return -1;
}
const int cn = mSrcGray.channels();
cv::Mat dx(mSrcGray.rows, mSrcGray.cols, CV_16SC(cn));
cv::Mat dy(mSrcGray.rows, mSrcGray.cols, CV_16SC(cn));
Sobel(mSrcGray, dx, CV_16S, 1, 0, apertureSize, 1, 0, cv::BORDER_REPLICATE);
Sobel(mSrcGray, dy, CV_16S, 0, 1, apertureSize, 1, 0, cv::BORDER_REPLICATE);
if (threshold1 < 0.0 || threshold2 < 0.0)
{
AdaptiveFindThreshold(dx, dy, threshold1, threshold2);
std::cout << "auto thresh: threshold1 = " << threshold1 << " threshold2 = "<< threshold2 << std::endl;
}
cv::Canny(mSrcGray, mEdgesGray, threshold1, threshold2, apertureSize, L2gradient);
return 0;
}
int COpencvUser::EqualizeHistMask(cv::Mat& mImageBGR, cv::Mat mMaskGray)
{
if (mImageBGR.depth() != CV_8U)
{
//只处理8位
return -1;
}
size_t width = mImageBGR.cols;
size_t height = mImageBGR.rows;
std::vector<cv::Mat> vm;
cv::split(mImageBGR, vm);
size_t c = MIN(3, vm.size());
for (size_t i = 0; i < c; ++i)
{
EqualizeHistGray(vm[i], mMaskGray);
}
cv::merge(vm, mImageBGR);
return 0;
}
int COpencvUser::ErodeDilate(cv::Mat mSrc, cv::Mat& mDst, int nPos)
{
int an = abs(nPos);
cv::Mat element = cv::getStructuringElement(cv::MORPH_RECT, cv::Size(an * 2 + 1, an * 2 + 1), cv::Point(an, an));
if( nPos < 0 )
cv::erode(mSrc, mDst, element);
else
cv::dilate(mSrc, mDst, element);
return 0;
}
int COpencvUser::OpenClose(cv::Mat mSrc, cv::Mat& mDst, int nPos)
{
int an = abs(nPos);
cv::Mat element = cv::getStructuringElement(cv::MORPH_RECT, cv::Size(an * 2 + 1, an * 2 + 1), cv::Point(an, an));
if( nPos < 0 )
cv::morphologyEx(mSrc, mDst, CV_MOP_OPEN, element);
else
cv::morphologyEx(mSrc, mDst, CV_MOP_CLOSE, element);
return 0;
}
int COpencvUser::TransMatToBuffer(cv::Mat mSrc, unsigned char** ppBuffer, int& nWidth, int& nHeight, int& nBandNum, int& nBPB, int nAlign /*= -1*/, bool bYReverse /*= false*/)
{
if (*ppBuffer)
{
delete[] * ppBuffer;
*ppBuffer = nullptr;
}
nWidth = mSrc.cols;
if (nAlign > 0)
{
nWidth = ((nWidth + nAlign - 1) / nAlign) * nAlign;
}
nHeight = mSrc.rows;
nBandNum = mSrc.channels();
nBPB = (mSrc.depth() >> 1) + 1;
size_t nMemSize = nWidth * nHeight * nBandNum * nBPB;
//这样可以改变外部*pBuffer的值
*ppBuffer = new uchar[nMemSize];
memset(*ppBuffer, 0, nMemSize);
if (bYReverse)
{
for (int j = 0; j < nHeight; ++j)
{
unsigned char* data = mSrc.ptr<unsigned char>(nHeight - 1 - j);
unsigned char* pSubBuffer = *ppBuffer + (j)* nWidth * nBandNum * nBPB;
memcpy(pSubBuffer, data, nWidth * nBandNum * nBPB);
}
}
else
{
for (int j = 0; j < nHeight; ++j)
{
unsigned char* data = mSrc.ptr<unsigned char>(j);
unsigned char* pSubBuffer = *ppBuffer + (j)* nWidth * nBandNum * nBPB;
memcpy(pSubBuffer, data, nWidth * nBandNum * nBPB);
}
}
return 0;
}
int COpencvUser::ValidRect(cv::Rect& roi, const int& nWidth, const int& nHeight)
{
if (roi.x < 0 || roi.x > nWidth - 1)
{
roi.x = 0;
}
if (roi.x + roi.width > nWidth)
{
roi.width = nWidth - roi.x;
}
if (roi.y < 0 || roi.y > nHeight - 1)
{
roi.y = 0;
}
if (roi.y + roi.height > nHeight)
{
roi.height = nHeight - roi.y;
}
return 0;
}
int COpencvUser::AdaptiveFindThreshold(cv::Mat dx, cv::Mat dy, double& low, double& high)
{
CvSize size;
IplImage *imge = nullptr;
int i = 0,j = 0;
CvHistogram *hist;
int hist_size = 255;
float range_0[]={0,256};
float* ranges[] = { range_0 };
double PercentOfPixelsNotEdges = 0.7;
size = dx.size();
imge = cvCreateImage(size, IPL_DEPTH_32F, 1);
// 计算边缘的强度, 并存于图像中
float maxv = 0;
for(i = 0; i < size.height; i++ )
{
const short* _dx = dx.ptr<short>(i); //(short*)(dx->data.ptr + dx->step*i);
const short* _dy = dy.ptr<short>(i); //(short*)(dy->data.ptr + dy->step*i);
float* _image = (float *)(imge->imageData + imge->widthStep*i);
for(j = 0; j < size.width; j++)
{
_image[j] = (float)(abs(_dx[j]) + abs(_dy[j]));
maxv = MAX(maxv, _image[j]);
}
}
// 计算直方图
range_0[1] = maxv;
hist_size = (int)(hist_size > maxv ? maxv:hist_size);
hist = cvCreateHist(1, &hist_size, CV_HIST_ARRAY, ranges, 1);
cvCalcHist( &imge, hist, 0, NULL );
int total = (int)(size.height * size.width * PercentOfPixelsNotEdges);
float sum=0;
int icount = hist->mat.dim[0].size;
float *h = (float*)cvPtr1D( hist->bins, 0 );
for(i = 0; i < icount; i++)
{
sum += h[i];
if( sum > total )
break;
}
// 计算高低门限
high = (i+1) * maxv / hist_size ;
low = high * 0.4;
cvReleaseImage( &imge );
cvReleaseHist(&hist);
return 0;
}
int COpencvUser::EqualizeHistGray(cv::Mat& mImageGray, cv::Mat& mMaskGray)
{
if (mImageGray.depth() != CV_8U)
{
//只处理8位单通道
return -1;
}
if (mImageGray.size() != mMaskGray.size())
{
//此时mask无效
cv::equalizeHist(mImageGray, mImageGray);
return 0;
}
size_t width = mImageGray.cols;
size_t height = mImageGray.rows;
// 灰度映射表
uchar map[256];
long lCounts[256];
memset(lCounts, 0, sizeof(long) * 256);
size_t nPixCount = 0;
// 计算各灰度值个数
for (int j = 0; j < height; ++j)
{
uchar* data = mImageGray.ptr<uchar>(j);
uchar* mask = mMaskGray.ptr<uchar>(j);
for (size_t i = 0; i < width; ++i)
{
if (mask[i])
{
uchar x = data[i];
lCounts[x]++;
nPixCount++;
}
}
}
// 保存运算中的临时值
for (size_t i = 1; i < 256; i++)
{
lCounts[i] = lCounts[i - 1] + lCounts[i] ;
map[i] = (uchar)(lCounts[i] * 255.0f / nPixCount);
}
// 变换后的值直接在映射表中查找
for (int j = 0; j < height; ++j)
{
uchar* data = mImageGray.ptr<uchar>(j);
uchar* mask = mMaskGray.ptr<uchar>(j);
for (size_t i = 0; i < width; ++i)
{
if (mask[i])
{
data[i] = map[data[i]];
}
}
}
return 0;
}