目錄
-
- 一 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
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)就是直角坐标系和斜坐标系的統稱。
極坐标,屬于二維坐标系統,創始人是牛頓,主要應用于數學領域。極坐标是指在平面内取一個定點 O O O,叫極點,引一條射線 O x Ox Ox,叫做極軸,再標明一個長度機關和角度的正方向(通常取逆時針方向)。對于平面内任何一點 M M M,用 ρ ρ ρ表示線段 O M OM OM的長度(有時也用 r r r表示), θ θ θ表示從 O x Ox Ox到 O M OM OM的角度, ρ ρ ρ叫做點 M M M的極徑, θ θ θ叫做點 M M M的極角,有序數對$ (ρ,θ) 就 叫 點 就叫點 就叫點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 np
def 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_64F
channels: 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_NN 方法。
INTER_AREA
:立方插值。
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規定的是目前二值化的方式
- 破折線為将被門檻值化的值;虛線為門檻值
-
:大于門檻值的部分被置為255,小于部分被置為0cv2.THRESH_BINARY
-
:大于門檻值部分被置為0,小于部分被置為255cv2.THRESH_BINARY_INV
-
:大于門檻值部分被置為threshold,小于部分保持原樣cv2.THRESH_TRUNC
-
:小于門檻值部分被置為0,大于部分保持不變cv2.THRESH_TOZERO
-
:大于門檻值部分被置為0,小于部分保持不變cv2.THRESH_TOZERO_INV
其實還有很重要的
cv2.THRESH_OTSU
:作為圖像自适應二值化的一個很優的算法 Otsu 大津算法的參數:
使用為:
cv2.threshold(img, 0, 255, cv2.THRESH_OTSU )
3 代碼
import cv2
img1 = 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 plt
img = 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
https://blog.csdn.net/zhinengshiyanshi/article/details/50500429
https://blog.csdn.net/u010996895/article/details/102939519