opencv 图像处理

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 [ D 2 ( u , v ) / D 0 2 ] ] + γ L H(u,v) =( \gamma_H - \gamma_L )[1 - e^{-c[D^{2}(u,v)/D^{2}_0]}]+\gamma_L H(u,v)=(γHγL)[1ec[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 − x 0 ) 2 + ( v − y 0 ) 2 ) D(u,v) = \sqrt((u-x_0)^{2}+(v-y_0)^{2}) D(u,v)=( (ux0)2+(vy0)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 + j v Z = u + jv Z=u+jv
  • 如果要分析的话,可以选择分析功率谱,也就是复数的模的平方
    P = ∣ Z ∣ 2 = u 2 + v 2 P = |Z|^{2} = u^{2} + v^{2} P=Z2=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);

交换第一象限与第三象限,交换第二象限与第四象限

持续更新。。。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值