天天看點

圖像處理基礎和OpenCV常用接口

目錄

    • 一 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(或°)。

圖像處理基礎和OpenCV常用接口
圖像處理基礎和OpenCV常用接口

三 圖像處理流程

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)
           

展示的結果是正常的:

圖像處理基礎和OpenCV常用接口

但是此時讀取到的img已經為bgr方式了,如果我們再用其他使用rgb方式讀取的函數進行讀取時就會出錯,比如我用plt對圖像進行顯示,效果如下:

圖像處理基礎和OpenCV常用接口

因為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錯誤:

圖像處理基礎和OpenCV常用接口

五 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_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規定的是目前二值化的方式

  • 破折線為将被門檻值化的值;虛線為門檻值
圖像處理基礎和OpenCV常用接口
  • cv2.THRESH_BINARY

    :大于門檻值的部分被置為255,小于部分被置為0
圖像處理基礎和OpenCV常用接口
  • cv2.THRESH_BINARY_INV

    :大于門檻值部分被置為0,小于部分被置為255
圖像處理基礎和OpenCV常用接口
  • cv2.THRESH_TRUNC

    :大于門檻值部分被置為threshold,小于部分保持原樣
圖像處理基礎和OpenCV常用接口
  • cv2.THRESH_TOZERO

    :小于門檻值部分被置為0,大于部分保持不變
圖像處理基礎和OpenCV常用接口
  • cv2.THRESH_TOZERO_INV

    :大于門檻值部分被置為0,小于部分保持不變
圖像處理基礎和OpenCV常用接口

  其實還有很重要的

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)
           

原圖

圖像處理基礎和OpenCV常用接口

1、cv2.THRESH_BINARY

圖像處理基礎和OpenCV常用接口

2、cv2.THRESH_BINARY_INV

圖像處理基礎和OpenCV常用接口

3、cv2.THRESH_TRUNC

圖像處理基礎和OpenCV常用接口

4、cv2.THRESH_TOZERO

圖像處理基礎和OpenCV常用接口

5、cv2.THRESH_TOZERO_INV

圖像處理基礎和OpenCV常用接口

十三 圖檔裁剪

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()
           
圖像處理基礎和OpenCV常用接口

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

繼續閱讀