OpenCV数据类型
#define CV_8U //unsigned char
#define CV_8UC1 //unsigned char单通道
#define CV_8UC3 //unsigned char三通道
#define CV_32S //int
#define CV_32SC1 //int单通道
#define CV_32SC3 //int三通道
#define CV_32F //float
#define CV_32FC1 //float单通道
#define CV_32FC3 //float三通道
#define CV_64F //double
#define CV_64FC1 //double单通道
#define CV_64FC3 //double三通道
CV_8U - 8-bit unsigned integers ( 0..255 )
CV_8S - 8-bit signed integers ( -128..127 )
CV_16U - 16-bit unsigned integers ( 0..65535 )
CV_16S - 16-bit signed integers ( -32768..32767 )
CV_32S - 32-bit signed integers ( -2147483648..2147483647 )
CV_32F - 32-bit floating-point numbers ( -FLT_MAX..FLT_MAX, INF, NAN )
CV_64F - 64-bit floating-point numbers ( -DBL_MAX..DBL_MAX, INF, NAN )
OpenCV类
Mat类
初始化
Mat kern = (Mat_<char>(3, 3) <<
-1, -1, -1,
-1, 9, -1,
-1, -1, -1);
Mat img = kern.clone();
Mat img = cv::imread("image_path");
//matlab样式
Mat img = Mat::ones(Size(3, 3), CV_8UC1);
Mat img = Mat::zeros(Size(3, 3), CV_8UC1);
参数
int col = img.cols//列数
int row = img.rows//行数
int channel = img.channel()//通道数
int type = img.type()//数据类型带通道信息CV_8UC3
int depth= img.depth()//数据类型CV_8U
Size size = img.size()//图像尺寸
bool empty = img.empty()//图像是否为空
拷贝
Mat image;
img.copyTo(image); //深拷贝
image = img.clone(); //深拷贝
image = img; //浅拷贝
访问
//按图像的数据类型访问
uchar px = img.at<uchar>(i,j);
img.at<uchar>(i,j) = px;
转换数据类型与线性变换
img.convertTo(image, CV_32FC1, alpha, beta);
//image.at<float>(i,j) = float(alpha*img.at<uchar>(i,j) + beta);
切割
//切割图像的一块
Mat image = img(Rect(x,y,width,height));
Mat image(img, Rect(x,y,width,height));
运算操作等于矩阵操作
黑白转换
img = 255 - img;
OpenCV函数库
读取图片
Mat img = cv::imread(imgname);
Mat imread( const String& filename, int flags = IMREAD_COLOR )
//filename为图像路径
//flags为读取图像色域
//flags = 0 为灰度图
保存图片
if (cv::2imwrite(save_path, img))
bool imwrite( const String& filename, InputArray img,
const std::vector<int>& params = std::vector<int>())
//filename为图像路径
//img为要保存图像
//成功返回true,失败返回false
色域转换
cvtColor(img, img, cv::COLOR_RGB2GRAY);
void cvtColor(InputArray src, OutputArray dst, int code, int dstCn = 0)
InputArray src 输入图像
OutputArray dst 输出图像
int code标志位,输入的数据类型为enum ColorConversionCodes
//简单的贴几个
enum ColorConversionCodes{
COLOR_RGB2GRAY = 7, //RGB转灰度图
COLOR_GRAY2BGR = 8, //灰度图转RGB
COLOR_RGB2XYZ = 33,//RGB转CIE的XYZ
COLOR_BGR2HSV = 40,//HSV转RGB
COLOR_RGB2HSV = 41,//RGB转HSV
}
二值化
double judge_value = cv::threshold(img, img, 0, 255, cv::THRESH_OTSU);
double threshold( InputArray src, OutputArray dst,
double thresh, double maxval, int type )
/**
InputArray src 输入图像类型为CV_8U或者CV_16F
OutputArray dst 输出图像
double thresh 阈值
double maxval 最大值
**/
int type 二值化方法,类型为enum ThresholdTypes
返回值:如果使用Otsu或TRIANGLE方法将会返回计算的阈值
enum ThresholdTypes {
THRESH_BINARY = 0, //!< \f[\texttt{dst} (x,y) = \fork{\texttt{maxval}}{if \(\texttt{src}(x,y) > \texttt{thresh}\)}{0}{otherwise}\f]
THRESH_BINARY_INV = 1, //!< \f[\texttt{dst} (x,y) = \fork{0}{if \(\texttt{src}(x,y) > \texttt{thresh}\)}{\texttt{maxval}}{otherwise}\f]
THRESH_TRUNC = 2, //!< \f[\texttt{dst} (x,y) = \fork{\texttt{threshold}}{if \(\texttt{src}(x,y) > \texttt{thresh}\)}{\texttt{src}(x,y)}{otherwise}\f]
THRESH_TOZERO = 3, //!< \f[\texttt{dst} (x,y) = \fork{\texttt{src}(x,y)}{if \(\texttt{src}(x,y) > \texttt{thresh}\)}{0}{otherwise}\f]
THRESH_TOZERO_INV = 4, //!< \f[\texttt{dst} (x,y) = \fork{0}{if \(\texttt{src}(x,y) > \texttt{thresh}\)}{\texttt{src}(x,y)}{otherwise}\f]
THRESH_MASK = 7,
THRESH_OTSU = 8, //!< flag, use Otsu algorithm to choose the optimal threshold value
THRESH_TRIANGLE = 16 //!< flag, use Triangle algorithm to choose the optimal threshold value
};
合并图像
cv::merge(image_planes, 2, image_complex);
void merge(InputArrayOfArrays mv, OutputArray dst);
void merge(const Mat* mv, size_t count, OutputArray dst);
/**
mv中的所有矩阵必须具有相同的尺寸和数据类型。
dst为输出矩阵
合并方式为合并在通道上
**/
分割图像
cv::split(image_complex, image_planes);
void split(const Mat& src, Mat* mvbegin);
void split(InputArray m, OutputArrayOfArrays mv);
/**
按通道数分割输入矩阵src
并保存到数组mv中
**/
扩充边缘
cv::copyMakeBorder(img, img, 1, 1, 1, 1, cv::BORDER_CONSTANT, Scalar::all(0));
void copyMakeBorder(InputArray src, OutputArray dst,
int top, int bottom, int left, int right,
int borderType, const Scalar& value = Scalar() );
/**
InputArray src 输入图像
OutputArray dst 输出图像
int top 向上扩充几行
int bottom 向下扩充几行
int left 向左扩充几列
int right 向右扩充几列
int borderType 边界像素外推的插值方法
const Scalar& value (如果用常量插值)插值颜色
**/
int borderType 的输入类型为enum BorderTypes
enum BorderTypes {
BORDER_CONSTANT = 0, //!< `iiiiii|abcdefgh|iiiiiii` with some specified `i`
BORDER_REPLICATE = 1, //!< `aaaaaa|abcdefgh|hhhhhhh`
BORDER_REFLECT = 2, //!< `fedcba|abcdefgh|hgfedcb`
BORDER_WRAP = 3, //!< `cdefgh|abcdefgh|abcdefg`
BORDER_REFLECT_101 = 4, //!< `gfedcb|abcdefgh|gfedcba`
BORDER_TRANSPARENT = 5, //!< `uvwxyz|abcdefgh|ijklmno`
BORDER_REFLECT101 = BORDER_REFLECT_101, //!< same as BORDER_REFLECT_101
BORDER_DEFAULT = BORDER_REFLECT_101, //!< same as BORDER_REFLECT_101
BORDER_ISOLATED = 16 //!< do not look outside of ROI
}
滤波函数
高斯滤波(低通滤波)
cv::GaussianBlur(img, img, cv::Size(3, 3), 0);
void GaussianBlur( InputArray src, OutputArray dst, Size ksize,
double sigmaX, double sigmaY = 0,
int borderType = BORDER_DEFAULT );
/**
InputArray src 输入图像
OutputArray dst 输出图像
Size ksize 高斯核(窗口)大小
double sigmaX 高斯核在X方向上的标准偏差
double sigmaY 高斯核在y方向上的标准偏差
int borderType 边界像素外推的插值方法
**/
双边滤波(保留边缘)
cv::Mat BF = Mat(img.size(), img.type());
cv::bilateralFilter(img, BF ,9 , 75, 75);
//注意,如果用一个Mat同时当作输入和输出会抛异常
void bilateralFilter( InputArray src, OutputArray dst, int d,
double sigmaColor, double sigmaSpace,
int borderType = BORDER_DEFAULT );
/**
InputArray src 输入图像
OutputArray dst 输出图像
int d 在滤波期间使用的每个像素邻域直径
double sigmaColor 颜色空间中的滤波器参数
double sigmaSpace 坐标空间中的滤波器σ,较大的参数值意味着:只要它们的颜色足够接近,更远的像素也会相互影响
int borderType 边界像素外推的插值方法
**/
均值滤波(暴力滤波,容易破坏图像信息)
cv::blur(img, img, cv::Size(3, 3));
void blur( InputArray src, OutputArray dst,
Size ksize, Point anchor = Point(-1,-1),
int borderType = BORDER_DEFAULT );
/**
InputArray src 输入图像
OutputArray dst 输出图像
Size ksize 卷积核(取均值的窗口)大小
Point anchor 内核中心
int borderType 边界像素外推的插值方法
**/
中值滤波(去除椒盐噪声)
cv::medianBlur(img, img, 3);
//注意ksize必须是奇数
void medianBlur( InputArray src, OutputArray dst, int ksize );
/**
InputArray src 输入图像
OutputArray dst 输出图像
Size ksize 卷积核(取中值的窗口)大小
**/
同态滤波
将图像进行log变换
然后傅里叶变换至频域
计算功率谱并搬移功率谱变成中心低频,四周高频
自制滤波器模型
以巴特沃斯滤波器为例:
H(u,v)=(γH−γL)[1−e−c[D2(u,v)/D02]]+γLH(u,v) =( \gamma_H - \gamma_L )[1 - e^{-c[D^{2}(u,v)/D^{2}_0]}]+\gamma_L H(u,v)=(γH−γL)[1−e−c[D2(u,v)/D02]]+γL
//High-Frequency-Emphasis Filters
Mat ImageOP::Butterworth_Homomorphic_Filter(Size sz, float D, float n, float high_h_v_TB, float low_h_v_TB, Mat& realIm)
{
Mat single(sz.height, sz.width, CV_32F);
Point centre = Point(sz.height / 2, sz.width / 2);
float radius;
float upper = high_h_v_TB;
float lower = low_h_v_TB;
float dpow = D * D;
float W = (upper - lower);
for (int i = 0; i < sz.height; i++) {
for (int j = 0; j < sz.width; j++) {
radius = pow((float)(i - centre.x), 2) + pow((float)(j - centre.y), 2);
float r = exp(-n * radius / dpow);
if (radius < 0)
single.at<float>(i, j) = upper;
else
single.at<float>(i, j) = W * (1 - r) + lower;
}
}
single.copyTo(realIm);
Mat butterworth_complex;
//make two channels to match complex
Mat butterworth_channels[] = { Mat_<float>(single), Mat::zeros(sz, CV_32F) };
merge(butterworth_channels, 2, butterworth_complex);
return butterworth_complex;
}
以功率谱的中心为原点(x0, y0)
D(u,v)=((u−x0)2+(v−y0)2)D(u,v) = \sqrt((u-x_0)^{2}+(v-y_0)^{2}) D(u,v)=((u−x0)2+(v−y0)2)
对功率谱进行滤波后,用逆傅里叶变回空域
再进行exp变换回原图像
傅里叶变化
前期处理
- 扩展图像至opencv傅里叶最优化速度的尺寸
int M = getOptimalDFTSize(img.rows);
int N = getOptimalDFTSize(img.cols);
cv::copyMakeBorder(img, img, 0, M - img.rows, 0, N - img.cols, BORDER_CONSTANT, cv::Scalar::all(0));
- 准备一个数据类型为浮点数的Mat,通道数为2。
- 已保存复数的实部与虚部
Mat image_planes[] = { Mat_<float>(padded), Mat::zeros(padded.size(), CV_32F) };
/*Creates one multichannel array out of several single-channel ones.*/
cv::merge(image_planes, 2, image_complex);
傅里叶
cv::dft(image_complex, image_complex);
void dft(InputArray src, OutputArray dst, int flags = 0, int nonzeroRows = 0);
/**
InputArray src 输入图像
OutputArray dst 输出图像(通道数为2,分别是实部和虚部)
flags和nonzeroRows与优化傅里叶算法有关
**/
傅里叶逆变换
Mat result;
cv::idft(image_complex, result, DFT_SCALE);
void idft(InputArray src, OutputArray dst, int flags = 0, int nonzeroRows = 0);
/**
InputArray src 输入图像(通道数为2,分别是实部和虚部)
OutputArray dst 输出图像
flags和nonzeroRows与优化傅里叶算法有关
**/
int flags 的输入类型为enum DftFlags
这里先列出来,有兴趣的可以去找找看。
enum DftFlags {
DFT_INVERSE = 1,
DFT_SCALE = 2,
DFT_ROWS = 4,
DFT_COMPLEX_OUTPUT = 16,
DFT_REAL_OUTPUT = 32,
DFT_COMPLEX_INPUT = 64,
DCT_INVERSE = DFT_INVERSE,
DCT_ROWS = DFT_ROWS
};
注意因为前期处理的时候扩大了图像的尺寸,所以输出的图像尺寸比原来的图像尺寸要大一点
要么用ROI切割,要么用resize()修改回原来的尺寸
功率谱
- 在傅里叶变换后得到了图像在频域的复数表达
Z=u+jvZ = u + jvZ=u+jv - 如果要分析的话,可以选择分析功率谱,也就是复数的模的平方
P=∣Z∣2=u2+v2P = |Z|^{2} = u^{2} + v^{2} P=∣Z∣2=u2+v2
pow(image_planes[0], 2, image_planes[0]);
pow(image_planes[1], 2, image_planes[1]);
Mat Power = image_planes[0] + image_planes[1];
频谱搬移
因为傅里叶变换后的图像是中心是高频,四周低频
为了频率与图像相关,将图变成中心低频,四周高频
频率与图像的关系为:频率与离中心点的欧式距离成正比,图像坐标点离中心点的距离越远,坐标点处的频率越高。
因此可直接通过距离来分析频率
fImage//频谱图像
Mat tmp, q0, q1, q2, q3;
fImage = fImage(Rect(0, 0, fImage.cols & -2, fImage.rows & -2));
int cx = fImage.cols / 2;
int cy = fImage.rows / 2;
q0 = fImage(Rect(0, 0, cx, cy));
q1 = fImage(Rect(cx, 0, cx, cy));
q2 = fImage(Rect(0, cy, cx, cy));
q3 = fImage(Rect(cx, cy, cx, cy));
q0.copyTo(tmp);
q3.copyTo(q0);
tmp.copyTo(q3);
q1.copyTo(tmp);
q2.copyTo(q1);
tmp.copyTo(q2);
交换第一象限与第三象限,交换第二象限与第四象限
持续更新。。。