目录
-
- 一 OpenCV安装
-
- 1 windows
- 2 Ubuntu
- 二 笛卡尔坐标系和极坐标系
- 三 图像处理流程
-
- 1 低通滤波
- 2 图像细化
- 3 图像锐化
- 4 图像平滑
- 5 图像二值化
- 6 图像分割算法
- 7 文档矫正
- 四 imread函数
-
- 1 imread()的用法
- 2 打开中文名称图片
- 3 关于 cv2.imread() 读取图像为BGR问题
- 五 shape
- 六 IplImage, CvMat, Mat 的关系
-
- 1 IplImage
- 2 CvMat
- 3 Mat
- 4 CvMat, Mat, IplImage之间的互相转换
- 七 确定 OpenCV 矩阵元素的数据类型
- 八 cvtColor和cvCvtColor
- 九 png图片和jpeg图片
- 十 Python Pillow (PIL) Image.save 保存为jpg图片压缩问题
- 十一 图像的缩放-resize()
- 十二 Opencv之图像固定阈值二值化处理threshold
-
- 2 threshold
- 3 代码
- 十三 图片裁剪
-
- 1 opencv Rect()函数介绍
- 2 截取
- 十四 sobel边缘检测
- 十五 插值技术
- 十六 直线检测
- 十七 角点检测
- 十八 曲线检测
一 OpenCV安装
1 windows
VS2017配置opencv教程(超详细!!!):https://blog.csdn.net/qq_41175905/article/details/80560429
【详细步骤】VS2019配置opencv4.1.0(opencv3以上适用):https://blog.csdn.net/qq_43249043/article/details/95337180
2 Ubuntu
1 安装opencv
开始之前进行必要的更新工作:sudo apt-get update
;
安装opencv:
sudo apt-get install libcv-dev
sudo apt-get install libopencv-dev
安装过程比较缓慢,请耐心等待。安装完毕之后,opencv相关的头文件被安装到/usr/lib文件夹中,该文件夹是linux默认头文件查找路径。
opencv的相关动态链接库被安装到/usr/lib文件夹中。这些动态链接库包含:
ocr_cpp_infer【opencv_calib3d】——相机校准和三维重建
ocr_cpp_infer【opencv_core】——核心模块,画图和其它辅助功能
ocr_cpp_infer【opencv_features2d】——二维特征检測
ocr_cpp_infer【opencv_flann】——高速最邻近搜索
ocr_cpp_infer【opencv_highgui】——GUI用户界面
ocr_cpp_infer【opencv_imgproc】——图像处理
ocr_cpp_infer【opencv_legacy】——废弃部分
ocr_cpp_infer【opencv_ml】——机器学习模块
ocr_cpp_infer【opencv_objdetect】——目标检測模块
ocr_cpp_infer【opencv_ocl】——运用OpenCL加速的计算机视觉组件模块
ocr_cpp_infer【opencv_video】——视频分析组件
2 简单演示样例
【C++】——通过代码加载一张图片,通过opencv把彩色图片转换为黑白图片,并把原图和转换后的图片输出到屏幕中。
#include <opencv2/core/core.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <iostream>
using namespace cv;
using namespace std;int main (int argc, char **argv)
{Mat image, image_gray;image = imread(argv[1], CV_LOAD_IMAGE_COLOR );if (argc != 2 || !image.data) {cout << "No image data\n";return -1;}cvtColor(image, image_gray, CV_RGB2GRAY);namedWindow("image", CV_WINDOW_AUTOSIZE);namedWindow("image gray", CV_WINDOW_AUTOSIZE);imshow("image", image);imshow("image gray", image_gray);waitKey(0);return 0;
}
3 编译
g++ main.cpp -o main `pkg-config --cflags --libs opencv`
二 笛卡尔坐标系和极坐标系
笛卡尔坐标系(Cartesian coordinates,法语:les coordonnées cartésiennes)就是直角坐标系和斜坐标系的统称。
极坐标,属于二维坐标系统,创始人是牛顿,主要应用于数学领域。极坐标是指在平面内取一个定点OOO,叫极点,引一条射线OxOxOx,叫做极轴,再选定一个长度单位和角度的正方向(通常取逆时针方向)。对于平面内任何一点MMM,用ρρρ表示线段OMOMOM的长度(有时也用rrr表示),θθθ表示从OxOxOx到OMOMOM的角度,ρρρ叫做点MMM的极径,θθθ叫做点MMM的极角,有序数对$ (ρ,θ)就叫点就叫点就叫点M的极坐标,这样建立的坐标系叫做极坐标系。通常情况下,的极坐标,这样建立的坐标系叫做极坐标系。通常情况下,的极坐标,这样建立的坐标系叫做极坐标系。通常情况下,M$的极径坐标单位为1(长度单位),极角坐标单位为rad(或°)。
三 图像处理流程
1 低通滤波
图像处理之低通滤波:https://blog.csdn.net/weixin_38570251/article/details/82054106
2 图像细化
图像细化(Image Thinning),一般指二值图像的骨架化(Image Skeletonization) 的一种操作运算。
细化是将图像的线条从多像素宽度减少到单位像素宽度过程的简称,一些文章经常将细化结果描述为“骨架化”、“中轴转换”和“对称轴转换”。
OpenCV学习(13) 细化算法(1):https://www.cnblogs.com/mikewolf2002/p/3321732.html
OpenCV学习(14) 细化算法(2):https://www.cnblogs.com/mikewolf2002/p/3322108.html
OpenCV学习(15) 细化算法(3):https://www.cnblogs.com/mikewolf2002/p/3327183.html
OpenCV学习(16) 细化算法(4):https://www.cnblogs.com/mikewolf2002/p/3327318.html
OpenCV学习(17) 细化算法(5):https://www.cnblogs.com/mikewolf2002/p/3329686.html
OpenCV学习(18) 细化算法(6):https://www.cnblogs.com/mikewolf2002/p/3329905.html
OpenCV学习(19) 细化算法(7):https://www.cnblogs.com/mikewolf2002/p/3329918.html
3 图像锐化
图像锐化(image sharpening)是补偿图像的轮廓,增强图像的边缘及灰度跳变的部分,使图像变得清晰,分为空间域处理和频域处理两类。图像锐化是为了突出图像上地物的边缘、轮廓,或某些线性目标要素的特征。这种滤波方法提高了地物边缘与周围像元之间的反差,因此也被称为边缘增强。
4 图像平滑
图像处理与matlab实例之图像平滑(一):https://www.cnblogs.com/luyaoblog/p/7160948.html
图像平滑处理:http://www.opencv.org.cn/opencvdoc/2.3.2/html/doc/tutorials/imgproc/gausian_median_blur_bilateral_filter/gausian_median_blur_bilateral_filter.html
5 图像二值化
定义:图像的二值化,就是将图像上的像素点的灰度值设置为0或255,也就是将整个图像呈现出明显的只有黑和白的视觉效果。
灰度值0:黑,灰度值255:白
一幅图像包括目标物体、背景还有噪声,要想从多值的数字图像中直接提取出目标物体,常用的方法就是设定一个阈值T,用T将图像的数据分成两部分:大于T的像素群和小于T的像素群。这是研究灰度变换的最特殊的方法,称为图像的二值化(Binarization)。
6 图像分割算法
最全综述 | 图像分割算法:https://zhuanlan.zhihu.com/p/70758906
7 文档矫正
从零开始学习「张氏相机标定法」(一)相机成像模型:https://zhuanlan.zhihu.com/p/35223115
四 imread函数
1 imread()的用法
对于 imread()
函数来说,它的参数有两个:
1、图片的相对路径或者绝对路径;2、flag的取值有三个,分别是0(总是返回一个灰度图),1(返回3通道彩色图),-1(返回原图(带alpha 通道:Alpha通道是计算机图形学中的术语,指的是特别的通道,意思是“非彩色”通道))。
2 打开中文名称图片
在windows下,OpenCV3 的 imread()
无法读取中文名称的图片。解决如下:
import cv2
import numpy as npdef cv_imread(image_path):cv_img = cv2.imdecode(np.fromfile(image_path, dtype=np.uint8), -1)return cv_img
同样,imwrite()
无法写入中文名称的图片。解决如下:
def cv_imwrite(write_path, img):cv2.imencode('.jpg', img,)[1].tofile(write_path)
3 关于 cv2.imread() 读取图像为BGR问题
本节内容来自这里。
opencv读取图像为b,g,r方法,比如:
img = cv2.imread("xx.jpg")
cv2.imshow("xx",img)
展示的结果是正常的:
但是此时读取到的img已经为bgr方式了,如果我们再用其他使用rgb方式读取的函数进行读取时就会出错,比如我用plt对图像进行显示,效果如下:
因为plt函数是rgb方式读取的,所以会出错。这时我们可以手动改变img的通道顺序,如下:
b,g,r = cv2.split(img)
img_rgb = cv2.merge([r,g,b])
plt.figure()
plt.imshow(img_rgb)
plt.show()
这时img_rgb就是rgb顺序的了。那么这时再用 cv2.imshow()
显示出来,rgb错误:
五 shape
image.shape[0]#图片垂直尺寸
image.shape[1]#图片水平尺寸
image.shape[2]#图片通道数## 遍历
for x in image.shape[0]:for y in image.shape[1]:print(image[x, y])
六 IplImage, CvMat, Mat 的关系
本节内容来自这里。
opencv 中常见的与图像操作有关的数据容器有 Mat,cvMat 和 IplImage,这三种类型都可以代表和显示图像,但是,Mat 类型侧重于计算,数学性较高,openCV 对 Mat 类型的计算也进行了优化。而 CvMat 和 IplImage 类型更侧重于“图像”,opencv 对其中的图像操作(缩放、单通道提取、图像阈值操作等)进行了优化。在 opencv2.0 之前,opencv 是完全用 C 实现的,但是,IplImage 类型与 CvMat 类型的关系类似于面向对象中的继承关系。实际上,CvMat 之上还有一个更抽象的基类:CvArr,这在源代码中会常见。
1 IplImage
本节内容来自这里。
opencv中的图像信息头,该结构体定义:
typedef struct _IplImage
{ int nSize; /* IplImage大小 */int ID; /* 版本 (=0)*/int nChannels; /* 大多数OPENCV函数支持1,2,3 或 4 个通道 */ int alphaChannel; /* 被OpenCV忽略 */ int depth; /* 像素的位深度: IPL_DEPTH_8U, IPL_DEPTH_8S, IPL_DEPTH_16U, IPL_DEPTH_16S, IPL_DEPTH_32S, IPL_DEPTH_32F and IPL_DEPTH_64F 可支持 */ char colorModel[4]; /* 被OpenCV忽略 */ char channelSeq[4]; /* 被OpenCV忽略 */ int dataOrder; /* 0 - 交叉存取颜色通道, 1 - 分开的颜色通道。 cvCreateImage只能创建交叉存取图像 */ int origin; /* 0 - 顶—左结构,1 - 底—左结构 (Windows bitmaps 风格) */ int align; /* 图像行排列 (4 or 8)。 OpenCV 忽略它,使用 widthStep 代替 */ int width; /* 图像宽像素数 */ int height; /* 图像高像素数*/ struct _IplROI *roi; /* 图像感兴趣区域。 当该值非空只对该区域进行处理 */ struct _IplImage *maskROI; /* 在 OpenCV中必须置NULL */ void *imageId; /* 同上*/ struct _IplTileInfo *tileInfo; /*同上*/ int imageSize; /* 图像数据大小(在交叉存取格式下imageSize=image->height*image->widthStep),单位字节*/ char *imageData; /* 指向排列的图像数据 */ int widthStep; /* 排列的图像行大小,以字节为单位 */ int BorderMode[4]; /* 边际结束模式, 被OpenCV忽略 */ int BorderConst[4]; /* 同上 */ char *imageDataOrigin; /* 指针指向一个不同的图像数据结构(不是必须排列的),是为了纠正图像内存分配准备的 */
} IplImage;
分配与释放图像空间:
//分配图像空间
IplImage* cvCreateImage(CvSize size, int depth, int channels);size: cvSize(width,height);depth: IPL_DEPTH_8U, IPL_DEPTH_8S, IPL_DEPTH_16U,IPL_DEPTH_16S, IPL_DEPTH_32S, IPL_DEPTH_32F, IPL_DEPTH_64Fchannels: 1, 2, 3 or 4. //注意数据为交叉存取。彩色图像的数据编排为b0 g0 r0 b1 g1 r1 ...//分配一个单通道字节图像
IplImage* img1=cvCreateImage(cvSize(640,480),IPL_DEPTH_8U,1); //分配一个三通道浮点图像
IplImage* img2=cvCreateImage(cvSize(640,480),IPL_DEPTH_32F,3);//释放图像空间
IplImage* img=cvCreateImage(cvSize(640,480),IPL_DEPTH_8U,1);
cvReleaseImage(&img);//复制图像
IplImage* img1=cvCreateImage(cvSize(640,480),IPL_DEPTH_8U,1);
IplImage* img2;
img2=cvCloneImage(img1);//设定/获取兴趣区域:
void cvSetImageROI(IplImage* image, CvRect rect);
void cvResetImageROI(IplImage* image);
vRect cvGetImageROI(const IplImage* image);//设定/获取兴趣通道:
void cvSetImageCOI(IplImage* image, int coi); // 0=all
int cvGetImageCOI(const IplImage* image);
读取储存图像:
//从文件中载入图像:
IplImage* img=0;
img=cvLoadImage(fileName);
if(!img) printf("Could not load image file: %s/n",fileName);Supported image formats: BMP, DIB, JPEG, JPG, JPE, PNG, PBM, PGM, PPM,SR, RAS, TIFF, TIF
//载入图像默认转为3通道彩色图像。 如果不是,则需加flag:img=cvLoadImage(fileName,flag);//flag: >0 载入图像转为三通道彩色图像=0 载入图像转为单通道灰度图像<0 不转换载入图像(通道数与图像文件相同)。//图像存储为图像文件:
if(!cvSaveImage(outFileName,img)) printf("Could not save: %s/n",outFileName);
//输入文件格式由文件扩展名决定。
存取图像元素:
//假设需要读取在i行j列像点的第k通道。 其中, 行数i的范围为[0, height-1], 列数j的范围为[0, width-1], 通道k的范围为[0, nchannels-1]。
/*间接存取: (比较通用, 但效率低, 可读取任一类型图像数据)*/
//对单通道字节图像:
IplImage* img=cvCreateImage(cvSize(640,480),IPL_DEPTH_8U,1);
CvScalar s;
s=cvGet2D(img,i,j); // get the (i,j) pixel value
printf("intensity=%f/n",s.val[0]);
s.val[0]=111;
cvSet2D(img,i,j,s); // set the (i,j) pixel value//对多通道浮点或字节图像:
IplImage* img=cvCreateImage(cvSize(640,480),IPL_DEPTH_32F,3);
CvScalar s;
s=cvGet2D(img,i,j); // get the (i,j) pixel value
printf("B=%f, G=%f, R=%f/n",s.val[0],s.val[1],s.val[2]);
s.val[0]=111;
s.val[1]=111;
s.val[2]=111;
cvSet2D(img,i,j,s); // set the (i,j) pixel value/*直接存取: (效率高, 但容易出错)*/
//对单通道字节图像:
IplImage* img=cvCreateImage(cvSize(640,480),IPL_DEPTH_8U,1);
((uchar *)(img->imageData + i*img->widthStep))[j]=111;//对多通道字节图像:
IplImage* img=cvCreateImage(cvSize(640,480),IPL_DEPTH_8U,3);
((uchar *)(img->imageData + i*img->widthStep))[j*img->nChannels + 0]=111; // B
((uchar *)(img->imageData + i*img->widthStep))[j*img->nChannels + 1]=112; // G
((uchar *)(img->imageData + i*img->widthStep))[j*img->nChannels + 2]=113; // R//对多通道浮点图像:
IplImage* img=cvCreateImage(cvSize(640,480),IPL_DEPTH_32F,3);
((float *)(img->imageData + i*img->widthStep))[j*img->nChannels + 0]=111; // B
((float *)(img->imageData + i*img->widthStep))[j*img->nChannels + 1]=112; // G
((float *)(img->imageData + i*img->widthStep))[j*img->nChannels + 2]=113; // R/*用指针直接存取 : (在某些情况下简单高效)*/
//对单通道字节图像:
IplImage* img = cvCreateImage(cvSize(640,480),IPL_DEPTH_8U,1);
int height = img->height;
int width = img->width;
int step = img->widthStep/sizeof(uchar);
uchar* data = (uchar *)img->imageData;
data[i*step+j] = 111;//对多通道字节图像:
IplImage* img = cvCreateImage(cvSize(640,480),IPL_DEPTH_8U,3);
int height = img->height;
int width = img->width;
int step = img->widthStep/sizeof(uchar);
int channels = img->nChannels;
uchar* data = (uchar *)img->imageData;
data[i*step+j*channels+k] = 111;//对单通道浮点图像(假设用4字节调整):
IplImage* img = cvCreateImage(cvSize(640,480),IPL_DEPTH_32F,3);
int height = img->height;
int width = img->width;
int step = img->widthStep/sizeof(float);
int channels = img->nChannels;
float * data = (float *)img->imageData;
data[i*step+j*channels+k] = 111;/*使用 c++ wrapper 进行直接存取: (简单高效)*/
//对单/多通道字节图像,多通道浮点图像定义一个 c++ wrapper:
template<class T> class Image
{private:IplImage* imgp;public:Image(IplImage* img=0) {imgp=img;}~Image(){imgp=0;}void operator=(IplImage* img) {imgp=img;}inline T* operator[](const int rowIndx) {return ((T *)(imgp->imageData + rowIndx*imgp->widthStep));}
};typedef struct{unsigned char b,g,r;
} RgbPixel;typedef struct{float b,g,r;
} RgbPixelFloat;typedef Image<RgbPixel> RgbImage;
typedef Image<RgbPixelFloat> RgbImageFloat;
typedef Image<unsigned char> BwImage;
typedef Image<float> BwImageFloat;//单通道字节图像:
IplImage* img=cvCreateImage(cvSize(640,480),IPL_DEPTH_8U,1);
BwImage imgA(img);
imgA[i][j] = 111;//多通道字节图像:
IplImage* img=cvCreateImage(cvSize(640,480),IPL_DEPTH_8U,3);
RgbImage imgA(img);
imgA[i][j].b = 111;
imgA[i][j].g = 111;
imgA[i][j].r = 111;//多通道浮点图像:
IplImage* img=cvCreateImage(cvSize(640,480),IPL_DEPTH_32F,3);
RgbImageFloat imgA(img);
imgA[i][j].b = 111;
imgA[i][j].g = 111;
imgA[i][j].r = 111;
从本质上讲,IplImage是一个CvMat对象,但CvMat中的data和IplImage中的imageData是有区别的,imageData指针是字节类型指针,所以指向的数据是uchar类型的。所以,在图像上进行指针运算时,你可以简单增加widthStep。
下面讲讲另一个变量ROI(感兴趣的区域)的故事,实际上它是一个IPL/IPP结构IplROI的实例。IplROI包含xOffset,yOffset,height,width和coi(感兴趣的通道)成员变量。
ROI的思想是:一旦设定ROI,通常作用于整幅图像的函数只会对ROI所表示的子图像进行操作。若果IplImage变量设置了ROI,则所有的OpenCV函数就会使用该ROI变量。
ROI在实际工作中有很重要的作用,很多情况下,使用它会提高计算机视觉代码的执行速度。如果想设置ROI,使用函数cvSetImageROI(),并为其传递一个 图像指针和矩形。取消ROI,只需要为函数cvResetImageROI(),传递一个图像指针。
2 CvMat
首先,我们需要知道,第一,在OpenCV中没有向量(vector)结构。任何时候需要向量,都只需要一个列矩阵(如果需要一个转置或者共轭向量,则需要一个行矩阵)。第二,OpenCV矩阵的概念与我们在线性代数课上学习的概念相比,更抽象,尤其是矩阵的元素,并非只能取简单的数值类型,可以是多通道的值。CvMat 的结构:
typedef struct CvMat{ int type;int step; /*用字节表示行数据长度*/int* refcount; /*内部访问*/union {uchar* ptr;short* s;int* i;float* fl;double* db;} data; /*数据指针*/union {int rows;int height;};union {int cols; int width;};
} CvMat; /*矩阵结构头*/
创建CvMat数据:
CvMat * cvCreateMat(int rows, int cols, int type); /*创建矩阵头并分配内存*/
CV_INLine CvMat cvMat((int rows, int cols, int type, void* data CV_DEFAULT); /*用已有数据data初始化矩阵*/
CvMat * cvInitMatHeader(CvMat * mat, int rows, int cols, int type, void * data CV_DEFAULT(NULL), int step CV_DEFAULT(CV_AUTOSTEP)); /*(用已有数据data创建矩阵头)*/
对矩阵数据进行访问:
/*间接访问*/
/*访问CV_32F1和CV_64FC1*/
cvmSet( CvMat* mat, int row, int col, double value);
cvmGet( const CvMat* mat, int row, int col );/*访问多通道或者其他数据类型: scalar的大小为图像的通道值*/
CvScalar cvGet2D(const CvArr * arr, int idx0, int idx1); //CvArr只作为函数的形参void cvSet2D(CvArr* arr, int idx0, int idx1, CvScalar value);/*直接访问: 取决于数组的数据类型*/
/*CV_32FC1*/
CvMat * cvmat = cvCreateMat(4, 4, CV_32FC1);
cvmat->data.fl[row * cvmat->cols + col] = (float)3.0;/*CV_64FC1*/
CvMat * cvmat = cvCreateMat(4, 4, CV_64FC1);
cvmat->data.db[row * cvmat->cols + col] = 3.0;/*一般对于单通道*/
CvMat * cvmat = cvCreateMat(4, 4, CV_64FC1);
CV_MAT_ELEM(*cvmat, double, row, col) = 3.0; /*double是根据数组的数据类型传入,这个宏不能处理多通道*//*一般对于多通道*/
if (CV_MAT_DEPTH(cvmat->type) == CV_32F)CV_MAT_ELEM_CN(*cvmat, float, row, col * CV_MAT_CN(cvmat->type) + ch) = (float)3.0; // ch为通道值
if (CV_MAT_DEPTH(cvmat->type) == CV_64F)CV_MAT_ELEM_CN(*cvmat, double, row, col * CV_MAT_CN(cvmat->type) + ch) = 3.0; // ch为通道值/*多通道数组*/
/*3通道*/
for (int row = 0; row < cvmat->rows; row++)
{ p = cvmat ->data.fl + row * (cvmat->step / 4);for (int col = 0; col < cvmat->cols; col++) { *p = (float) row + col; *(p+1) = (float)row + col + 1; *(p+2) = (float)row + col + 2; p += 3; }
}
/*2通道*/
CvMat * vector = cvCreateMat(1,3, CV_32SC2);CV_MAT_ELEM(*vector, CvPoint, 0, 0) = cvPoint(100,100);
/*4通道*/
CvMat * vector = cvCreateMat(1,3, CV_64FC4);CV_MAT_ELEM(*vector, CvScalar, 0, 0) = CvScalar(0, 0, 0, 0);
复制矩阵操作:
/*复制矩阵*/
CvMat* M1 = cvCreateMat(4,4,CV_32FC1);
CvMat* M2;
M2=cvCloneMat(M1);
3 Mat
Mat是 opencv2.0 推出的处理图像的新的数据结构,现在越来越有趋势取代之前的cvMat和lplImage,相比之下Mat最大的好处就是能够更加方便的进行内存管理,不再需要程序员手动管理内存的释放。opencv2.3 中提到Mat是一个多维的密集数据数组,可以用来处理向量和矩阵、图像、直方图等等常见的多维数据。
class CV_EXPORTS Mat{
public:
/*..很多方法..*/
/*............*/int flags;(Note :目前还不知道flags做什么用的)
int dims; /*数据的维数*/
int rows,cols; /*行和列的数量;数组超过2维时为(-1,-1)*/
uchar *data; /*指向数据*/
int * refcount; /*指针的引用计数器; 阵列指向用户分配的数据时,指针为 NULL
/* 其他成员 */
...};
从以上结构体可以看出Mat也是一个矩阵头,默认不分配内存,只是指向一块内存(注意读写保护)。初始化使用create函数或者Mat构造函数,以下整理自opencv2.3.1 Manual:
Mat(nrows, ncols, type, fillValue]);
M.create(nrows, ncols, type);例子:
Mat M(7,7,CV_32FC2,Scalar(1,3)); /*创建复数矩阵1+3j*/
M.create(100, 60, CV_8UC(15)); /*创建15个通道的8bit的矩阵*//*创建100*100*100的8位数组*/
int sz[] = {100, 100, 100};
Mat bigCube(3, sz, CV_8U, Scalar:all(0));/*现成数组*/
double m[3][3] = {{a, b, c}, {d, e, f}, {g, h, i}};
Mat M = Mat(3, 3, CV_64F, m).inv();/*图像数据*/
Mat img(Size(320,240),CV_8UC3);
Mat img(height, width, CV_8UC3, pixels, step); /*const unsigned char* pixels,int width, int height, int step*//*使用现成图像初始化Mat*/
IplImage* img = cvLoadImage("greatwave.jpg", 1);
Mat mtx(img,0); // convert IplImage* -> Mat; /*不复制数据,只创建一个数据头*/
访问Mat的数据元素:
/*对某行进行访问*/
Mat M;
M.row(3) = M.row(3) + M.row(5) * 3; /*第5行扩大三倍加到第3行*//*对某列进行复制操作*/
Mat M1 = M.col(1);
M.col(7).copyTo(M1); /*第7列复制给第1列*//*对某个元素的访问*/
Mat M;
M.at<double>(i,j); /*double*/
M.at(uchar)(i,j); /*CV_8UC1*/
Vec3i bgr1 = M.at(Vec3b)(i,j) /*CV_8UC3*/
Vec3s bgr2 = M.at(Vec3s)(i,j) /*CV_8SC3*/
Vec3w bgr3 = M.at(Vec3w)(i,j) /*CV_16UC3*//*遍历整个二维数组*/
double sum = 0.0f;
for(int row = 0; row < M.rows; row++)
{ const double * Mi = M.ptr<double>(row); for (int col = 0; col < M.cols; col++) sum += std::max(Mi[j], 0.);
}/*STL iterator*/
double sum=0;
MatConstIterator<double> it = M.begin<double>(), it_end = M.end<double>();
for(; it != it_end; ++it)
sum += std::max(*it, 0.);
Mat可进行Matlab风格的矩阵操作,如初始化的时候可以用initializers,zeros(), ones(), eye()。 除以上内容之外,Mat还有有3个重要的方法:
Mat mat = imread(const String* filename); // 读取图像
imshow(const string frameName, InputArray mat); // 显示图像
imwrite (const string& filename, InputArray img); //储存图像
4 CvMat, Mat, IplImage之间的互相转换
IpIImage -> CvMat
/*cvGetMat*/
CvMat matheader;
CvMat * mat = cvGetMat(img, &matheader);
/*cvConvert*/
CvMat * mat = cvCreateMat(img->height, img->width, CV_64FC3);
cvConvert(img, mat)IplImage -> Mat
Mat::Mat(const IplImage* img, bool copyData=false);/*default copyData=false,与原来的IplImage共享数据,只是创建一个矩阵头*/
例子:
IplImage* iplImg = cvLoadImage("greatwave.jpg", 1);
Mat mtx(iplImg); /* IplImage * -> Mat,共享数据; or : Mat mtx = iplImg;*/Mat -> IplImage
Mat M
IplImage iplimage = M; /*只创建图像头,不复制数据*/CvMat -> Mat
Mat::Mat(const CvMat* m, bool copyData=false); /*类似IplImage -> Mat,可选择是否复制数据*/Mat -> CvMat
例子(假设Mat类型的imgMat图像数据存在):
CvMat cvMat = imgMat;/*Mat -> CvMat, 类似转换到IplImage,不复制数据只创建矩阵头
七 确定 OpenCV 矩阵元素的数据类型
本节内容来自这里。
八 cvtColor和cvCvtColor
1、C++接口:void cvtColor(InputArray src, OutputArray dst, int code, int dstCn=0)
//InputArray:接口类可以是Mat、Mat_<T>、Mat_<T, m, n>、vector<T>、vector<vector<T>>、vector<Mat>
2、C接口:void cvCvtColor(const CvArr* src, CvArr* dst, int code)
//CvArr* src 可以是 iplimage 类型
Mat
:是C++中的一个类,用 Mat 定义变量,要用 cvtColor()
函数来调用。
CvMat
:是C中的一个结构体,用 CvMat 定义的变量,要用 cvCvtColor()
函数来调用。
九 png图片和jpeg图片
png图片是四通道ARGB,jpeg是三通道RGB;
十 Python Pillow (PIL) Image.save 保存为jpg图片压缩问题
本节内容来自这里。
在使用Pillow中的Image.save()方法,使用默认参数保存jpg图片的过程中发现图片被压缩的很严重,导致原来很大的大小变成几十K。这是因为在保存为jpg的过程中,内部使用压缩算法对图片进行的压缩处理。
但是有些时候往往需要图片的大小不能变化太大或不能太小。所以在使用此方式时可以加入参数:
imObj.save(img_name, quality=95)
quality参数: 保存图像的质量,值的范围从1(最差)到95(最佳)。 默认值为75,使用中应尽量避免高于95的值; 100会禁用部分JPEG压缩算法,并导致大文件图像质量几乎没有任何增益。
使用此参数后,图片大小会增加。如果图片的大小还不能满足你的需求,是否还有其他方式去增加图片大小呢?
通过查阅资料并尝试,发现save方法还有一个可以配合quality使用的参数,能够大大增加图片大小:
imObj.save(new_name, quality=95, subsampling=0)
subsampling参数:子采样,通过实现色度信息的分辨率低于亮度信息来对图像进行编码的实践。 (参考:https://en.wikipedia.org/wiki/Chroma_subsampling)
可能的子采样值是0,1和2,对应于4:4:4,4:2:2和4:1:1(或4:2:0?)。
经过实践将值设为0便可以满足图片大小增大的需求。
注意: 以上方法的参数只针对于保存为JPG/JPEG格式的图片的情况。
参考文档:https://pillow.readthedocs.io/en/5.1.x/handbook/image-file-formats.html#jpeg
参考文档:https://pillow.readthedocs.io/en/4.0.x/PIL.html
https://www.2cto.com/kf/201406/306128.html
十一 图像的缩放-resize()
OpenCV图像缩放使用的函数是:resize
void resize(InputArray src, OutputArray dst, Size dsize, double fx=0, double fy=0, int interpolation=INTER_LINEAR)
参数含义:
InputArray src
:原图像;
OutputArray dst
:输出图像;
Size dsize
:输出图像的大小。如果这个参数不为0,那么就代表将原图像缩放到这个Size(width,height)指定的大小;如果这个参数为0,那么原图像缩放之后的大小就要通过下面的公式来计算:dsize = Size(round(fx*src.cols), round(fy*src.rows));
double fx=0
:在x轴上的缩放比例;
double fy=0
:在y轴上的缩放比例;
int interpolation
:插值方式,有以下四种方式:
INTER_NN
:最近邻插值INTER_LINEAR
:双线性插值 (缺省使用)INTER_AREA
:使用象素关系重采样,当图像缩小时候,该方法可以避免波纹出现。当图像放大时,类似于 INTER_NN 方法。INTER_CUBIC
:立方插值。
#include <opencv2\opencv.hpp>
#include <opencv2\imgproc\imgproc.hpp>using namespace cv;int main()
{//读入图像Mat srcImage=imread("1.jpg");Mat temImage,dstImage1,dstImage2;temImage=srcImage;//显示原图imshow("原图",srcImage);//尺寸调整resize(temImage,dstImage1,Size(temImage.cols/2,temImage.rows/2),0,0,INTER_LINEAR);resize(temImage,dstImage2,Size(temImage.cols*2,temImage.rows*2),0,0,INTER_LINEAR);imshow("缩小",dstImage1);imshow("放大",dstImage2);waitKey();return 0;}
说明:
1、dsize与fx和fy必须不能同时为零
2、至于最后的插值方法,正常情况下使用默认的双线性插值就够用了。
几种常用方法的效率是:最邻近插值>双线性插值>双立方插值>Lanczos插值;
但是效率和效果成反比,所以根据自己的情况酌情使用。
十二 Opencv之图像固定阈值二值化处理threshold
本节内容来自这里。
2 threshold
cv2.threshold(img, threshold, maxval, type)
其中:
1、threshold是设定的阈值
2、maxval是当灰度值大于(或小于)阈值时将该灰度值赋成的值
3、type规定的是当前二值化的方式
- 破折线为将被阈值化的值;虚线为阈值
cv2.THRESH_BINARY
:大于阈值的部分被置为255,小于部分被置为0
cv2.THRESH_BINARY_INV
:大于阈值部分被置为0,小于部分被置为255
cv2.THRESH_TRUNC
:大于阈值部分被置为threshold,小于部分保持原样
cv2.THRESH_TOZERO
:小于阈值部分被置为0,大于部分保持不变
cv2.THRESH_TOZERO_INV
:大于阈值部分被置为0,小于部分保持不变
其实还有很重要的 cv2.THRESH_OTSU
:作为图像自适应二值化的一个很优的算法 Otsu 大津算法的参数:
使用为:cv2.threshold(img, 0, 255, cv2.THRESH_OTSU )
3 代码
import cv2img1 = cv2.imread('./Image/cv.jpg', cv2.IMREAD_GRAYSCALE)img1 = cv2.resize(img1, (300, 300), interpolation=cv2.INTER_AREA)
cv2.imshow('img1',img1)ret,binary = cv2.threshold(img1,175,255,cv2.THRESH_BINARY)
ret,binaryinv = cv2.threshold(img1,175,255,cv2.THRESH_BINARY_INV)
ret,trunc = cv2.threshold(img1,175,255,cv2.THRESH_TRUNC)
ret,tozero = cv2.threshold(img1,175,255,cv2.THRESH_TOZERO)
ret,tozeroinv = cv2.threshold(img1,175,255,cv2.THRESH_TOZERO_INV)cv2.imshow('binary',binary)
cv2.imshow('binaryinv',binaryinv)
cv2.imshow('trunc',trunc)
cv2.imshow('tozero',tozero)
cv2.imshow('tozeroinv',tozeroinv)
cv2.waitKey(0)
原图
1、cv2.THRESH_BINARY
2、cv2.THRESH_BINARY_INV
3、cv2.THRESH_TRUNC
4、cv2.THRESH_TOZERO
5、cv2.THRESH_TOZERO_INV
十三 图片裁剪
1 opencv Rect()函数介绍
Rect(int _x,int _y,int _width,int _height);
,参数意思为:左上角x坐标,左上角y坐标,矩形的宽,矩形的高。
一般的用法为:
Rect g_rectangle;
g_rectangle=Rect(a,b,c,d);
2 截取
本节内容来自这里。
本节内容来自这里。
十四 sobel边缘检测
import cv2
import numpy as np
import matplotlib.pyplot as pltimg = cv2.imread('number.jpg',0)
# 其中,0表示将图片以灰度读出来。#### 图像边缘处理sobel细节
sobelx = cv2.Sobel(img, cv2.CV_64F, 1, 0, ksize=3)
# 利用Sobel方法可以进行sobel边缘检测
# img表示源图像,即进行边缘检测的图像
# cv2.CV_64F表示64位浮点数即64float。
# 这里不使用numpy.float64,因为可能会发生溢出现象。用cv的数据则会自动
# 第三和第四个参数分别是对X和Y方向的导数(即dx,dy),对于图像来说就是差分,这里1表示对X求偏导(差分),0表示不对Y求导(差分)。其中,X还可以求2次导。
# 注意:对X求导就是检测X方向上是否有边缘。
# 第五个参数ksize是指核的大小。# 这里说明一下,这个参数的前四个参数都没有给谁赋值,而ksize则是被赋值的对象
# 实际上,这时可省略的参数,而前四个是不可省的参数。注意其中的不同点
# 还有其他参数,有需要的话可以去看,也可留言。sobely = cv2.Sobel(img, cv2.CV_64F, 0, 1, ksize=3)
# 与上面不同的是对y方向进行边缘检测sobelXY = cv2.Sobel(img, cv2.CV_64F, 1, 1, ksize=3)
# 这里对两个方向同时进行检测,则会过滤掉仅仅只是x或者y方向上的边缘##### 图像展示
# 展示上面处理的图片,包括源图像。
# 注意使用subplot和title方法
plt.subplot(2,2,1)
plt.imshow(img,'gray')
# 其中gray表示将图片用灰度的方式显示,注意需要使用引号表示这是string类型。
# 可以用本行命令显示'gray'的类型:print(type('gray'))
plt.title('src')
plt.subplot(2,2,2)
plt.imshow(sobelx,'gray')
plt.title('sobelX')
plt.subplot(2,2,3)
plt.imshow(sobely,'gray')
plt.title('sobelY')
plt.subplot(2,2,4)
plt.imshow(sobelXY,'gray')
plt.title('sobelXY')
plt.show()
https://blog.csdn.net/sunny2038/article/details/9170013
十五 插值技术
插值技术(Interpolate Technology)。是通过数学计算的方式。将两个值之间的部分进行平滑过渡的一种技术方案。
首先我们要明白什么叫做光滑的曲线,可以这么认为,这个曲线是一个运动物体,在时间[0,1]内运动的轨迹。而要求的光滑的曲线,就是要求物体运动过程中没有速度的突变。且要求不同的曲线段之间,速度也不能有突变。据此,我们可以大约知道插值一段曲线,需要知道曲线其实点的位置和速度,结束点的位置和速度。由于有四个已知变量,显然,用一个三次方程来描述这个曲线是再合适不过了。
十六 直线检测
https://blog.csdn.net/wishchin/article/details/83447861
https://blog.csdn.net/qq_19281769/article/details/84820515
https://blog.csdn.net/qq_19281769/category_8497640.html
https://www.cnblogs.com/WhyEngine/p/4020390.html
https://www.cnblogs.com/WhyEngine/p/4020294.html
https://blog.csdn.net/adamshan/article/details/78733302
https://blog.csdn.net/adamshan/category_7227853.html
https://adamshan.blog.csdn.net/article/details/78712120
https://blog.csdn.net/weixin_38746685/article/details/81613065
https://adamshan.blog.csdn.net/article/details/78712120
https://adamshan.blog.csdn.net/article/details/90578382
https://blog.csdn.net/qq_45281711/article/details/112980794
十七 角点检测
https://www.cnblogs.com/skyfsm/p/6899627.html
https://blog.csdn.net/zhu_hongji/article/details/81235643
https://www.cnblogs.com/ssyfj/p/9275368.html
https://blog.csdn.net/zhangquan2015/article/details/76686848
https://blog.csdn.net/piaoxuezhong/article/details/58587907
十八 曲线检测
https://blog.csdn.net/qq826309057/article/details/71328831
八邻域;
#include <iostream>
#include <cstdio>
#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include<opencv2/opencv.hpp>
#include <opencv2\imgproc\types_c.h>
using namespace cv;
using namespace std;/*** @brief 寻找图像曲线上某个点的下一个点* @details 如果坐标点值为255则返回** @param _neighbor_points 相邻的8个坐标点* @param _image 需要检测的图像* @param _inpoint 图像中起始坐标点* @param _flag 8个相邻点的开始下标* @param _outpoint 返回的坐标点* @param _outflag 返回坐标点在相邻8个点中的下标*/
bool findNextPoint(vector<Point> &_neighbor_points, Mat &_image, Point _inpoint, int _flag, Point &_outpoint, int &_outflag)
{int i = _flag;int count = 1;while (count <= 7){Point tmppoint = _inpoint + _neighbor_points[i];if (tmppoint.x > 0 && tmppoint.y > 0 && tmppoint.x < _image.cols && tmppoint.y < _image.rows){if (_image.at<uchar>(tmppoint) == 255){_outpoint = tmppoint;_outflag = i;_image.at<uchar>(tmppoint) = 0; //置为0return true;}}if (count % 2){i += count;if (i > 7){i -= 8;}}else{i -= count;if (i < 0){i += 8;}}count++;}return false;
}/*** @brief 寻找图像上的第一个点* @details 如果坐标点值为255则返回,且置为0** @param _inputimg 需要检测的图像* @param _outputpoint 返回的坐标点*/
bool findFirstPoint(Mat &_inputimg, Point &_outputpoint)
{for (int i = 0; i < _inputimg.rows; i++){uchar* data = _inputimg.ptr<uchar>(i);for (int j = 0; j < _inputimg.cols; j++){if (data[j] == 255){_outputpoint.x = j;_outputpoint.y = i;data[j] = 0;return true;}}}return false;
}/*** @brief 寻找曲线** @param _inputimg 需要检测的图像* @param _outputlines 返回曲线 */
void findLines(Mat &_inputimg, vector<deque<Point>> &_outputlines)
{// 相邻八个坐标点vector<Point> neighbor_points = { Point(-1,-1),Point(0,-1),Point(1,-1),Point(1,0),Point(1,1),Point(0,1),Point(-1,1),Point(-1,0) };Point first_point;while (findFirstPoint(_inputimg, first_point)){/*imshow("draw_img", _inputimg);waitKey(0);*/deque<Point> line;line.push_back(first_point);//由于第一个点不一定是线段的起始位置,双向找Point this_point = first_point;int this_flag = 0;Point next_point;int next_flag;while (findNextPoint(neighbor_points, _inputimg, this_point, this_flag, next_point, next_flag)){line.push_back(next_point);this_point = next_point;this_flag = next_flag;}//找另一边this_point = first_point;this_flag = 0;//cout << "flag:" << this_flag << endl;while (findNextPoint(neighbor_points, _inputimg, this_point, this_flag, next_point, next_flag)){line.push_front(next_point);this_point = next_point;this_flag = next_flag;}if (line.size() > 10){_outputlines.push_back(line);}}
}//随机取色 用于画线的时候
Scalar random_color(RNG &_rng)
{int icolor = (unsigned)_rng;return Scalar(icolor & 0xFF, (icolor >> 8) & 0xFF, (icolor >> 16) & 0xFF);
}int main()
{Mat image = imread("1.jpg");Mat gray;Mat binary_img;cvtColor(image, gray, CV_BGR2GRAY);threshold(gray, binary_img, 240, 255, THRESH_BINARY);//std::cout << gray << std::endl;vector<deque<Point>> lines;findLines(binary_img, lines);cout << lines.size() << endl;//draw linesMat draw_img(image.size(), image.type(), Scalar(0));// = image.clone();RNG rng(123);Scalar color;for (int i = 0; i < lines.size(); i++){color = random_color(rng);for (int j = 0; j < lines[i].size(); j++){draw_img.at<Vec3b>(lines[i][j]) = Vec3b(color[0], color[1], color[2]);}}imshow("draw_img", draw_img);imwrite("draw_img.jpg", draw_img);waitKey(0);return 0;
}