赞
踩
CV_8U:占8位的unsigned
CV_8UC(n):占8位的unsigned char
CV_8UC1:占8位的unsigned char 一通道
CV_8UC2:占8位的unsigned char 二通道
CV_8UC3:占8位的unsigned char 三通道
CV_8UC4:占8位的unsigned char 四通道
CV_8S:占8位的signed
CV_8SC(n):占8位的signed char
CV_8SC1:占8位的signed char 一通道
CV_8SC2:占8位的signed char 二通道
CV_8SC3:占8位的signed char 三通道
CV_8SC4:占8位的signed char 四通道
CV_16U:占16位的unsigned
CV_16UC(n):占16位的unsigned char
CV_16UC1:占16位的unsigned char 一通道
CV_16U2:占16位的unsigned char 二通道
CV_16U3:占16位的unsigned char 三通道
CV_16U4:占16位的unsigned char 四通道
CV_16S:占16位的signed
CV_16SC(n):占16位的signed char
CV_16SC1:占16位的signed char 一通道
CV_16SC2:占16位的signed char 二通道
CV_16SC3:占16位的signed char 三通道
CV_16SC4:占16位的signed char 四通道
CV_16F:占16位的float
CV_16FC(n):占16位的float char
CV_16FC1:占16位的float char 一通道
CV_16FC2:占16位的float char 二通道
CV_16FC3:占16位的float char 三通道
CV_16FC4:占16位的float char 四通道
CV_32S:占32位的signed
CV_32SC(n):占32位的signed char
CV_32SC1:占32位的signed char 一通道
CV_32SC2:占32位的signed char 二通道
CV_32SC3:占32位的signed char 三通道
CV_32SC4:占32位的signed char 四通道
CV_32F:占32位的float
CV_32FC(n):占32位的float char
CV_32FC1:占32位的float char 一通道
CV_32FC2:占32位的float char 二通道
CV_32FC3:占32位的float char 三通道
CV_32FC4:占23位的float char 四通道
CV_64F:占64位的float
CV_64FC(n):占64位的float char
CV_64FC1:占64位的float char 一通道
CV_64FC2:占64位的float char 二通道
CV_64FC3:占64位的float char 三通道
CV_64FC4:占64位的float char 四通道
CV_NODISCARD_STD static MatExpr Mat::zeros(int rows, int cols, int type);
CV_NODISCARD_STD static MatExpr Mat::zeros(Size size, int type);
CV_NODISCARD_STD static MatExpr Mat::zeros(int ndims, const int* sz, int type);
//not recommended
参数 | 含义 |
---|---|
rows | 行数 |
cols | 列数 |
type | 数据类型(CV_16F) |
size | Size(宽(列数),高(行数)) |
CV_NODISCARD_STD static MatExpr Mat::ones(int rows, int cols, int type);
CV_NODISCARD_STD static MatExpr Mat::ones(Size size, int type);
CV_NODISCARD_STD static MatExpr Mat::ones(int ndims, const int* sz, int type);
//not recommended
参数 | 含义 |
---|---|
rows | 行数 |
cols | 列数 |
type | 数据类型(CV_16F) |
size | Size(宽(列数),高(行数)) |
CV_NODISCARD_STD static MatExpr Mat::eye(int rows, int cols, int type);
CV_NODISCARD_STD static MatExpr Mat::eye(Size size, int type);
参数 | 含义 |
---|---|
rows | 行数 |
cols | 列数 |
type | 数据类型(CV_16F) |
size | Size(宽(列数),高(行数)) |
MatExpr Mat::t() const;
MatExpr Mat::inv(int method=DECOMP_LU) const;
template<typename _Tp> inline Mat_<_Tp>::Mat_(int _rows, int _cols) : Mat(_rows, _cols, traits::Type<_Tp>::value) { } template<typename _Tp> inline Mat_<_Tp>::Mat_(int _rows, int _cols, const _Tp& value) : Mat(_rows, _cols, traits::Type<_Tp>::value) { *this = value; } template<typename _Tp> inline Mat_<_Tp>::Mat_(Size _sz) : Mat(_sz.height, _sz.width, traits::Type<_Tp>::value) {} template<typename _Tp> inline Mat_<_Tp>::Mat_(Size _sz, const _Tp& value) : Mat(_sz.height, _sz.width, traits::Type<_Tp>::value) { *this = value; }
Mat a=(Mat_<int>(2,2)<<1,2,3,4);
Mat b=(Mat_<double>(Size(2,2))<<1,2,3,4);
注意 :给出的数据类型必须是基本数据类型,如int,double。不能是CV_16F等。
Mat::Mat() CV_NOEXCEPT;
Mat::Mat(int rows, int cols, int type);
Mat::Mat(Size size, int type);
Mat::Mat(int rows, int cols, int type, const Scalar& s);
Mat::Mat(Size size, int type, const Scalar& s);
Mat::Mat(const std::vector<int>& sizes, int type);
Mat::Mat(const std::vector<int>& sizes, int type, const Scalar& s);
Mat::Mat(const Mat& m);
void Mat::create(int rows, int cols, int type);
void Mat::create(Size size, int type);
void Mat::create(const std::vector<int>& sizes, int type);
参数 | 含义 |
---|---|
rows | 行数 |
cols | 列数 |
type | 数据类型(CV_16F) |
size | Size(宽(列数),高(行数)) |
typedef Vec<uchar, 2> Vec2b; typedef Vec<uchar, 3> Vec3b; typedef Vec<uchar, 4> Vec4b; typedef Vec<short, 2> Vec2s; typedef Vec<short, 3> Vec3s; typedef Vec<short, 4> Vec4s; typedef Vec<ushort, 2> Vec2w; typedef Vec<ushort, 3> Vec3w; typedef Vec<ushort, 4> Vec4w; typedef Vec<int, 2> Vec2i; typedef Vec<int, 3> Vec3i; typedef Vec<int, 4> Vec4i; typedef Vec<int, 6> Vec6i; typedef Vec<int, 8> Vec8i; typedef Vec<float, 2> Vec2f; typedef Vec<float, 3> Vec3f; typedef Vec<float, 4> Vec4f; typedef Vec<float, 6> Vec6f; typedef Vec<double, 2> Vec2d; typedef Vec<double, 3> Vec3d; typedef Vec<double, 4> Vec4d; typedef Vec<double, 6> Vec6d;
Mat a(Size(2560,1440),CV_8UC3); for(int i=0;i<a.rows;i++){ for(int j=0;j<a.cols;j++){ a.ptr(i,j)[0]=0; a.ptr(i,j)[1]=0; a.ptr(i,j)[2]=255; } } for(int i=0;i<a.rows;i++){ for(int j=0;j<a.cols;j++){ a.ptr<Vec3b>(i,j)[0]=0; a.ptr<Vec3b>(i,j)[1]=0; a.ptr<Vec3b>(i,j)[2]=255; } } for(int i=0;i<a.rows;i++){ for(int j=0;j<a.cols;j++){ a.at<Vec3b>(i,j)[0]=0; a.at<Vec3b>(i,j)[1]=0; a.at<Vec3b>(i,j)[2]=255; } }
Mat a(Size(2560,1440),CV_8UC3);
for(auto iter=a.begin<Vec3b>();iter!=a.end<Vec3b>();iter++){
iter[0]=255;
iter[1]=0;
iter[2]=0;
}
CV_EXPORTS_W Mat imread( const String& filename, int flags = IMREAD_COLOR ); enum ImreadModes { IMREAD_UNCHANGED = -1, //!< If set, return the loaded image as is (with alpha channel, otherwise it gets cropped). Ignore EXIF orientation. IMREAD_GRAYSCALE = 0, //!< If set, always convert image to the single channel grayscale image (codec internal conversion). IMREAD_COLOR = 1, //!< If set, always convert image to the 3 channel BGR color image. IMREAD_ANYDEPTH = 2, //!< If set, return 16-bit/32-bit image when the input has the corresponding depth, otherwise convert it to 8-bit. IMREAD_ANYCOLOR = 4, //!< If set, the image is read in any possible color format. IMREAD_LOAD_GDAL = 8, //!< If set, use the gdal driver for loading the image. IMREAD_REDUCED_GRAYSCALE_2 = 16, //!< If set, always convert image to the single channel grayscale image and the image size reduced 1/2. IMREAD_REDUCED_COLOR_2 = 17, //!< If set, always convert image to the 3 channel BGR color image and the image size reduced 1/2. IMREAD_REDUCED_GRAYSCALE_4 = 32, //!< If set, always convert image to the single channel grayscale image and the image size reduced 1/4. IMREAD_REDUCED_COLOR_4 = 33, //!< If set, always convert image to the 3 channel BGR color image and the image size reduced 1/4. IMREAD_REDUCED_GRAYSCALE_8 = 64, //!< If set, always convert image to the single channel grayscale image and the image size reduced 1/8. IMREAD_REDUCED_COLOR_8 = 65, //!< If set, always convert image to the 3 channel BGR color image and the image size reduced 1/8. IMREAD_IGNORE_ORIENTATION = 128 //!< If set, do not rotate the image according to EXIF's orientation flag. };
CV_EXPORTS_W void namedWindow(const String& winname, int flags = WINDOW_AUTOSIZE);
参数 | 含义 |
---|---|
winname(window name) | 窗体名 |
CV_EXPORTS_W void imshow(const String& winname, InputArray mat);
参数 | 含义 |
---|---|
winname(window name) | 窗体名 |
mat | 输入的欲显示的图片 |
CV_EXPORTS_W int waitKey(int delay = 0);
控制图片的展示时间,如设置delay=0,则表示一直展示,按SPACE停止展示
如设置delay不为0,则表示停留delay毫秒
CV_EXPORTS_W bool imwrite( const String& filename, InputArray img,
const std::vector<int>& params = std::vector<int>());
参数 | 含义 |
---|---|
filename | 保存的文件名 |
img(image) | 要保存的图片 |
CV_WRAP explicit VideoCapture::VideoCapture(const String& filename, int apiPreference = CAP_ANY); CV_WRAP explicit VideoCapture::VideoCapture(const String& filename, int apiPreference, const std::vector<int>& params); CV_WRAP explicit VideoCapture::VideoCapture(int index, int apiPreference = CAP_ANY); CV_WRAP explicit VideoCapture::VideoCapture(int index, int apiPreference, const std::vector<int>& params); CV_WRAP VideoWriter::VideoWriter(const String& filename, int fourcc, double fps,Size frameSize, bool isColor = true); CV_WRAP VideoWriter::VideoWriter(const String& filename, int fourcc, double fps, const Size& frameSize,const std::vector<int>& params); CV_WRAP VideoWriter::VideoWriter(const String& filename, int apiPreference, int fourcc, double fps,const Size& frameSize, const std::vector<int>& params); //fps:帧率 //frameSize:输出视频中每一帧的尺寸
CV_WRAP static int fourcc(char c1, char c2, char c3, char c4);
代码 | 含义 |
---|---|
VideoWriter::fourcc(‘M’, ‘P’, ‘4’, ‘V’) | MPEG-4编码,输出文件拓展名mp4 |
VideoWriter::fourcc( ‘X’ , ‘2’ , ‘6’ , ‘4’ ) | MPEG-4编码,输出文件拓展名mp4 |
VideoWriter::fourcc(‘P’,‘I’,‘M’,‘1’) | MPEG-1编码,输出文件拓展名avi |
VideoWriter::fourcc(‘X’,‘V’,‘I’,‘D’) | MPEG-4编码,输出文件拓展名avi |
VideoWriter::fourcc(‘I’,‘4’,‘2’,‘0’) | YUV编码,输出文件拓展名avi |
VideoWriter::fourcc(‘T’,‘H’,‘E’,‘O’) | ogg vorbis编码,输出文件拓展名ogv |
VideoWriter::fourcc(‘F’,L’,‘V’,‘1’) | flash video编码,输出文件拓展名flv |
首选使用的Capture API后端。如果有多个可用的读取器实现,则可以用于实施特定的读取器实现。
设置读取的摄像头编号,默认CAP_ANY=0,自动检测摄像头。多个摄像头时,使用索引0,1,2,…进行编号调用摄像头。 apiPreference = -1时单独出现窗口,选取相应编号摄像头。
VideoCapture video("demo.mp4");
Mat fps;
video.read(fps);
VideoWriter video_out("demo_out.avi",VideoWriter::fourcc('P','I','M','1'),30,fps.size());
while (1){
Mat fps;
video>>fps;
//video.read(fps);
fps>>video_out;
//video_out.write(fps);
imshow("video",fps);
waitKey(10);//控制帧率
}
CV_EXPORTS void split(const Mat& src, Mat* mvbegin);
参数 | 含义 |
---|---|
src(source) | 输入图像 |
mvbegin(mat vector begin) | 分离后的Mat数组的地址 |
CV_EXPORTS_W void split(InputArray m, OutputArrayOfArrays mv);
参数 | 含义 |
---|---|
m(mat) | 输入图像 |
mv(mat vector) | 分离后的的Mat数组,可以使用STL容器vector。 |
CV_EXPORTS void merge(const Mat* mv, size_t count, OutputArray dst);
参数 | 含义 |
---|---|
mv(mat vector) | 欲合并的图像数组的地址 |
count | 欲合并的图像的个数 |
dst(destination) | 输出图片 |
CV_EXPORTS_W void merge(InputArrayOfArrays mv, OutputArray dst);
参数 | 含义 |
---|---|
mv(mat vector) | 欲合并的图像数组,可以使用STL容器vector。 |
dst(destination) | 输出图片 |
CV_EXPORTS_W void cvtColor( InputArray src, OutputArray dst, int code, int dstCn = 0 );
参数 | 含义 |
---|---|
src(source) | 源图像 |
dst(destination) | 输出图片 |
code | 转换码 |
cv::COLOR_BGR2RGB
cv::COLOR_RGB2BGR
cv::COLOR_RGBA2BGRA
cv::COLOR_BGRA2RGBA
cv::COLOR_RGB2RGBA
cv::COLOR_BGR2BGRA
cv::COLOR_RGBA2RGB
cv::COLOR_BGRA2BGR
cv::COLOR_RGB2GRAY
cv::COLOR_BGR2GRAY
cv::COLOR_RGBA2GRAY
cv::COLOR_BGRA2GRAY
cv::COLOR_GRAY2RGB
cv::COLOR_GRAY2BGR
cv::COLOR_GRAY2RGBA
cv::COLOR_GRAY2BGRA
cv::COLOR_RGB2BGR565
cv::COLOR_BGR2BGR565
cv::COLOR_BGR5652RGB
cv::COLOR_BGR5652BGR
cv::COLOR_RGBA2BGR565
cv::COLOR_BGRA2BGR565
cv::COLOR_BGR5652RGBA
cv::COLOR_BGR5652BGRA
cv::COLOR_GRAY2BGR555
cv::COLOR_BGR5552GRAY
cv::COLOR_RGB2XYZ
cv::COLOR_BGR2XYZ
cv::COLOR_XYZ2RGB
cv::COLOR_XYZ2BGR
cv::COLOR_RGB2YCrCb
cv::COLOR_BGR2YCrCb
cv::COLOR_YCrCb2RGB
cv::COLOR_YCrCb2BGR
cv::COLOR_RGB2HSV
cv::COLOR_BGR2HSV
cv::COLOR_HSV2RGB
cv::COLOR_HSV2BGR
cv::COLOR_RGB2HLS
cv::COLOR_BGR2HLS
cv::COLOR_HLS2RGB
cv::COLOR_HLS2BGR
cv::COLOR_RGB2Lab
cv::COLOR_BGR2Lab
cv::COLOR_Lab2RGB
cv::COLOR_Lab2BGR
cv::COLOR_RGB2Luv
cv::COLOR_BGR2Luv
cv::COLOR_Luv2RGB
cv::COLOR_Luv2BGR
cv::COLOR_BayerBG2RGB
cv::COLOR_BayerGB2RGB
cv::COLOR_BayerRG2RGB
cv::COLOR_BayerGR2RGB
cv::COLOR_BayerBG2BGR
cv::COLOR_BayerGB2BGR
cv::COLOR_BayerRG2BGR
cv::COLOR_BayerGR2BGR
Mat.ptr(i,j)=Mat.ptr(i,j)*a+b
a:控制对比度增益
b:控制亮度增益
Mat xuenai = imread("xuenai.jpg");
imshow("xuenai", xuenai);
for(int i=0;i<xuenai.rows;i++){
for(int j=0;j<xuenai.cols;j++){
for(int k=0;k<xuenai.channels();k++) {
xuenai.at<Vec3b>(i, j)[k] = saturate_cast<uchar>(xuenai.at<Vec3b>(i, j)[k] * 1.2 + 30);
}
}
}
imshow("xuenai_convertTo",xuenai);
waitKey();
void Mat::convertTo( OutputArray m, int rtype, double alpha=1, double beta=0 ) const;
参数 | 含义 |
---|---|
m(mat) | 输出图片 |
rtype(result type) | 输出图片的深度,-1表示与原图一致 |
alpha | 对应系数 |
beta | 对应常数 |
Mat xuenai = imread("xuenai.jpg");
imshow("xuenai", xuenai);
xuenai.convertTo(xuenai,-1,1.2,30);
imshow("xuenai_convertTo",xuenai);
waitKey();
可以看到效果是一样的
CV_EXPORTS_W void addWeighted(InputArray src1, double alpha, InputArray src2,
double beta, double gamma, OutputArray dst, int dtype = -1);
参数 | 含义 |
---|---|
src(source1) | 输入图片1 |
alpha | src1的权重 |
src2(source2) | 输入图片2 |
beta | src2的权重 |
gamma | 额外的增量 |
dst(destination) | 输出图片 |
dtype(destination type) | 输出图片的数据类型,-1表示与输入图片一致 |
CV_EXPORTS_W void resize( InputArray src, OutputArray dst,
Size dsize, double fx = 0, double fy = 0,
int interpolation = INTER_LINEAR );
参数 | 含义 |
---|---|
src(source) | 输入图片 |
dsize(destination size) | 输出图片的尺寸 |
fx | x方向(width方向)的缩放比例,如果它是0,那么它就会按照(double)dsize.width/src.cols来计算 |
fy | y方向(height方向)的缩放比例,如果它是0,那么它就会按照(double)dsize.height/src.rows来计算 |
interpolation | 插值算法的选择 |
enum InterpolationFlags{ /** nearest neighbor interpolation */ INTER_NEAREST = 0, /** bilinear interpolation */ INTER_LINEAR = 1, /** bicubic interpolation */ INTER_CUBIC = 2, /** resampling using pixel area relation. It may be a preferred method for image decimation, as it gives moire'-free results. But when the image is zoomed, it is similar to the INTER_NEAREST method. */ INTER_AREA = 3, /** Lanczos interpolation over 8x8 neighborhood */ INTER_LANCZOS4 = 4, /** Bit exact bilinear interpolation */ INTER_LINEAR_EXACT = 5, /** Bit exact nearest neighbor interpolation. This will produce same results as the nearest neighbor method in PIL, scikit-image or Matlab. */ INTER_NEAREST_EXACT = 6, /** mask for interpolation codes */ INTER_MAX = 7, /** flag, fills all of the destination image pixels. If some of them correspond to outliers in the source image, they are set to zero */ WARP_FILL_OUTLIERS = 8, /** flag, inverse transformation For example, #linearPolar or #logPolar transforms: - flag is __not__ set: \f$dst( \rho , \phi ) = src(x,y)\f$ - flag is set: \f$dst(x,y) = src( \rho , \phi )\f$ */ WARP_INVERSE_MAP = 16 };
使用注意事项:
//缩小一倍
CV_EXPORTS_W void pyrDown( InputArray src, OutputArray dst,
const Size& dstsize = Size(), int borderType = BORDER_DEFAULT );
//放大一倍
CV_EXPORTS_W void pyrUp( InputArray src, OutputArray dst,
const Size& dstsize = Size(), int borderType = BORDER_DEFAULT );
参数 | 含义 |
---|---|
src(source) | 输入图片 |
dst(destination) | 输出图片 |
dstsize(destination size) | 输出图片的尺寸,默认自动调整 |
borderType | 边界填充方式,默认为黑边。如果没有设置dstsize,则不会出现黑边,因为已经进行了自动调整 |
CV_EXPORTS_W double threshold( InputArray src, OutputArray dst,
double thresh, double maxval, int type );
参数 | 含义 |
---|---|
src(source) | 输入图片 |
dst(destination) | 输出图片 |
thresh(threshold) | 阈值 |
maxval(max value) | 最大值 |
type | 阈值类型 |
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
};
首先指定像素的灰度值的阈值,遍历图像中像素值,如果像素的灰度值大于这个阈值,则将这个像素设置为最大像素值(8位灰度值最大为255);若像素的灰度值小于阈值,则将该像素点像素值赋值为0。公式以及示意图如下:
首先也要指定一个阈值,不同的是在对图像进行阈值化操作时与阈值二值化相反,当像素的灰度值超过这个阈值的时候为该像素点赋值为0;当该像素的灰度值低于该阈值时赋值为最大值。公式及示意图如下:
给定像素值阈值,在图像中像素的灰度值大于该阈值的像素点被设置为该阈值,而小于该阈值的像素值保持不变。公式以及示意图如下:
与截断阈值化相反,像素点的灰度值如果大于该阈值则像素值不变,如果像素点的灰度值小于该阈值,则该像素值设置为0.公式以及示意图如下:
像素值大于阈值的像素赋值为0,而小于该阈值的像素值则保持不变,公式以及示意图如下:
inline
Mat Mat::operator()( const Rect& roi ) const
{
return Mat(*this, roi);
}
以下为实例
Mat xuenai = imread("xuenai.jpg");
resize(xuenai,xuenai,Size(1000,1000));
imshow("xuenai", xuenai);
Mat tuanzi(xuenai,(Rect(0,0,500,1000)));
imshow("tuanzi",tuanzi);
waitKey();
Mat::Mat(const Mat& m, const Rect& roi);
以下为实例
Mat xuenai = imread("xuenai.jpg");
resize(xuenai,xuenai,Size(1000,1000));
imshow("xuenai", xuenai);
Mat tuanzi(xuenai(Rect(0,0,500,1000)));
imshow("tuanzi",tuanzi);
waitKey();
template<typename _Tp> inline Rect_<_Tp>::Rect_(_Tp _x, _Tp _y, _Tp _width, _Tp _height) : x(_x), y(_y), width(_width), height(_height) {} template<typename _Tp> inline Rect_<_Tp>::Rect_(const Point_<_Tp>& org, const Size_<_Tp>& sz) : x(org.x), y(org.y), width(sz.width), height(sz.height) {} template<typename _Tp> inline Rect_<_Tp>::Rect_(const Point_<_Tp>& pt1, const Point_<_Tp>& pt2) { x = std::min(pt1.x, pt2.x); y = std::min(pt1.y, pt2.y); width = std::max(pt1.x, pt2.x) - x; height = std::max(pt1.y, pt2.y) - y; }
CV_EXPORTS_W void flip(InputArray src, OutputArray dst, int flipCode);
参数 | 含义 |
---|---|
src(source) | 输入图片 |
dst(destination) | 输出图片 |
flipCode | 翻转类型,参见下表 |
flipCode 可选值 | 含义 |
---|---|
flipcode==0 | 上下翻转 |
flipcod>0 | 左右翻转 |
flipcode<0 | 上下加左右翻转,等价于旋转180° |
Mat xuenai = imread("xuenai.jpg");
imshow("xuenai", xuenai);
Mat xuenai_flip(xuenai.size(), xuenai.type());
flip(xuenai, xuenai_flip, 0);
imshow("xuenai_flip", xuenai_flip);
waitKet();
CV_EXPORTS_W void rotate(InputArray src, OutputArray dst, int rotateCode);
enum RotateFlags {
ROTATE_90_CLOCKWISE = 0, //!<Rotate 90 degrees clockwise
ROTATE_180 = 1, //!<Rotate 180 degrees clockwise
ROTATE_90_COUNTERCLOCKWISE = 2, //!<Rotate 270 degrees clockwise
};
参数 | 含义 |
---|---|
src(source) | 输入图片 |
dst(destination) | 输出图片 |
rotateCode | 旋转类型 |
Mat xuenai = imread("xuenai.jpg");
imshow("xuenai", xuenai);
Mat xuenai_rotate(xuenai.size(), xuenai.type());
rotate(xuenai, xuenai_rotate, ROTATE_180);
imshow("xuenai_rotate", xuenai_rotate);
waitKet();
CV_EXPORTS_W void warpAffine( InputArray src, OutputArray dst,
InputArray M, Size dsize,
int flags = INTER_LINEAR,
int borderMode = BORDER_CONSTANT,
const Scalar& borderValue = Scalar());
参数 | 含义 |
---|---|
src(source) | 输入图片 |
dst(destination) | 输出图片 |
M | 变换矩阵 |
dsize(destination size) | 输出图片的尺寸,若不对输出图片的尺寸进行调整,那么很可能会出现黑边 |
flags | 插值算法 |
borderMode | 边界外推法 |
borderValue | 填充边界的值 |
float delta_x=200,delta_y=200;
float M_values[]={1,0,delta_x,
0,1,delta_y};
Mat M(Size(3,2),CV_32F,M_values);
delta_x:x方向上的偏移量
delta_y:y方向上的偏移量
M_values:必须是浮点类型的数组对象
M:必须是CV_32F,不能用逗号式分隔创建
Mat xuenai = imread("xuenai.jpg");
imshow("xuenai",xuenai);
double M_values[]={1,0,200,
0,1,200};
Mat M(Size(3,2), CV_64F,M_values);
Mat xuenai_shift(xuenai.size(),xuenai.type());
warpAffine(xuenai,xuenai_shift,M,xuenai.size());
imshow("xuenai_shift",xuenai_shift);
waitKet();
inline
Mat getRotationMatrix2D(Point2f center, double angle, double scale)
{
return Mat(getRotationMatrix2D_(center, angle, scale), true);
}
参数 | 含义 |
---|---|
center | 旋转中心点的坐标 |
angle | 逆时针偏角 |
scale | 生成图与原图之比 |
Mat xuenai = imread("xuenai.jpg");
imshow("xuenai", xuenai);
Mat M= getRotationMatrix2D(Point2f(xuenai.cols/2,xuenai.rows/2),45,1);
Mat xuenai_rotate(xuenai.size(),xuenai.type());
warpAffine(xuenai,xuenai_rotate,M,xuenai.size());
imshow("xuenai_flip",xuenai_rotate);
CV_EXPORTS Mat getAffineTransform( const Point2f src[], const Point2f dst[] );
参数 | 含义 |
---|---|
src[](source[]) | 输入图片的坐标点集,含三个坐标点 |
dst[](destination[]) | 三个坐标点变换的目标位置 |
CV_EXPORTS_W void warpPerspective( InputArray src, OutputArray dst,
InputArray M, Size dsize,
int flags = INTER_LINEAR,
int borderMode = BORDER_CONSTANT,
const Scalar& borderValue = Scalar());
参数 | 含义 |
---|---|
src(source) | 输入图片 |
dst(destination) | 输出图片 |
M | 变换矩阵 |
dsize(destination size) | 输出图片的尺寸,若不对输出图片的尺寸进行调整,那么很可能会出现黑边 |
flags | 插值算法 |
borderMode | 边界外推法 |
borderValue | 填充边界的值 |
CV_EXPORTS_W Mat getPerspectiveTransform(InputArray src, InputArray dst, int solveMethod = DECOMP_LU);
参数 | 含义 |
---|---|
src(source) | 输入图片 |
dst(destination) | 输出图片 |
CV_EXPORTS Mat getPerspectiveTransform(const Point2f src[], const Point2f dst[], int solveMethod = DECOMP_LU);
参数 | 含义 |
---|---|
src[](source[]) | 输入图片的坐标点集,含四个坐标点 |
dst[](destination[]) | 三个坐标点变换的目标位置 |
Mat origin = imread("origin.jpg");
Point2f point2F_origin[4]={Point2f (405,105),Point2f(2469,217),Point2f(2573,3489),Point2f(349,3547)};
Point2f point2F_tansform[4]={Point2f (0,0),Point2f(2500,0),Point2f(2500,3500),Point2f(0,3500)};
Mat M=getPerspectiveTransform(point2F_origin,point2F_tansform);
Mat transfrom(origin.size(),origin.type());
warpPerspective(origin,transfrom,M,Size(2500,3500));
resize(origin,origin,Size(500,700));
resize(transfrom,transfrom,Size(500,700));
imshow("origin",origin);
imshow("transform",transfrom);
Mat kernal=Mat::ones(Size(ksize,ksize),CV_64F)/(ksize*ksize);
CV_EXPORTS_W void blur( InputArray src, OutputArray dst,
Size ksize, Point anchor = Point(-1,-1),
int borderType = BORDER_DEFAULT );
参数 | 含义 |
---|---|
src(source) | 输入图片 |
dst(destination) | 输出图片 |
ksize(kernal size) | 卷积核宽高,必须是正奇数 |
anchor | 滤波器中心像素位置,取(-1,-1)表示几何中心 |
borderType | 边界填充方式,默认为黑边 |
Mat xuenai = imread("xuenai.jpg");
imshow("xuenai",xuenai);
Mat xuenai_blur(xuenai.size(),xuenai.type());
blur(xuenai,xuenai_blur,Size(3,5));
imshow("xuenai_blur",xuenai_blur);
waitKet();
二维高斯函数表述为:
对应图形:
代码实现(不区分sigmaX与sigmaY)
void GetGaussianKernel(Mat kernal, const int ksize,const double sigma) { const double PI=4.0*atan(1.0); //圆周率π赋值 int center= ksize/2; double sum=0; for(int i=0;i<ksize;i++) { for(int j=0;j<ksize;j++) { kernal.ptr(i,j)=(1/(2*PI*sigma*sigma))*exp(-((i-center)*(i-center)+(j-center)*(j-center))/(2*sigma*sigma)); sum+=kernal.ptr(i,j); } } for(int i=0;i<ksize;i++) { for(int j=0;j<ksize;j++) { kernal.ptr(i,j)/=sum; } } return ; }
CV_EXPORTS_W void GaussianBlur( InputArray src, OutputArray dst, Size ksize,
double sigmaX, double sigmaY = 0,
int borderType = BORDER_DEFAULT );
参数 | 含义 |
---|---|
src(source) | 输入图片 |
dst(destination) | 输出图片 |
ksize(kernal size) | 卷积核宽高。如果这个尺寸我们设其为非正数,那么OpenCV会从第五个参数sigmaSpace来计算出它来。 |
sigmaX | x方向上的标准差 |
sigmaY | y方向上的标准差。默认输入量为0,则将其设置为等于sigmaX,如果两个轴的标准差均为0,则根据输入的高斯滤波器尺寸计算标准偏差。 |
borderType | 边界填充方式,默认为黑边 |
Mat xuenai = imread("xuenai.jpg");
imshow("xuenai",xuenai);
Mat xuenai_Gauss(xuenai.size(),xuenai.type());
GaussianBlur(xuenai,xuenai_Gauss,Size(-1,-1),10);
imshow("xuenai_Gauss",xuenai_Gauss);
waitKet();
取滤波器内的中值作为输出,可以很好的抑制椒盐噪声
CV_EXPORTS_W void medianBlur( InputArray src, OutputArray dst, int ksize );
参数 | 含义 |
---|---|
src(source) | 输入图片 |
dst(destination) | 输出图片 |
ksize(kernal size) | 卷积核边长,必须是正奇数 |
Mat xuenai = imread("xuenai.jpg");
imshow("xuenai",xuenai);
Mat xuenai_median(xuenai.size(),xuenai.type());
medianBlur(xuenai,xuenai_median,5);
imshow("xuenai_median",xuenai_median);
waitKet();
双边滤波器的好处是可以做边缘保存(edge preserving),一般用高斯滤波去降噪,会较明显地模糊边缘,对于高频细节的保护效果并不明显。双边滤波器顾名思义比高斯滤波多了一个高斯方差sigma-d,它是基于空间分布的高斯滤波函数,所以在边缘附近,离的较远的像素不会太多影响到边缘上的像素值,这样就保证了边缘附近像素值的保存。但是由于保存了过多的高频信息,对于彩色图像里的高频噪声,双边滤波器不能够干净的滤掉,只能够对于低频信息进行较好的滤波。
CV_EXPORTS_W void bilateralFilter( InputArray src, OutputArray dst, int d,
double sigmaColor, double sigmaSpace,
int borderType = BORDER_DEFAULT );
参数 | 含义 |
---|---|
src(source) | 输入图片 |
dst(destination) | 输出图片 |
d | 卷积核边长。如果这个值我们设其为非正数,那么OpenCV会从第五个参数sigmaSpace来计算出它来。 |
sigmaColor | 颜色空间滤波器的sigma值。这个参数的值越大,就表明该像素邻域内有更宽广的颜色会被混合到一起,产生较大的半相等颜色区域。 |
sigmaSpace | 坐标空间中滤波器的sigma值,坐标空间的标注方差。他的数值越大,意味着越远的像素会相互影响,从而使更大的区域足够相似的颜色获取相同的颜色。当d>0,卷积核大小已被指定且与sigmaSpace无关。否则,d正比于sigmaSpace。 |
borderType | 边界填充方式,默认为黑边 |
Mat xuenai = imread("xuenai.jpg");
imshow("xuenai",xuenai);
Mat xuenai_bilateral(xuenai.size(),xuenai.type());
bilateralFilter(xuenai,xuenai_bilateral,-1,100,10);
imshow("xuenai_bilateral",xuenai_bilateral);
waitKet();
CV_EXPORTS_W Mat getStructuringElement(int shape, Size ksize, Point anchor = Point(-1,-1));
enum MorphShapes {
MORPH_RECT = 0, //!< a rectangular structuring element: \f[E_{ij}=1\f]
MORPH_CROSS = 1, //!< a cross-shaped structuring element:
//!< \f[E_{ij} = \begin{cases} 1 & \texttt{if } {i=\texttt{anchor.y } {or } {j=\texttt{anchor.x}}} \\0 & \texttt{otherwise} \end{cases}\f]
MORPH_ELLIPSE = 2 //!< an elliptic structuring element, that is, a filled ellipse inscribed
//!< into the rectangle Rect(0, 0, esize.width, 0.esize.height)
};
shape:滤波器形状
ksize(kernal size):滤波器大小
anchor:滤波器中心像素位置,取(-1,-1)表示几何中心
腐蚀:取滤波器内的最小值作为输出
膨胀:取滤波器内的最大值作为输出
CV_EXPORTS_W void erode( InputArray src, OutputArray dst, InputArray kernel,
Point anchor = Point(-1,-1), int iterations = 1,
int borderType = BORDER_CONSTANT,
const Scalar& borderValue = morphologyDefaultBorderValue() );
参数 | 含义 |
---|---|
src(source) | 输入图片,尽量是二值图 |
dst(destination) | 输出图片 |
kernal | 滤波器矩阵 |
anchor | 滤波器中心像素位置,取(-1,-1)表示几何中心 |
iterations | 执行erode函数的次数,默认执行一次 |
borderType | 边界填充方式,默认为黑边 |
borderValue | 填充边界的值 |
Mat xuenai = imread("xuenai.jpg");
Mat xuenai_gray(xuenai.size(),xuenai.type());
cvtColor(xuenai,xuenai_gray,COLOR_BGR2GRAY);
Mat xuenai_threshold(xuenai.size(),xuenai.type());
threshold(xuenai_gray,xuenai_threshold,100,255,THRESH_BINARY);
imshow("xuenai_threshold",xuenai_threshold);
Mat kernal=getStructuringElement(MORPH_RECT,Size(3,3));
Mat xuenai_erode(xuenai.size(),xuenai.type());
erode(xuenai_threshold,xuenai_erode,kernal);
imshow("xuenai_erode",xuenai_erode);
waitKet();
CV_EXPORTS_W void dilate( InputArray src, OutputArray dst, InputArray kernel,
Point anchor = Point(-1,-1), int iterations = 1,
int borderType = BORDER_CONSTANT,
const Scalar& borderValue = morphologyDefaultBorderValue() );
参数 | 含义 |
---|---|
src(source) | 输入图片,尽量是二值图 |
dst(destination) | 输出图片 |
kernal | 滤波器矩阵 |
anchor | 滤波器中心像素位置,取(-1,-1)表示几何中心 |
iterations | 执行erode函数的次数,默认执行一次 |
borderType | 边界填充方式,默认为黑边 |
borderValue | 填充边界的值 |
Mat xuenai = imread("xuenai.jpg");
Mat xuenai_gray(xuenai.size(),xuenai.type());
cvtColor(xuenai,xuenai_gray,COLOR_BGR2GRAY);
Mat xuenai_threshold(xuenai.size(),xuenai.type());
threshold(xuenai_gray,xuenai_threshold,100,255,THRESH_BINARY);
imshow("xuenai_threshold",xuenai_threshold);
Mat kernal=getStructuringElement(MORPH_RECT,Size(3,3));
Mat xuenai_dilate(xuenai.size(),xuenai.type());
dilate(xuenai_threshold,xuenai_dilate,kernal);
imshow("xuenai_dilate",xuenai_dilate);
waitKet();
CV_EXPORTS_W void morphologyEx( InputArray src, OutputArray dst,
int op, InputArray kernel,
Point anchor = Point(-1,-1), int iterations = 1,
int borderType = BORDER_CONSTANT,
const Scalar& borderValue = morphologyDefaultBorderValue() );
参数 | 含义 |
---|---|
src(source) | 输入图片,尽量是二值图 |
dst(destination) | 输出图片 |
op(option) | 变换类型 |
kernal | 滤波器矩阵 |
anchor | 滤波器中心像素位置,取(-1,-1)表示几何中心 |
iterations | 执行erode函数的次数,默认执行一次 |
borderType | 边界填充方式,默认为黑边 |
borderValue | 填充边界的值 |
enum MorphTypes{ MORPH_ERODE = 0, //腐蚀 MORPH_DILATE = 1, //膨胀 MORPH_OPEN = 2, //开 MORPH_CLOSE = 3, //闭 MORPH_GRADIENT = 4, //形态学梯度 MORPH_TOPHAT = 5, //顶帽 MORPH_BLACKHAT = 6, //黑帽 MORPH_HITMISS = 7 //击中击不中变换 };
对输入图片先进行腐蚀,然后进行膨胀。可以用来屏蔽与滤波器大小相当的亮部。
Mat xuenai = imread("xuenai.jpg");
Mat xuenai_gray(xuenai.size(),xuenai.type());
cvtColor(xuenai,xuenai_gray,COLOR_BGR2GRAY);
Mat xuenai_threshold(xuenai.size(),xuenai.type());
threshold(xuenai_gray,xuenai_threshold,100,255,THRESH_BINARY);
imshow("xuenai_threshold",xuenai_threshold);
Mat kernal=getStructuringElement(MORPH_RECT,Size(3,3));
Mat xuenai_morphology(xuenai.size(),xuenai.type());
morphologyEx(xuenai_threshold,xuenai_morphology,MORPH_OPEN,kernal);
imshow("xuenai_morphology",xuenai_morphology);
waitKet();
对输入图片先进行膨胀,然后进行腐蚀。可以用来屏蔽与滤波器大小相当的暗部。
Mat xuenai = imread("xuenai.jpg");
Mat xuenai_gray(xuenai.size(),xuenai.type());
cvtColor(xuenai,xuenai_gray,COLOR_BGR2GRAY);
Mat xuenai_threshold(xuenai.size(),xuenai.type());
threshold(xuenai_gray,xuenai_threshold,100,255,THRESH_BINARY);
imshow("xuenai_threshold",xuenai_threshold);
Mat kernal=getStructuringElement(MORPH_RECT,Size(3,3));
Mat xuenai_morphology(xuenai.size(),xuenai.type());
morphologyEx(xuenai_threshold,xuenai_morphology,MORPH_CLOSE,kernal);
imshow("xuenai_morphology",xuenai_morphology);
waitKet();
对输入图片先进行开操作,然后原图-开操作图。可以用来提取与滤波器大小相当的亮部。
Mat xuenai = imread("xuenai.jpg");
Mat xuenai_gray(xuenai.size(),xuenai.type());
cvtColor(xuenai,xuenai_gray,COLOR_BGR2GRAY);
Mat xuenai_threshold(xuenai.size(),xuenai.type());
threshold(xuenai_gray,xuenai_threshold,100,255,THRESH_BINARY);
imshow("xuenai_threshold",xuenai_threshold);
Mat kernal=getStructuringElement(MORPH_RECT,Size(3,3));
Mat xuenai_morphology(xuenai.size(),xuenai.type());
morphologyEx(xuenai_threshold,xuenai_morphology,MORPH_TOPHAT,kernal);
imshow("xuenai_morphology",xuenai_morphology);
waitKet();
对输入图片先进行闭操作,然后闭操作图-原图。可以用来提取与滤波器大小相当的暗部。
Mat xuenai = imread("xuenai.jpg");
Mat xuenai_gray(xuenai.size(),xuenai.type());
cvtColor(xuenai,xuenai_gray,COLOR_BGR2GRAY);
Mat xuenai_threshold(xuenai.size(),xuenai.type());
threshold(xuenai_gray,xuenai_threshold,100,255,THRESH_BINARY);
imshow("xuenai_threshold",xuenai_threshold);
Mat kernal=getStructuringElement(MORPH_RECT,Size(3,3));
Mat xuenai_morphology(xuenai.size(),xuenai.type());
morphologyEx(xuenai_threshold,xuenai_morphology,MORPH_BLACKHAT,kernal);
imshow("xuenai_morphology",xuenai_morphology);
waitKet();
膨胀图与腐蚀图之差。可以用来 提取边界轮廓 ,但提取效果比不上专业的边缘检测算法。
Mat xuenai = imread("xuenai.jpg");
Mat xuenai_gray(xuenai.size(),xuenai.type());
cvtColor(xuenai,xuenai_gray,COLOR_BGR2GRAY);
Mat xuenai_threshold(xuenai.size(),xuenai.type());
threshold(xuenai_gray,xuenai_threshold,100,255,THRESH_BINARY);
imshow("xuenai_threshold",xuenai_threshold);
Mat kernal=getStructuringElement(MORPH_RECT,Size(3,3));
Mat xuenai_morphology(xuenai.size(),xuenai.type());
morphologyEx(xuenai_threshold,xuenai_morphology,MORPH_GRADIENT,kernal);
imshow("xuenai_morphology",xuenai_morphology);
waitKet();
击中击不中变换由下面三步构成:
用结构元素B1来腐蚀输入图像
用结构元素B2来腐蚀输入图像的补集
前两步结果的与运算
结构元素B1和B2可以结合为一个元素B。例如:
结构元素:左B1(击中元素),中B2(击不中元素),右B(两者结合)
本例中,我们寻找这样一种结构模式,中间像素属于背景,其上下左右属于前景,其余领域像素忽略不计(背景为黑色,前景为白色)。然后用上面的核在输入图像中找这种结构。从下面的输出图像中可以看到,输入图像中只有一个位置满足要求。
输入二值图像
输出二值图像
int sdepth | int ddepth |
---|---|
CV_8U | CV_16S/CV_32F/CV_64F |
CV_16U/CV_16S | CV_32F/CV_64F |
CV_32F | CV_32F/CV_64F |
CV_64F | CV_64F |
CV_EXPORTS_W void normalize( InputArray src, InputOutputArray dst, double alpha = 1, double beta = 0,
int norm_type = NORM_L2, int dtype = -1, InputArray mask = noArray());
参数 | 含义 |
---|---|
src(source) | 输入数组 |
dst(destination) | 输出数组 |
alpha | 如果norm_type为NORM_MINMAX ,则alpha为最小值或最大值;如果norm_type为其他类型,则为归一化要乘的系数 |
beta | 如果norm_type为NORM_MINMAX ,则beta为最小值或最大值;如果norm_type为其他类型,beta被忽略. |
norm_type | 归一化类型,详见下面的内容 |
iterations | 执行erode函数的次数,默认执行一次 |
dtype | 输出数组的深度,若输入-1则表示与src一致。如果不能判断需要的深度,则可以输入-1然后使用convertScaleAbs绝对值化,这也是最推荐的做法,而不推荐自己判断深度。 |
mask | 掩码,用于指示函数是否仅仅对指定的元素进行操作。大小必须与src保持一致。具体用法见8.1.4 |
enum NormTypes {
NORM_INF = 1,
NORM_L1 = 2,
NORM_L2 = 4,
NORM_L2SQR = 5,
NORM_HAMMING = 6,
NORM_HAMMING2 = 7,
NORM_TYPE_MASK = 7, //!< bit-mask which can be used to separate norm type from norm flags
NORM_RELATIVE = 8, //!< flag
NORM_MINMAX = 32 //!< flag
};
NORM_L1
NORM_L2
NORM_INF
CV_EXPORTS_W void convertScaleAbs(InputArray src, OutputArray dst,
double alpha = 1, double beta = 0);
参数 | 含义 |
---|---|
src(source) | 输入图片 |
dst(destination) | 输出图片 |
Mat kernalX=Mat_<int>(Size(3,3))<<(-1,0,1
-2,0,2
-1,0,1);
Mat kernalY=Mat_<int>(Size(3,3))<<(-1,-2,1
0,0,0
1,2,1);
CV_EXPORTS_W void Sobel( InputArray src, OutputArray dst, int ddepth,
int dx, int dy, int ksize = 3,
double scale = 1, double delta = 0,
int borderType = BORDER_DEFAULT );
参数 | 含义 |
---|---|
src(source) | 输入图片,数据类型Mat |
dst(destination) | 输出图片,数据类型Mat |
ddepth(destination depth) | 输出图片的深度(CV_16F) |
dx | x方向导数的阶数,一般取1 |
dy | y方向导数的阶数,一般取1 |
ksize(kernal size) | 卷积核边长,默认为3 |
scale | 生成图与原图的缩放比例,默认为1 |
delta | 额外的增量,默认为0 |
borderType | 边界填充方式,默认为黑边 |
Mat xuenai = imread("xuenai.jpg"); imshow("xuenai", xuenai); //转灰度图 Mat xuenai_gray(xuenai.size(),xuenai.type()); cvtColor(xuenai,xuenai_gray,COLOR_BGR2GRAY); //同时在x,y方向上调用Sobel Mat xuenai_sobel1(xuenai.size(),xuenai.type()); Sobel(xuenai_gray,xuenai_sobel1,CV_16S,1,1,3); convertScaleAbs(xuenai_sobel1,xuenai_sobel1); imshow("xuenai_sobel1",xuenai_sobel1); //在x,y方向上分别各调用一次Sobel Mat xuenai_xsobel(xuenai.size(),xuenai.type());Mat xuenai_ysobel(xuenai.size(),xuenai.type());Mat xuenai_sobel2(xuenai.size(),xuenai.type()); Sobel(xuenai_gray,xuenai_xsobel,CV_16S,1,0,3); convertScaleAbs(xuenai_xsobel,xuenai_xsobel); Sobel(xuenai_gray,xuenai_ysobel,CV_16S,0,1,3); convertScaleAbs(xuenai_ysobel,xuenai_ysobel); addWeighted(xuenai_xsobel,0.5,xuenai_ysobel,0.5,0,xuenai_sobel2); imshow("xuenai_sobel2",xuenai_sobel2); waitKey();
可以看到效果差了很多
虽然Sobel算子可以有效的提取图像边缘,但是对图像中较弱的边缘提取效果较差。因此为了能够有效的提取出较弱的边缘,需要将像素值间的差距增大,因此引入Scharr算子。Scharr算子是对Sobel算子差异性的增强,因此两者之间的在检测图像边缘的原理和使用方式上相同。
Mat kernalX=Mat_<int>(Size(3,3))<<(-3,0,3
-10,0,10
-3,0,3);
Mat kernalY=Mat_<int>(Size(3,3))<<(-3,-10,3
0,0,0
3,10,3);
CV_EXPORTS_W void Scharr( InputArray src, OutputArray dst, int ddepth,
int dx, int dy, double scale = 1, double delta = 0,
int borderType = BORDER_DEFAULT );
参数 | 含义 |
---|---|
src(source) | 输入图片,数据类型Mat |
dst(destination) | 输出图片,数据类型Mat |
ddepth(destination depth) | 输出图片的深度(CV_16F) |
dx | x方向导数的阶数,一般取1 |
dy | y方向导数的阶数,一般取1 |
scale | 生成图与原图的缩放比例,默认为1 |
delta | 额外的增量,默认为0 |
borderType | 边界填充方式,默认为黑边 |
Mat kernal=Mat_<int>(Size(3,3))<<(0,-1,0
-1,4,-1
0,-1,0);
Laplacian算子的卷积核形状决定了它 对噪声非常敏感 ,因此,通常需要通过 滤波平滑处理 。
CV_EXPORTS_W void Laplacian( InputArray src, OutputArray dst, int ddepth,
int ksize = 1, double scale = 1, double delta = 0,
int borderType = BORDER_DEFAULT );
参数 | 含义 |
---|---|
src(source) | 输入图片,数据类型Mat |
dst(destination) | 输出图片,数据类型Mat |
ddepth(destination depth) | 输出图片的深度(CV_16F) |
scale | 生成图与原图的缩放比例,默认为1 |
delta | 额外的增量,默认为0 |
borderType | 边界填充方式,默认为黑边 |
CV_EXPORTS_W void Canny( InputArray image, OutputArray edges,
double threshold1, double threshold2,
int apertureSize = 3, bool L2gradient = false );
参数 | 含义 |
---|---|
image | 输入图片,数据类型Mat |
edges | 输出图片,数据类型Mat |
threshold1 | 最小阈值 |
threshold2 | 最大阈值 |
apertureSize | Sobel卷积核的大小,默认为3。 核越大,对噪声越不敏感,但是边缘检测的错误也会随之增加 |
L2gradient | 计算图像梯度幅度的标识,默认为false,表示L1范数(直接将两个方向的导数的绝对值相加)。如果使用true,表示L2范数(两个方向的导数的平方和再开方) |
Mat xuenai = imread("xuenai.jpg");
imshow("xuenai",xuenai);
Mat xuenai_canny(xuenai.size(),xuenai.type());
Canny(xuenai,xuenai_canny,60,150);
imshow("xuenai_canny",xuenai_canny);
waitKet();
为了检测算法的稳定性,常常需要在图片中人为地添加一些噪声来进行检验。
static void addSaltNoise(const Mat& src,Mat& dst,int num=1000) { dst=src.clone(); for (int k = 0; k < num; k++) { //随机取值行列,得到像素点(i,j) int i = rand() % dst.rows; int j = rand() % dst.cols; //修改像素点(i,j)的像素值 for(int channel=0;channel<src.channels();channel++){ dst.ptr(i,j)[channel]=255; } } for (int k = 0; k < num; k++) { //随机取值行列 default_random_engine engine; uniform_int_distribution<unsigned>u(0,10000); int i = rand() % dst.rows; int j = rand() % dst.cols; //修改像素点(i,j)的像素值 for(int channel=0;channel<src.channels();channel++){ dst.ptr(i,j)[channel]=0; } } return; }
src(source):输入图片
dst(destination):输出图片
num(number):噪声的个数
static void addGaussianNoise(const Mat& src,Mat& dst,InputArray meanValue=10,InputArray std=36){
dst=src.clone();
//构造高斯噪声矩阵
Mat noise(dst.size(),dst.type());
RNG rng(time(NULL));
rng.fill(noise, RNG::NORMAL, meanValue, std);
//将高斯噪声矩阵与原图像叠加得到含噪图像
dst+=noise;
return ;
}
src(source):输入图片
dst(destination):输出图片
meanValue:高斯函数均值
std(standard deviation):高斯函数标准差
void RNG::fill( InputOutputArray mat, int distType, InputArray a, InputArray b, bool saturateRange = false );
参数 | 含义 |
---|---|
mat | 输入输出矩阵,最多支持4通道,超过4通道先用reshape()改变结构 |
distType(distination type) | 可选UNIFORM 或 NORMAL,分别表示均匀分布和高斯分布 |
a | disType是UNIFORM,a表示下界(闭区间);disType是NORMAL,a表示均值 |
b | disType是UNIFORM,b表示上界(开区间);disType是NORMAL,b表示标准差 |
saturateRange | 只针对均匀分布有效。当为真的时候,会先把产生随机数的范围变换到数据类型的范围,再产生随机数;如果为假,会先产生随机数,再进行截断到数据类型的有效区间。 |
CV_EXPORTS_W void line(InputOutputArray img, Point pt1, Point pt2, const Scalar& color,int thickness = 1, int lineType = LINE_8, int shift = 0);
参数 | 含义 |
---|---|
img(image) | 绘制多边形的画布,数据类型Mat |
pt1(point1) | 端点1 |
pt2(point2) | 端点2 |
color | 绘制线条的颜色 |
thickness | 绘制线条的粗细 |
lineType | 绘制线条的连通类型 |
shift | 坐标点小数点位数(not important) |
enum LineTypes {
FILLED = -1,
LINE_4 = 4, //!< 4-connected line
LINE_8 = 8, //!< 8-connected line
LINE_AA = 16 //!< antialiased line
};
CV_EXPORTS_W void rectangle(InputOutputArray img, Point pt1, Point pt2,
const Scalar& color, int thickness = 1,
int lineType = LINE_8, int shift = 0);
参数 | 含义 |
---|---|
img(image) | 绘制多边形的画布,数据类型Mat |
pt1(point1) | 左上角端点 |
pt2(point2) | 右下角端点 |
color | 绘制线条的颜色 |
thickness | 绘制线条的粗细。若取负值,则表示进行填充 |
lineType | 绘制线条的连通类型 |
shift | 坐标点小数点位数(not important) |
CV_EXPORTS_W void rectangle(InputOutputArray img, Rect rec,
const Scalar& color, int thickness = 1,
int lineType = LINE_8, int shift = 0);
参数 | 含义 |
---|---|
img(image) | 绘制多边形的画布,数据类型Mat |
rec(rect) | 一个矩形 |
color | 绘制线条的颜色 |
thickness | 绘制线条的粗细。若取负值,则表示进行填充 |
lineType | 绘制线条的连通类型 |
shift | 坐标点小数点位数(not important) |
CV_EXPORTS_W void circle(InputOutputArray img, Point center, int radius,
const Scalar& color, int thickness = 1,
int lineType = LINE_8, int shift = 0);
参数 | 含义 |
---|---|
img(image) | 绘制多边形的画布,数据类型Mat |
center | 圆心坐标 |
radius | 半径 |
color | 绘制线条的颜色 |
thickness | 绘制线条的粗细。若取负值,则表示进行填充 |
lineType | 绘制线条的连通类型 |
shift | 坐标点小数点位数(not important) |
CV_EXPORTS_W void ellipse(InputOutputArray img, Point center, Size axes,
double angle, double startAngle, double endAngle,
const Scalar& color, int thickness = 1,
int lineType = LINE_8, int shift = 0);
参数 | 含义 |
---|---|
img(image) | 绘制多边形的画布,数据类型Mat |
center | 圆心坐标 |
axes | (x方向上半轴长,y方向上半轴长) |
angle | 顺时针偏角 |
startAngle | 以x方向上的正半轴为起点,偏移一定角度后的起点,从此起点开始画椭圆 |
endAngle | 以x方向上的正半轴为起点,偏移一定角度后的终点,到此为止结束画椭圆 |
color | 绘制线条的颜色 |
thickness | 绘制线条的粗细。若取负值,则表示进行填充 |
lineType | 绘制线条的连通类型 |
shift | 坐标点小数点位数(not important) |
Mat canvas(Size(1000,1000),CV_8U,Scalar(255));
ellipse(canvas,Point2f(500,500),Size(50,100),0,0,90,Scalar(0,0,0),5);
imshow("canvas",canvas);
waitKey();
Mat canvas(Size(1000,1000),CV_8U,Scalar(255));
ellipse(canvas,Point2f(500,500),Size(50,100),20,0,360,Scalar(0,0,0),5);
imshow("canvas",canvas);
waitKey();
Mat canvas(Size(1000,1000),CV_8U,Scalar(255));
ellipse(canvas,Point2f(500,500),Size(50,100),20,0,180,Scalar(0,0,0),5);
imshow("canvas",canvas);
waitKey();
class CV_EXPORTS RotatedRect { public: //! default constructor RotatedRect(); /*center:质心坐标 size:(x方向上全边长,y方向上全边长) angle:顺时针偏角 */ RotatedRect(const Point2f& center, const Size2f& size, float angle); /** 三点确定一矩形,记得要互相垂直 */ RotatedRect(const Point2f& point1, const Point2f& point2, const Point2f& point3); /** 返回四个角点坐标,要用Point2f类型的数组对象作为参数传入,不能是仅仅是Point类型的数组对象*/ void points(Point2f pts[]) const; //! returns the minimal up-right integer rectangle containing the rotated rectangle Rect boundingRect() const; //! returns the minimal (exact) floating point rectangle containing the rotated rectangle, not intended for use with images Rect_<float> boundingRect2f() const; //! returns the rectangle mass center Point2f center; //! returns width and height of the rectangle Size2f size; //! returns the rotation angle. When the angle is 0, 90, 180, 270 etc., the rectangle becomes an up-right rectangle. float angle; };
下面是自定义的一个快捷画斜矩形的函数
void drawRotatedRect(InputOutputArray img, RotatedRect rRect,const Scalar& color, int thickness = 1,int lineType = LINE_8, int shift = 0){
Point2f vertices[4];
rRect.points(vertices);
for(int i=0;i<4;i++){
line(img,vertices[i],vertices[(i+1)%4],color,lineType,shift);
}
}
CV_EXPORTS_W void polylines(InputOutputArray img, InputArrayOfArrays pts,
bool isClosed, const Scalar& color,
int thickness = 1, int lineType = LINE_8, int shift = 0 );
参数 | 含义 |
---|---|
img(image) | 绘制多边形的画布,数据类型Mat |
pts(points) | 多边形角点的坐标点集,数据类型vector<Point>或vector<vector<Point>>,若为vector<Point2f>或vector<vector<Point2f>>会报错 |
isClosed | 多边形是否闭合,如果isClosed为真,那么pts的最后一个点将和第一个点连起来,否则轮廓被认为是不封闭的。 |
color | 绘制线条的颜色 |
thickness | 绘制线条的粗细。若取负值,则表示进行填充 |
lineType | 绘制线条的连通类型 |
shift | 坐标点小数点位数(not important) |
CV_EXPORTS void polylines(InputOutputArray img, const Point* const* pts, const int* npts,
int ncontours, bool isClosed, const Scalar& color,
int thickness = 1, int lineType = LINE_8, int shift = 0 );
参数 | 含义 |
---|---|
img(image) | 绘制多边形的画布,数据类型Mat |
pts(points) | 多个多边形的角点坐标点集的地址的数组。如果有3个四边形的角点坐标点集Point[3][4],那么pts={Point[0][0],Point[1][0],Point[2][0],} |
npts(number points) | 多个多边形的角点坐标点集的元素个数排列成的数组,用来指示需要用到pts[i]中的几个元素。如果有3个四边形的角点坐标点集Point[3][4],那么npts={4,4,4,} |
ncontours | 多边形的数量 |
isClosed | 多边形是否闭合,如果isClosed为真,那么pts的最后一个点将和第一个点连起来,否则轮廓被认为是不封闭的。 |
color | 绘制线条的颜色 |
thickness | 绘制线条的粗细。若取负值,则表示进行填充 |
lineType | 绘制线条的连通类型 |
shift | 坐标点小数点位数(not important) |
CV_EXPORTS int createTrackbar(const String& trackbarname, const String& winname,
int* value, int count,
TrackbarCallback onChange = 0,
void* userdata = 0);
参数 | 含义 |
---|---|
trackbarname | 滚动条名字 |
winname(window name) | 窗体名字。要先用nameWindow创建好同名窗体,滚动条才会出现 |
value | 欲控制的变量的地址 |
count | 欲控制的变量的最大值(最小为0) |
onChange | 回调函数,默认为空。如果想要传入,那么其 参数是固定的 |
userdata | 万能指针,默认为空。如果想要传入,通常用一个类的对象的地址。作为可使用的数据库,用来给回调函数提供变量支持 |
void onChange(int,void*);
CV_EXPORTS_W int getTrackbarPos(const String& trackbarname, const String& winname);
参数 | 含义 |
---|---|
trackbarname | 滚动条名字 |
winname(window name) | 窗体名字 |
不使用createTrackbar函数的参数value、onChange、userdata参数。通过while(1)的无限循环,在循环中不断地用getTrackbarPos函数动态地获取滚动条的值,然后在循环内部用这些值进行操作。
Mat xuenai = imread("xuenai.jpg");
imshow("xuenai",xuenai);
namedWindow("xuenai_rotate");
Mat xuenai_rotate(xuenai.size(), xuenai.type());
createTrackbar("angle","xuenai_rotate", nullptr,360);
while (1) {
int angle= getTrackbarPos("angle","xuenai_rotate");
Mat M = getRotationMatrix2D(Point2f(xuenai.cols / 2, xuenai.rows / 2), angle, 1);
warpAffine(xuenai, xuenai_rotate, M, xuenai.size());
imshow("xuenai_rotate",xuenai_rotate);
waitKey(20);
}
不使用getTrackbarPos函数,使用createTrackbar的全部参数,在onChange回调函数中完成所有操作,由于回调函数的参数表是固定的,因此**需要userdata传入所需数据。**在每次移动滚动条时,相当于调用了一次回调函数,就完成了操作。结尾没有waitKey(0)就显示不了多久。
class TrackbarUserdata{ public: Mat input; Mat output; int angle=0; string winname; }; void RotateonChange(int,void *userdata) { TrackbarUserdata *data = (TrackbarUserdata *) userdata; int rows = data->input.rows; int cols = data->output.cols; Mat M = getRotationMatrix2D(Point2f(rows / 2, cols / 2), data->angle, 1); warpAffine(data->input,data->output,M,data->input.size()); imshow(data->winname,data->output); waitKey(10); } int main(){ Mat xuenai = imread("xuenai.jpg"); imshow("xuenai",xuenai); Mat xuenai_rotate(xuenai.size(), xuenai.type()); TrackbarUserdata userdata; userdata.input=xuenai; userdata.output=xuenai_rotate; userdata.winname="xuenai_rotate"; namedWindow(userdata.winname); createTrackbar("angle",userdata.winname, &userdata.angle,360, RotateonChange,&userdata); waitKey(); return 0; }
CV_EXPORTS_W void findContours( InputArray image, OutputArrayOfArrays contours,
OutputArray hierarchy, int mode,
int method, Point offset = Point());
/** @overload */
CV_EXPORTS void findContours( InputArray image, OutputArrayOfArrays contours,
int mode, int method, Point offset = Point());
参数 | 含义 |
---|---|
image | 输入图片,数据类型Mat |
contours | 保存输出轮廓的点坐标。通常用vector<vector<Point>>数据类型担任,通过Point可以看出存储的是坐标。 |
hierarchy | 可选参数,保存输出轮廓的层级关系。通常用vector<Vec4i>数据类型担任。 详见下文 |
mode | 轮廓层级的检测模式 ,详见下文 |
method | 轮廓坐标点的储存方式 ,详见下文 |
offset | 额外偏移量,在每一个检测出的轮廓点上加上该偏移量,可以是负值。当所分析图像是另外一个图像的ROI的时候,通过加减这个偏移量,可以把ROI图像的检测结果投影到原始图像对应位置上。 |
enum RetrievalModes {
RETR_EXTERNAL = 0,
RETR_LIST = 1,
RETR_CCOMP = 2,
RETR_TREE = 3,
};
只检测最外围轮廓,包含在外围轮廓内的内围轮廓被忽略
检测所有的轮廓,包括内围、外围轮廓,但是检测到的轮廓不建立层级关系,这就意味着这个检索模式下不存在父轮廓或内嵌轮廓,所以hierarch[i]向量内所有元素的第3、第4个分量都会被置为-1。
检测所有的轮廓,但所有轮廓只建立两个等级关系,外围为顶层,若外围内的内围轮廓还包含了其他的轮廓信息,则内围内的所有轮廓均归属于顶层
检测所有轮廓,所有轮廓建立一个等级树结构。外层轮廓包含内层轮廓,内层轮廓还可以继续包含内嵌轮廓。
enum ContourApproximationModes {
CHAIN_APPROX_NONE = 1,
CHAIN_APPROX_SIMPLE = 2,
CHAIN_APPROX_TC89_L1 = 3,
CHAIN_APPROX_TC89_KCOS = 4
};
method可选值 | 含义 |
---|---|
CHAIN_APPROX_NONE | 保存物体边界上所有连续的轮廓点到contours向量内 |
CHAIN_APPROX_SIMPLE(recommended) | 仅保存轮廓的拐点信息,把所有轮廓拐点处的点保存入contours向量内,拐点与拐点之间直线段上的信息点不予保留,效率比较高。 |
CHAIN_APPROX_TC89_L1或CV_CHAIN_APPROX_TC89_KCOS | 使用tehChinl chain 近似算法(not important) |
CV_EXPORTS_W void drawContours( InputOutputArray image, InputArrayOfArrays contours,
int contourIdx, const Scalar& color,
int thickness = 1, int lineType = LINE_8,
InputArray hierarchy = noArray(),
int maxLevel = INT_MAX, Point offset = Point() );
参数 | 含义 |
---|---|
image | 绘制轮廓的画布,数据类型Mat |
contours | 输入轮廓,数据类型vector<vector<Point>> |
contourIdx(contour index) | 欲绘制的轮廓的索引值,输入-1可以绘制所有轮廓 |
color | 绘制线条的颜色 |
thickness | 绘制线条的粗细。若取负值,则表示进行填充 |
lineType | 绘制线条的连通类型 |
hierarchy | 可选的层次结构信息。它仅在当你需要绘制一些轮廓线时被使用。(详见参数maxLevel)默认为noArray(),返回一个空数组。 |
maxLevel | 绘制轮廓线的最高级别。此参数仅在参数hierarchy有效时被考虑。详见下表 |
offset | 额外偏移量,在每一个绘制出的轮廓点上加上该偏移量,可以是负值。当所分析图像是另外一个图像的ROI的时候,通过加减这个偏移量,可以把ROI图像的绘制结果投影到原始图像对应位置上。 |
maxLevel可选值 | 含义 |
---|---|
0 | 只有被指定的轮廓被绘制 |
1 | 绘制被指定的轮廓和 其下一级轮廓 |
2 | 绘制被指定的轮廓和 其所有子轮廓 |
CV_EXPORTS_W double contourArea( InputArray contour, bool oriented = false );
参数 | 含义 |
---|---|
contour | 某一个轮廓,数据类型vector<Point> |
oriented | 有方向的区域标志(not important)。若为true: 此函数依赖轮廓的方向(顺时针或逆时针)返回一个已标记区域的值。若为false: 默认值,意味着返回不带方向的绝对值。 |
CV_EXPORTS_W double arcLength( InputArray curve, bool closed );
参数 | 含义 |
---|---|
curve | 某一个轮廓,数据类型vector<Point> |
closed | 轮廓是否是闭合的 |
CV_EXPORTS_W void approxPolyDP( InputArray curve,
OutputArray approxCurve,
double epsilon, bool closed );
参数 | 含义 |
---|---|
curve | 某一个轮廓,数据类型vector<Point> |
approxCurve | 输出多边形的点集,数据类型vector<Point> |
epsilon | 设置精度,越小则精度越高,多边形越趋近于曲线,拟合效果更好但效率低。 |
closed | 轮廓是否是闭合的 |
CV_EXPORTS_W void convexHull( InputArray points, OutputArray hull,
bool clockwise = false, bool returnPoints = true );
参数 | 含义 |
---|---|
points | 输入点集,数据类型vector<Point> |
hull | 输出凸包。 数据类型取决于returnPoints,vector<Point>或vector<int> |
clockwise | 拟合凸包的直线的转动方向,TRUE为顺时针,否则为逆时针。 |
returnPoints | 若为true,则在hull中存储点的坐标。若为false,则在hull中存储点的索引,索引值根据参数points得到。默认为true |
CV_EXPORTS_W RotatedRect minAreaRect( InputArray points );
参数 | 含义 |
---|---|
points | 输入点集,数据类型vector<Point> |
CV_EXPORTS_W Rect boundingRect( InputArray array );
参数 | 含义 |
---|---|
points | 输入点集,数据类型vector<Point> |
模板图像在原图像上从原点开始移动,计算模板与原图被模板覆盖的地方的差别程度,计算方法有几种,然后将每次计算的结果放进输出矩阵。若原图像为A*B大小,模板为a*b大小,则 输出矩阵为(A-a+1)*(B-b+1) 大小。
CV_EXPORTS_W void matchTemplate( InputArray image, InputArray templ,
OutputArray result, int method, InputArray mask = noArray() );
参数 | 含义 |
---|---|
image | 输入图像,数据类型Mat |
templ(template) | 模板图像,数据类型Mat |
result | 输出矩阵,深度为CV_32FC1。若原图像为A*B大小,模板为a*b大小,则 输出矩阵为(A-a+1)*(B-b+1) 大小。 |
method | 模板匹配计算方法。详见下文 |
mask | 掩码图像。其大小与模板图像必须相同,且必须为灰度图。匹配时,对于掩码中的非0像素匹配算法起作用,掩码中的灰度值为0的像素位置,匹配算法不起作用。 |
enum TemplateMatchModes {
TM_SQDIFF = 0,
TM_SQDIFF_NORMED = 1,
TM_CCORR = 2,
TM_CCORR_NORMED = 3,
TM_CCOEFF = 4,
TM_CCOEFF_NORMED = 5
};
method可选值 | 含义 |
---|---|
TM_SQDIFF | 计算平方误差,计算出来的值越小,则匹配得越好 |
TM_CCORR | 计算相关性,计算出来的值越大,则匹配得越好 |
TM_CCOEFF | 计算相关系数,计算出来的值越大,则匹配得越好 |
TM_SQDIFF_NORMED | 计算归一化平方误差,计算出来的值越接近0,则匹配得越好 |
TM_CCORR_NORMED | 计算归一化相关性,计算出来的值越接近1,则匹配得越好 |
TM_CCOEFF_NORMED | 计算归一化相关系数,计算出来的值越接近1,则匹配得越好 |
在进行特征匹配时,我们有时并不需要用整个图片作为模板,因为模板的背景可能会干扰匹配的结果。因此,我们需要加入掩码,就可以屏蔽掉背景进行模板匹配
Mat xuenai = imread("xuenai.jpg"); imshow("xuenai",xuenai); Mat templ= imread("xuenai_rect.jpg"); imshow("template",templ); Mat match_result; matchTemplate(xuenai,templ,match_result,TM_SQDIFF); Point temLoc; Point minLoc; Point maxLoc; double min,max; minMaxLoc(match_result,&min,&max,&minLoc,&maxLoc); temLoc=minLoc; rectangle(xuenai,Rect(temLoc.x,temLoc.y,templ.cols,templ.rows),Scalar(0,0,255)); imshow("xuenai_match",xuenai); waitKey();
Mat xuenai = imread("xuenai.jpg"); rotate(xuenai,xuenai,ROTATE_90_CLOCKWISE); imshow("xuenai",xuenai); Mat templ= imread("xuenai_rect.jpg"); Mat match_result; matchTemplate(xuenai,templ,match_result,TM_SQDIFF); Point temLoc; Point minLoc; Point maxLoc; double min,max; minMaxLoc(match_result,&min,&max,&minLoc,&maxLoc); temLoc=minLoc; rectangle(xuenai,Rect(temLoc.x,temLoc.y,templ.cols,templ.rows),Scalar(0,0,255)); imshow("xuenai_match",xuenai); waitKey();
Mat xuenai = imread("xuenai.jpg"); resize(xuenai,xuenai,Size(500,500)); imshow("xuenai",xuenai); Mat templ= imread("xuenai_rect.jpg"); Mat match_result; matchTemplate(xuenai,templ,match_result,TM_SQDIFF); Point temLoc; Point minLoc; Point maxLoc; double min,max; minMaxLoc(match_result,&min,&max,&minLoc,&maxLoc); temLoc=minLoc; rectangle(xuenai,Rect(temLoc.x,temLoc.y,templ.cols,templ.rows),Scalar(0,0,255)); imshow("xuenai_match",xuenai); waitKey();
使用一个固定窗口在图像上进行任意方向上的滑动,比较滑动前与滑动后两种情况,窗口中的像素灰度变化程度,如果存在任意方向上的滑动,都有着较大灰度变化,那么我们可以认为该窗口中存在角点。
考虑到一个灰度图像 . 划动窗口 (with displacements 在x方向和 方向) 计算像素灰度变化。
其中:
为了寻找带角点的窗口,搜索像素灰度变化较大的窗口。于是, 我们期望最大化以下式子:
泰勒展开:
矩阵化:
得二次型:
因此有等式:
每个窗口中计算得到一个值。这个值决定了这个窗口中是否包含了角点。
其中,det(M) = 矩阵M的行列式,trace(M) = 矩阵M的迹
CV_EXPORTS_W void cornerHarris( InputArray src, OutputArray dst, int blockSize,
int ksize, double k,
int borderType = BORDER_DEFAULT );
参数 | 含义 |
---|---|
src(source) | 输入图片 (灰度图),深度要求:CV_8UC1或CV_32FC1 |
dst(destination) | 输出图片,数据类型Mat |
bolckSize | 检测窗口的大小,越大则对角点越敏感,一般取2 |
ksize(kernal size) | 使用sobel算子计算一阶导数时的滤波器大小,一般取3即可。 |
k | 计算用到的系数,公认一般取值在0.02~0.06。 |
borderType | 边界填充方式,默认为黑边。 |
Mat xuenai = imread("xuenai.jpg"); imshow("xuenai", xuenai); //转灰度图 Mat xuenai_gray(xuenai.size(),xuenai.type()); cvtColor(xuenai,xuenai_gray,COLOR_BGR2GRAY); Mat xuenai_harris; cornerHarris(xuenai_gray,xuenai_harris,2,3,0.04); normalize(xuenai_harris,xuenai_harris,0,255,NORM_MINMAX,-1); convertScaleAbs(xuenai_harris,xuenai_harris); namedWindow("xuenai_harris"); createTrackbar("threshold","xuenai_harris", nullptr,255); while (1) { int thres = getTrackbarPos("threshold", "xuenai_harris"); if(thres==0)thres=100; Mat harris_result=xuenai.clone(); for(int i=0;i<xuenai_harris.rows;i++){ uchar * ptr =xuenai_harris.ptr(i); for(int j=0;j<xuenai_harris.cols;j++){ int value=(int) *ptr; if(value>thres){ circle(harris_result, Point(j,i), 3, Scalar(0, 0, 255)); } ptr++; } } imshow("xuenai_harris",harris_result); if (waitKey(0) == 'q')break; }
Mat xuenai = imread("xuenai.jpg"); imshow("xuenai", xuenai); namedWindow("panel"); createTrackbar("threshold","panel", nullptr,255); createTrackbar("angle","panel", nullptr,360); createTrackbar("width","panel", nullptr,1000); createTrackbar("height","panel", nullptr,1000); while (1) { int thres = getTrackbarPos("threshold", "panel"); if(thres==0)thres=100; int width = getTrackbarPos("width", "panel"); if(width==0)width=xuenai.cols; int height = getTrackbarPos("height", "panel"); if(height==0)height=xuenai.rows; int angle = getTrackbarPos("angle","panel"); Mat xuenai_harris, xuenai_transform=xuenai.clone(); resize(xuenai_transform,xuenai_transform,Size(width,height)); Mat M= getRotationMatrix2D(Point2f(xuenai.cols/2,xuenai.rows/2),angle,1); warpAffine(xuenai_transform,xuenai_transform,M,xuenai_transform.size()); Mat xuenai_gray(xuenai.size(),xuenai.type()); cvtColor(xuenai_transform,xuenai_gray,COLOR_BGR2GRAY); cornerHarris(xuenai_gray,xuenai_harris,2,3,0.04); normalize(xuenai_harris,xuenai_harris,0,255,NORM_MINMAX,-1); convertScaleAbs(xuenai_harris,xuenai_harris); Mat harris_result=xuenai_transform.clone(); for(int i=0;i<xuenai_harris.rows;i++){ uchar * ptr =xuenai_harris.ptr(i); for(int j=0;j<xuenai_harris.cols;j++){ int value=(int) *ptr; if(value>thres){ circle(harris_result, Point(j,i), 3, Scalar(0, 0, 255)); } ptr++; } } imshow("xuenai_harris",harris_result); if (waitKey(0) == 'q')break; }
由于cornerHarris角点检的稳定性与k密切相关,而k是个经验值,难以设定最佳值,Shi-Tomasi在这一点上进行了改进
CV_EXPORTS_W void goodFeaturesToTrack( InputArray image, OutputArray corners,
int maxCorners, double qualityLevel, double minDistance,
InputArray mask = noArray(), int blockSize = 3,
bool useHarrisDetector = false, double k = 0.04 );
CV_EXPORTS_W void goodFeaturesToTrack( InputArray image, OutputArray corners,
int maxCorners, double qualityLevel, double minDistance,
InputArray mask, int blockSize,
int gradientSize, bool useHarrisDetector = false,
double k = 0.04 );
参数 | 含义 |
---|---|
image | 输入图片 (灰度图),深度要求:CV_8UC1或CV_32FC1 |
corners | 输出角点的点集,数据类型vector<Point2f> |
maxCorners | 控制输出角点点集的上限个数,即控制corners.size()。输入0则表示不限制上限 |
qualityLevel | 质量系数(小于1.0的正数,一般在0.01-0.1之间),表示可接受角点的最低质量水平。该系数乘以输入图像中最大的角点分数,作为可接受的最小分数;例如,如果输入图像中最大的角点分数值为1500且质量系数为0.01,那么所有角点分数小于15的角都将被忽略。 |
minDistance | 角点之间的最小欧式距离,小于此距离的点将被忽略。 |
mask | 掩码图像。其大小与输入图像必须相同,且必须为灰度图。计算时,对于掩码中的非0像素算法起作用,掩码中的灰度值为0的像素位置,算法不起作用。 |
blockSize | 检测窗口的大小,越大则对角点越敏感。 |
useHarrisDetector | 用于指定角点检测的方法,如果是true则使用Harris角点检测,false则使用Shi Tomasi算法。默认为False。 |
k | 默认为0.04,只有useHarrisDetector参数为true时起作用。 |
Mat xuenai = imread("xuenai.jpg"); imshow("xuenai", xuenai); namedWindow("panel"); createTrackbar("threshold","panel", nullptr,255); createTrackbar("angle","panel", nullptr,360); createTrackbar("width","panel", nullptr,1000); createTrackbar("height","panel", nullptr,1000); while (1) { int thres = getTrackbarPos("threshold", "panel"); if(thres==0)thres=100; int width = getTrackbarPos("width", "panel"); if(width==0)width=xuenai.cols; int height = getTrackbarPos("height", "panel"); if(height==0)height=xuenai.rows; int angle = getTrackbarPos("angle","panel"); Mat xuenai_transform=xuenai.clone(); resize(xuenai_transform,xuenai_transform,Size(width,height)); Mat M= getRotationMatrix2D(Point2f(xuenai.cols/2,xuenai.rows/2),angle,1); warpAffine(xuenai_transform,xuenai_transform,M,xuenai_transform.size()); Mat xuenai_gray(xuenai.size(),xuenai.type()); cvtColor(xuenai_transform,xuenai_gray,COLOR_BGR2GRAY); vector<Point2f>xuenai_cornersSet; goodFeaturesToTrack(xuenai_gray,xuenai_cornersSet,0,0.1,10); for(auto corner:xuenai_cornersSet){ circle(xuenai_transform,corner,3,Scalar(0,0,255)); } imshow("xuenai_corners",xuenai_transform); if (waitKey(0) == 'q')break; }
cornerHarris和Shi-Tomasi都没能保证角点在尺度上的稳定性,因此SIFT和SURF针对这一特点进行了优化。由于其数学原理较为复杂,请自行查阅相关论文和文献,本文不再赘述。
相较于cornerHarris和Shi-Tomasi,SIFT和SURF的优点是显著的,其检测出的角点对旋转、尺度缩放、亮度变化等保持不变性,对视角变换、仿射变化、噪声也保持一定程度的稳定性,是一种非常优秀的局部特征描述算法。
需要注意的是,SIFT和SURF的计算量较为庞大,难以做到实时运算。SIFT和SURF两者相比,SIFT更为精确,SURF更为高效。
CV_WRAP static Ptr<SIFT> SIFT::create(int nfeatures = 0, int nOctaveLayers = 3,
double contrastThreshold = 0.04, double edgeThreshold = 10,
double sigma = 1.6);
CV_WRAP static Ptr<SIFT> SIFT::create(int nfeatures, int nOctaveLayers,
double contrastThreshold, double edgeThreshold,
double sigma, int descriptorType);
CV_WRAP static Ptr<SURF> SURF::create(double hessianThreshold=100,
int nOctaves = 4, int nOctaveLayers = 3,
bool extended = false, bool upright = false);
CV_WRAP virtual void Feature2D::detect( InputArray image,
CV_OUT std::vector<KeyPoint>& keypoints,
InputArray mask=noArray() );
CV_WRAP virtual void Feature2D::detect( InputArrayOfArrays images,
CV_OUT std::vector<std::vector<KeyPoint> >& keypoints,
InputArrayOfArrays masks=noArray() );
参数 | 含义 |
---|---|
image | 输入图像 (灰度图),深度要求:CV_8UC1或CV_32FC1 |
keypoints | 含多个关键点的vector<KeyPoint>。使用detect时作为输出,使用compute时作为输入,使用detectAndCompute时可以作为输入也可以作为输出。 |
mask | 掩码图像。其大小与输入图像必须相同,且必须为灰度图。计算时,对于掩码中的非0像素算法起作用,掩码中的灰度值为0的像素位置,算法不起作用。 |
CV_WRAP virtual void Feature2D::compute( InputArray image,
CV_OUT CV_IN_OUT std::vector<KeyPoint>& keypoints,
OutputArray descriptors );
CV_WRAP virtual void Feature2D::compute( InputArrayOfArrays images,
CV_OUT CV_IN_OUT std::vector<std::vector<KeyPoint> >& keypoints,
OutputArrayOfArrays descriptors );
CV_WRAP virtual void Feature2D::detectAndCompute( InputArray image, InputArray mask,
CV_OUT std::vector<KeyPoint>& keypoints,
OutputArray descriptors,
bool useProvidedKeypoints=false );
参数 | 含义 |
---|---|
image | 输入图片 (灰度图),深度要求:CV_8UC1或CV_32FC1 |
keypoints | 含多个关键点的vector<KeyPoint>。使用detect时作为输出,使用compute时作为输入,使用detectAndCompute时可以作为输入也可以作为输出。 |
descriptors | 描述子,数据类型Mat。在进行特征匹配的时候会用到。 |
useProvidedKeypoints | false时,keypoints作为输出,并根据keypoints算出descriptors。true时,keypoints作为输入,不再进行detect,即不修改keypoints,并根据keypoints算出descriptors。 |
CV_EXPORTS_W void drawKeypoints( InputArray image, const std::vector<KeyPoint>& keypoints, InputOutputArray outImage,
const Scalar& color=Scalar::all(-1), DrawMatchesFlags flags=DrawMatchesFlags::DEFAULT );
enum struct DrawMatchesFlags
{
DEFAULT = 0, //!< Output image matrix will be created (Mat::create),
//!< i.e. existing memory of output image may be reused.
//!< Two source image, matches and single keypoints will be drawn.
//!< For each keypoint only the center point will be drawn (without
//!< the circle around keypoint with keypoint size and orientation).
DRAW_OVER_OUTIMG = 1, //!< Output image matrix will not be created (Mat::create).
//!< Matches will be drawn on existing content of output image.
NOT_DRAW_SINGLE_POINTS = 2, //!< Single keypoints will not be drawn.
DRAW_RICH_KEYPOINTS = 4 //!< For each keypoint the circle around keypoint with keypoint size and
//!< orientation will be drawn.
};
参数 | 含义 |
---|---|
image | 输入图像,数据类型Mat |
keypoints | 含多个关键点的vector<KeyPoint> |
outImage | 输出图像,数据类型Mat |
color | 绘制颜色信息,默认绘制的是随机彩色。 |
flags | 特征点的绘制模式,其实就是设置特征点的那些信息需要绘制,那些不需要绘制。详见下表 |
flags可选值 | 含义 |
---|---|
DrawMatchesFlags::DEFAULT | 只绘制特征点的坐标点,显示在图像上就是一个个小圆点,每个小圆点的圆心坐标都是特征点的坐标。 |
DrawMatchesFlags::DRAW_OVER_OUTIMG | 函数不创建输出的图像,而是直接在输出图像变量空间绘制,要求本身输出图像变量就是一个初始化好了的,size与type都是已经初始化好的变量。 |
DrawMatchesFlags::NOT_DRAW_SINGLE_POINTS | 单点的特征点不被绘制 |
DrawMatchesFlags::DRAW_RICH_KEYPOINTS | 绘制特征点的时候绘制的是一个个带有方向的圆,这种方法同时显示图像的坐标,size和方向,是最能显示特征的一种绘制方式。 |
Mat xuenai = imread("xuenai.jpg"); imshow("xuenai", xuenai); namedWindow("panel"); createTrackbar("threshold","panel", nullptr,255); createTrackbar("angle","panel", nullptr,360); createTrackbar("width","panel", nullptr,1000); createTrackbar("height","panel", nullptr,1000); while (1) { int thres = getTrackbarPos("threshold", "panel"); if(thres==0)thres=100; int width = getTrackbarPos("width", "panel"); if(width==0)width=xuenai.cols; int height = getTrackbarPos("height", "panel"); if(height==0)height=xuenai.rows; int angle = getTrackbarPos("angle","panel"); Mat xuenai_transform=xuenai.clone(); resize(xuenai_transform,xuenai_transform,Size(width,height)); Mat M= getRotationMatrix2D(Point2f(xuenai_transform.cols/2,xuenai_transform.rows/2),angle,1); warpAffine(xuenai_transform,xuenai_transform,M,xuenai_transform.size()); Mat xuenai_gray(xuenai.size(),xuenai.type()); cvtColor(xuenai_transform,xuenai_gray,COLOR_BGR2GRAY); Ptr<SIFT> sift=SIFT::create(); Ptr<SURF> surf=SURF::create(); vector<KeyPoint>xuenai_SiftKp,xuenai_Surfp; sift->detect(xuenai_gray,xuenai_SiftKp); surf->detect(xuenai_gray,xuenai_Surfp); Mat sift_result=xuenai_transform.clone(),surf_result=xuenai_transform.clone(); drawKeypoints(sift_result,xuenai_SiftKp,sift_result,Scalar::all(-1),DrawMatchesFlags::DRAW_RICH_KEYPOINTS); drawKeypoints(surf_result,xuenai_Surfp,surf_result,Scalar::all(-1),DrawMatchesFlags::DRAW_RICH_KEYPOINTS); imshow("sift_result",sift_result); imshow("surf_result",surf_result); if (waitKey(0) == 'q')break; }
前文已经阐述,SIFT和SURF已经做到了角点在旋转和缩放下的稳定性,但是它们还有一个致命的缺陷,就是它们难以做到实时运算,因此,FAST和OBR应运而生了。
从图片中选取一个坐标点P,获取该点的像素值,接下来判定该点是否为特征点.
选取一个以选取点P坐标为圆心的半径等于r的Bresenham圆(一个计算圆的轨迹的离散算法,得到整数级的圆的轨迹点),一般来说,这个圆上有16个点,如下所示
FAST算法虽然很快,但是没有建立关键点的描述子,也就无法进行特征匹配
ORB 是 Oriented Fast and Rotated Brief 的简称,从这个简介就可以看出,OBR算法是基础FAST算法的改进。其中,Fast 和 Brief 分别是特征检测算法和向量创建算法。ORB 首先会从图像中查找特殊区域,称为关键点。关键点即图像中突出的小区域,比如角点,比如它们具有像素值急剧的从浅色变为深色的特征。然后 ORB 会为每个关键点计算相应的特征向量。ORB 算法创建的特征向量只包含 1 和 0,称为二元特征向量。1 和 0 的顺序会根据特定关键点和其周围的像素区域而变化。该向量表示关键点周围的强度模式,因此多个特征向量可以用来识别更大的区域,甚至图像中的特定对象。
关于Brief算法的具体原理本文不再赘述,请自行查阅相关论文和文献。
CV_WRAP static Ptr<FastFeatureDetector> create( int threshold=10,
bool nonmaxSuppression=true,
FastFeatureDetector::DetectorType type=FastFeatureDetector::TYPE_9_16 );
CV_WRAP static Ptr<ORB> create(int nfeatures=500, float scaleFactor=1.2f, int nlevels=8, int edgeThreshold=31,
int firstLevel=0, int WTA_K=2, ORB::ScoreType scoreType=ORB::HARRIS_SCORE, int patchSize=31, int fastThreshold=20);
threshold:进行FAST检测时用到的阈值,阈值越大检测到的角点越少
Mat xuenai = imread("xuenai.jpg"); imshow("xuenai", xuenai); namedWindow("panel"); createTrackbar("threshold","panel", nullptr,255); createTrackbar("angle","panel", nullptr,360); createTrackbar("width","panel", nullptr,1000); createTrackbar("height","panel", nullptr,1000); while (1) { int thres = getTrackbarPos("threshold", "panel"); if(thres==0)thres=100; int width = getTrackbarPos("width", "panel"); if(width==0)width=xuenai.cols; int height = getTrackbarPos("height", "panel"); if(height==0)height=xuenai.rows; int angle = getTrackbarPos("angle","panel"); Mat xuenai_transform=xuenai.clone(); resize(xuenai_transform,xuenai_transform,Size(width,height)); Mat M= getRotationMatrix2D(Point2f(xuenai_transform.cols/2,xuenai_transform.rows/2),angle,1); warpAffine(xuenai_transform,xuenai_transform,M,xuenai_transform.size()); Mat xuenai_gray(xuenai.size(),xuenai.type()); cvtColor(xuenai_transform,xuenai_gray,COLOR_BGR2GRAY); Ptr<FastFeatureDetector>fast=FastFeatureDetector::create(thres); Ptr<ORB>obr=ORB::create(); vector<KeyPoint>xuenai_FastKp,xuenai_ObrKp; fast->detect(xuenai_gray,xuenai_FastKp); obr->detect(xuenai_gray,xuenai_ObrKp); Mat fast_result=xuenai_transform.clone(),obr_result=xuenai_transform.clone(); drawKeypoints(fast_result,xuenai_FastKp,fast_result,Scalar::all(-1),DrawMatchesFlags::DRAW_RICH_KEYPOINTS); drawKeypoints(obr_result,xuenai_ObrKp,obr_result,Scalar::all(-1),DrawMatchesFlags::DRAW_RICH_KEYPOINTS); imshow("fast_result",fast_result); imshow("obr_result",obr_result); if (waitKey(0) == 'q')break; }
fast->detectAndCompute(xuenai_gray,noArray(),fast_kp,fast_des);
前文已经提及,FAST算法不支持描述子的计算
error: (-213:The function/feature is not implemented) in function 'detectAndCompute'
暴力匹配(Brute-force matcher)是最简单的二维特征点匹配方法。对于从两幅图像中提取的两个特征描述符集合,对第一个集合中的每个描述符Ri,从第二个集合中找出与其距离最小的描述符Sj作为匹配点。
暴力匹配显然会导致大量错误的匹配结果,还会出现一配多的情况。通过交叉匹配或设置比较阈值筛选匹配结果的方法可以改进暴力匹配的质量。
CV_WRAP static Ptr<BFMatcher> BFMatcher::create( int normType=NORM_L2, bool crossCheck=false ) ;
CV_WRAP static Ptr<FlannBasedMatcher> create();
enum NormTypes {
NORM_INF = 1,
NORM_L1 = 2,
NORM_L2 = 4,
NORM_L2SQR = 5,
NORM_HAMMING = 6,
NORM_HAMMING2 = 7,
NORM_TYPE_MASK = 7,
NORM_RELATIVE = 8,
NORM_MINMAX = 32
};
参数 | 含义 |
---|---|
normType | 计算距离用到的方法,默认是欧氏距离。详见下表 |
crossCheck | 是否使用交叉验证,默认不使用。 |
normType可选值 | 含义 |
---|---|
NORM_L1 | L1范数,曼哈顿距离 |
NORM_L2 | L2范数,欧氏距离 |
NORM_HAMMING | 汉明距离 |
NORM_HAMMING2 | 汉明距离2,对每2个比特相加处理。 |
CV_WRAP void DescriptorMatcher::match( InputArray queryDescriptors, InputArray trainDescriptors,
CV_OUT std::vector<DMatch>& matches, InputArray mask=noArray() ) const;
参数 | 含义 |
---|---|
queryDescriptors | 描述子的查询点集,数据类型Mat,即参考图像的特征描述符的集合。 |
trainDescriptors | 描述子的训练点集,数据类型Mat,即检测图像的特征描述符的集合。 |
matches | 匹配结果,长度为成功匹配的数量。 |
mask | 掩码图像。其大小与输入图像必须相同,且必须为灰度图。计算时,对于掩码中的非0像素算法起作用,掩码中的灰度值为0的像素位置,算法不起作用。 |
CV_WRAP void DescriptorMatcher::knnMatch( InputArray queryDescriptors, InputArray trainDescriptors,
CV_OUT std::vector<std::vector<DMatch> >& matches, int k,
InputArray mask=noArray(), bool compactResult=false ) const;
参数 | 含义 |
---|---|
queryDescriptors | 描述子的查询点集,数据类型Mat,即参考图像的特征描述符的集合。 |
trainDescriptors | 描述子的训练点集,数据类型Mat,即检测图像的特征描述符的集合。 |
matches | vector<std::vector<DMatch>>类型,对每个特征点返回k个最优的匹配结果 |
k | 返回匹配点的数量 |
mask | 掩码图像。其大小与输入图像必须相同,且必须为灰度图。计算时,对于掩码中的非0像素算法起作用,掩码中的灰度值为0的像素位置,算法不起作用。 |
CV_EXPORTS_W void drawMatches( InputArray img1, const std::vector<KeyPoint>& keypoints1, InputArray img2, const std::vector<KeyPoint>& keypoints2, const std::vector<DMatch>& matches1to2, InputOutputArray outImg, const Scalar& matchColor=Scalar::all(-1), const Scalar& singlePointColor=Scalar::all(-1), const std::vector<char>& matchesMask=std::vector<char>(), DrawMatchesFlags flags=DrawMatchesFlags::DEFAULT ); CV_EXPORTS_W void drawMatches( InputArray img1, const std::vector<KeyPoint>& keypoints1, InputArray img2, const std::vector<KeyPoint>& keypoints2, const std::vector<DMatch>& matches1to2, InputOutputArray outImg, const int matchesThickness, const Scalar& matchColor=Scalar::all(-1), const Scalar& singlePointColor=Scalar::all(-1), const std::vector<char>& matchesMask=std::vector<char>(), DrawMatchesFlags flags=DrawMatchesFlags::DEFAULT ); CV_EXPORTS_AS(drawMatchesKnn) void drawMatches( InputArray img1, const std::vector<KeyPoint>& keypoints1, InputArray img2, const std::vector<KeyPoint>& keypoints2, const std::vector<std::vector<DMatch> >& matches1to2, InputOutputArray outImg, const Scalar& matchColor=Scalar::all(-1), const Scalar& singlePointColor=Scalar::all(-1), const std::vector<std::vector<char> >& matchesMask=std::vector<std::vector<char> >(), DrawMatchesFlags flags=DrawMatchesFlags::DEFAULT );
参数 | 含义 |
---|---|
img1(image1) | 源图像1,数据类型Mat |
keypoints1 | 源图像1的关键点 |
img2(image2) | 源图像2,数据类型Mat |
keypoints2 | 源图像2的关键点 |
matches1to2 | 源图像1的描述子匹配源图像2的描述子的匹配结果 |
outImg(out image) | 输出图像,数据类型Mat |
matchColor | 匹配的颜色(特征点和连线),默认Scalar::all(-1),颜色随机 |
singlePointColor | 单个点的颜色,即未配对的特征点,默认Scalar::all(-1),颜色随机 |
matchesMask | 掩码,决定哪些点将被画出,若为空,则画出所有匹配点 |
flags | 特征点的绘制模式,其实就是设置特征点的那些信息需要绘制,那些不需要绘制。 |
Mat xuenai = imread("xuenai.jpg"); Mat xuenai_rect = imread("xuenai_rect.jpg"); Mat xuenai_rect_gray;cvtColor(xuenai_rect,xuenai_rect_gray,COLOR_BGR2GRAY); imshow("xuenai", xuenai); namedWindow("panel"); createTrackbar("threshold","panel", nullptr,255); createTrackbar("angle","panel", nullptr,360); createTrackbar("width","panel", nullptr,1000); createTrackbar("height","panel", nullptr,1000); while (true) { int thres = getTrackbarPos("threshold", "panel"); if(thres==0)thres=100; int width = getTrackbarPos("width", "panel"); if(width==0)width=xuenai.cols; int height = getTrackbarPos("height", "panel"); if(height==0)height=xuenai.rows; int angle = getTrackbarPos("angle","panel"); Mat xuenai_transform=xuenai.clone(); //调整尺寸 resize(xuenai_transform,xuenai_transform,Size(width,height)); //进行旋转 Mat M= getRotationMatrix2D(Point2f((float )xuenai_transform.cols/2,(float )xuenai_transform.rows/2),angle,1); warpAffine(xuenai_transform,xuenai_transform,M,xuenai_transform.size()); //灰度图 Mat xuenai_gray(xuenai.size(),xuenai.type()); cvtColor(xuenai_transform,xuenai_gray,COLOR_BGR2GRAY); //准备工作 Ptr<ORB>obr=ORB::create(); vector<KeyPoint>xuenai_ObrKp; Mat BFMmatch_result;Mat FLANNmatch_result; vector<KeyPoint>xuenai_rect_ObrKp; Mat xuenai_obr_descriptorsForBF;Mat xuenai_rect_obr_descriptorsForBF;Mat xuenai_obr_descriptorsForFLANN;Mat xuenai_rect_obr_descriptorsForFLANN; vector<vector<DMatch>>xuenai_BFMmatch_results;vector<vector<DMatch>>xuenai_FLANNmatch_results; obr->detectAndCompute(xuenai_gray,noArray(),xuenai_ObrKp,xuenai_obr_descriptorsForBF); obr->detectAndCompute(xuenai_rect_gray,noArray(),xuenai_rect_ObrKp,xuenai_rect_obr_descriptorsForBF); xuenai_obr_descriptorsForBF.convertTo(xuenai_obr_descriptorsForFLANN,CV_32F);//注意这里不能进行原地运算 xuenai_rect_obr_descriptorsForBF.convertTo(xuenai_rect_obr_descriptorsForFLANN,CV_32F);//注意这里不能进行原地运算 //进行匹配 Ptr<BFMatcher>bfm=BFMatcher::create(NORM_HAMMING); Ptr<FlannBasedMatcher>flann=FlannBasedMatcher::create(); bfm->knnMatch(xuenai_rect_obr_descriptorsForBF,xuenai_obr_descriptorsForBF,xuenai_BFMmatch_results,2); flann->knnMatch(xuenai_rect_obr_descriptorsForFLANN,xuenai_obr_descriptorsForFLANN,xuenai_FLANNmatch_results,2); //比率检验 vector<DMatch>goodBFMresult,goodFLANNresult; for(auto match_result:xuenai_BFMmatch_results){ if(match_result.size()>1 && match_result[0].distance<0.7*match_result[1].distance ){ goodBFMresult.push_back(match_result[0]); } } for(auto match_result:xuenai_FLANNmatch_results){ if(match_result.size()>1 && match_result[0].distance<0.7*match_result[1].distance ){ goodFLANNresult.push_back(match_result[0]); } } //绘制匹配结果 if(!goodBFMresult.empty()) { drawMatches(xuenai_rect, xuenai_rect_ObrKp, xuenai_transform, xuenai_ObrKp, goodBFMresult, BFMmatch_result, Scalar::all(-1), Scalar::all(-1), vector<char>(), DrawMatchesFlags::NOT_DRAW_SINGLE_POINTS); imshow("BFMmatch_result",BFMmatch_result); } if(!goodFLANNresult.empty()) { drawMatches(xuenai_rect, xuenai_rect_ObrKp, xuenai_transform, xuenai_ObrKp, goodFLANNresult, FLANNmatch_result, Scalar::all(-1), Scalar::all(-1), vector<char>(), DrawMatchesFlags::NOT_DRAW_SINGLE_POINTS); imshow("FLANNmatch_result",FLANNmatch_result); } if (waitKey(0) == 'q')break; }
使用最小均方误差或者RANSAC方法,计算多个二维点对之间的最优单映射变换矩阵 H(3行x3列),配合perspectivetransform函数,可以实现对图片的矫正
CV_EXPORTS_W Mat findHomography( InputArray srcPoints, InputArray dstPoints,
int method = 0, double ransacReprojThreshold = 3,
OutputArray mask=noArray(), const int maxIters = 2000,
const double confidence = 0.995);
/** @overload */
CV_EXPORTS Mat findHomography( InputArray srcPoints, InputArray dstPoints,
OutputArray mask, int method = 0, double ransacReprojThreshold = 3 );
CV_EXPORTS_W Mat findHomography(InputArray srcPoints, InputArray dstPoints, OutputArray mask,
const UsacParams ¶ms);
参数 | 含义 |
---|---|
srcPoints | 源平面中点的坐标矩阵,可以是CV_32FC2类型,也可以是vector<Point2f>类型 |
dstPoints | 目标平面中点的坐标矩阵,可以是CV_32FC2类型,也可以是vector<Point2f>类型 |
method | 计算单应矩阵所使用的方法。不同的方法对应不同的参数,参考如下表格。 |
ransacReprojThreshold | 将点对视为内点的最大允许重投影错误阈值(仅用于RANSAC和RHO方法)。若srcPoints和dstPoints是以vector<Point2f>为单位的,则该参数通常设置在1到10的范围内,建议选择5 |
mask | 可选输出掩码矩阵。通常由鲁棒算法(RANSAC或LMEDS)设置。 请注意,输入掩码矩阵是不需要设置的。 |
maxIters | RANSAC算法的最大迭代次数,默认值为2000 |
confidence | 可信度值,取值范围为0到1 |
method可选值 | 含义 |
---|---|
0 | 利用所有点的常规方法 |
RANSAC | 基于RANSAC的鲁棒算法 |
LMEDS | 最小中值鲁棒算法 |
RHO | 基于PROSAC的鲁棒算法 |
perspectivetransform函数与warpPerspective函数的区别在于:
CV_EXPORTS_W void perspectiveTransform(InputArray src, OutputArray dst, InputArray m );
Mat xuenai = imread("xuenai.jpg"); Mat xuenai_rect = imread("xuenai_rect.jpg"); Mat xuenai_rect_gray;cvtColor(xuenai_rect,xuenai_rect_gray,COLOR_BGR2GRAY); imshow("xuenai", xuenai); namedWindow("panel"); createTrackbar("threshold","panel", nullptr,255); createTrackbar("angle","panel", nullptr,360); createTrackbar("width","panel", nullptr,1000); createTrackbar("height","panel", nullptr,1000); while (true) { int thres = getTrackbarPos("threshold", "panel"); if(thres==0)thres=100; int width = getTrackbarPos("width", "panel"); if(width==0)width=xuenai.cols; int height = getTrackbarPos("height", "panel"); if(height==0)height=xuenai.rows; int angle = getTrackbarPos("angle","panel"); Mat xuenai_transform=xuenai.clone(); resize(xuenai_transform,xuenai_transform,Size(width,height)); Mat M= getRotationMatrix2D(Point2f((float )xuenai_transform.cols/2,(float )xuenai_transform.rows/2),angle,1); warpAffine(xuenai_transform,xuenai_transform,M,xuenai_transform.size()); Mat xuenai_gray(xuenai.size(),xuenai.type()); cvtColor(xuenai_transform,xuenai_gray,COLOR_BGR2GRAY); //准备工作 Ptr<ORB>obr=ORB::create(); vector<KeyPoint>xuenai_ObrKp; Mat BFMmatch_result; vector<KeyPoint>xuenai_rect_ObrKp; Mat xuenai_obr_descriptors;Mat xuenai_rect_obr_descriptors; vector<vector<DMatch>>xuenai_BFMmatch_results; obr->detectAndCompute(xuenai_gray,noArray(),xuenai_ObrKp,xuenai_obr_descriptors); obr->detectAndCompute(xuenai_rect_gray,noArray(),xuenai_rect_ObrKp,xuenai_rect_obr_descriptors); //进行匹配 Ptr<BFMatcher>bfm=BFMatcher::create(NORM_HAMMING); bfm->knnMatch(xuenai_rect_obr_descriptors,xuenai_obr_descriptors,xuenai_BFMmatch_results,2); //比率检验 vector<DMatch>goodBFMresult; for(auto match_result:xuenai_BFMmatch_results){ if(match_result.size()>1 && match_result[0].distance<0.8*match_result[1].distance ){ goodBFMresult.push_back(match_result[0]); } } //寻找单应性矩阵并绘制 vector<Point2f>srcPoints,dstPoints; for (auto good_one:goodBFMresult){ srcPoints.push_back(xuenai_rect_ObrKp[good_one.queryIdx].pt); dstPoints.push_back(xuenai_ObrKp[good_one.trainIdx].pt); } if (srcPoints.size()>=4){ Mat homo = findHomography(srcPoints, dstPoints, RANSAC,5); vector<Point2f> origin_pts = {Point2f(0, 0), Point2f(0, xuenai_rect.rows - 1), Point2f(xuenai_rect.cols - 1, xuenai_rect.rows - 1), Point2f(xuenai_rect.cols - 1, 0),}; vector<Point2f> result_pts; vector<Point> result_pts_int; perspectiveTransform(origin_pts, result_pts, homo); //Point2f转Point for (auto point: result_pts) { result_pts_int.push_back(Point(point)); } polylines(xuenai_transform, result_pts_int, true, Scalar(0, 0, 255)); } //绘制结果 if(!goodBFMresult.empty()) { drawMatches(xuenai_rect, xuenai_rect_ObrKp, xuenai_transform, xuenai_ObrKp, goodBFMresult, BFMmatch_result, Scalar::all(-1), Scalar::all(-1), vector<char>(), DrawMatchesFlags::NOT_DRAW_SINGLE_POINTS); imshow("FLANNmatch_result",BFMmatch_result); } if (waitKey(0) == 'q')break; }
对于所有看到这儿的朋友们,我很高兴地祝贺你们已经完成了OpenCV的基础知识学习,我可以很明确地说,你们对计算机视觉领域的了解已经超过了绝大多数的人,恭喜你们。本文总计八万二千一百五十三字,三千六百一十二行,是本人第一次撰写的长篇系列教程,多有不当之处,谢谢读者们对我的包容和支持。
然而,纸上得来终觉浅,绝知此事要躬行。本文只是基础知识的讲解,关于OpenCV的实战项目,敬请关注本人的后续文章。
以上。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。