1. 程式人生 > >OpenCV中影象的淺拷貝與深拷貝 = copy clone區別

OpenCV中影象的淺拷貝與深拷貝 = copy clone區別

下面介紹三種OpenCV複製影象的方法:

方法1、過載運算子=

使用過載運算子“=”進行的拷貝是一種淺拷貝,雖然它們有不同的矩陣頭,但是二者共享相同的記憶體空間,二者內容相互關聯,任何一個變數變化的同時另一個變數也隨之改變。

/*OpenCV v1版本*/
IplImage img_origin = cvLoadImage(".\\picture.jpg", CV_LOAD_IMAGE_COLOR); // 讀取一張彩色圖 
IplImage img_copy = img_origin;  // 直接賦值,淺拷貝

/*OpenCV v2之後版本*/
Mat img_origin = imread(picture
, IMREAD_COLOR); // 讀取一張彩色圖 Mat img_copy = img_origin;

方法2、cvCopy

cvCopy的原型是:
void cvCopy( const CvArr* src, CvArr* dst, const CvArr* mask CV_DEFAULT(NULL) );
OpenCV官網關於cvCopy函式的介紹
在使用這個函式之前,必須先用cvCreateImage()一類的函式開闢一段記憶體,然後傳遞給dst。cvCopy會把src中的資料複製到dst的記憶體中。這是一種深拷貝,真正地拷貝了一個新的影象矩陣,此時二者相互之間沒有影響,但是如果設定了ROI、COI,copy只會複製ROI、COI區域的內容。

/*OpenCV v1版本*/
IplImage img_origin = cvLoadImage(".\\picture.jpg", CV_LOAD_IMAGE_COLOR); // 讀取一張彩色圖 
IplImage img_copy = cvCreateImage(Size(img_origin->width, img_origin ->height), img_origin ->depth, img_origin ->nChannels); 
// 開闢一個新的記憶體空間,影象的大小、深度與顏色通道與原圖保持一致
cvCopy(img_origin, img_copy);  // 拷貝影象
/*OpenCV v2之後版本*/ Mat img_origin = imread(picture, IMREAD_COLOR); // 讀取一張彩色圖 Mat img_copy; img_origin.copyTo(img_copy); //在拷貝資料前會有一步img_copy.create(this->size , this->type)

方法3、cvCloneImage

cvCloneImage的原型是:
IplImage* cvCloneImage( const IplImage* image );
OpenCV官網關於cvCloneImage函式的介紹
在使用函式之前,不用開闢記憶體。該函式會自己開一段記憶體,然後複製好影象裡面的資料,然後返回這段記憶體中的資料。clone是把所有的都複製過來,不論你是否設定了ROI、COI等影響,clone都會原封不動的克隆過來。用clone複製後,如果源影象在記憶體中消失,複製的影象也變了,而用copy複製,源影象消失後,複製的影象不變。

IplImage img_origin = cvLoadImage(<span class="hljs-string">".\\picture.jpg"</span>, CV_LOAD_IMAGE_COLOR); <span class="hljs-comment">// 讀取一張彩色圖 </span>
IplImage img_copy = cvCloneImage(img_origin);

這裡先學習OpenCV中的一個函式

void flip(InputArray src, OutputArray dst, int flipCode)
//影象變換函式,第三個引數為1時,表示水平反轉,0表示垂直反轉,負數表示既有水平又有垂直反轉。

為介紹OpenCV中的淺拷貝,我們還是從cv::Mat說起吧。cv::Mat類是用於儲存影象以及其他矩陣資料的資料結構。當cv::Mat例項化後,分配記憶體;當物件離開作用域後,分配的記憶體自動釋放。cv::Mat實現了引用計數以及淺拷貝。引用計數的作用是隻有當所有引用記憶體資料的物件都被析構後,記憶體才會釋放。淺拷貝是指當影象之間進行賦值時,影象資料並未發生複製,而是兩個物件都指向同一塊記憶體塊。
通過OpenCV中的flip函式驗證淺拷貝,具體做法:

先宣告一個Mat物件img載入本地圖片,並顯示;
然後宣告一個Mat物件img1,將img淺拷貝到img1;
在img1上垂直翻轉圖片,注意是在原地進行操作,不建立新的影象;
顯示img,注意視窗名稱應與之前不相同,觀察img的影象內容是否改變。

程式如下:

#include<iostream>
#include<opencv2/core/core.hpp>
#include<opencv2/imgproc/imgproc.hpp>
#include<opencv2/highgui/highgui.hpp>

using namespace std;
using namespace cv;

int main()
{
    Mat img1=imread("test.jpg");  //將任意一張名為test.jpg的圖片放置於工程資料夾test中
    Mat img2=img1;                //拷貝方式為淺拷貝
    imshow("First",img1);
    if(!img1.data)
    {
        cout<<"error! The image is not built!"<<endl;
        return -1;
    }
    flip(img2,img2,1);            //注意應在原地進行映象變換
    imshow("Second",img1);
    waitKey();
    return 0;
}

執行如下,很顯然,我們修改img1的內容img發生了改變。
這裡寫圖片描述

深拷貝是指新建立的影象擁有原始影象的嶄新拷貝,即拷貝影象和原始影象在記憶體中存放在不同地方。OpenCV中可以通過下面兩種方式實現深拷貝。

1) img.copyTo(img1)
2) img1=img.clone()

通過OpenCV中的flip函式驗證深拷貝,具體做法與之前相似,將img深拷貝到img1即可。
程式如下:

#include<iostream>
#include<opencv2/core/core.hpp>
#include<opencv2/imgproc/imgproc.hpp>
#include<opencv2/highgui/highgui.hpp>

using namespace std;
using namespace cv;

int main()
{
    Mat img1=imread("test.jpg");  //將任意一張名為test.jpg的圖片放置於工程資料夾test中
    Mat img2=img1.clone();        //拷貝方式是深拷貝
    imshow("First",img1);
    if(!img1.data)
    {
        cout<<"error! The image is not built!"<<endl;
        return -1;
    }
    flip(img2,img2,1);
    imshow("Second",img1);
    waitKey();
    return 0;
}

執行如下,深拷貝之後,任他img1七十二變,img自然不變。
這裡寫圖片描述

瞭解了影象的深淺拷貝,我們使用時就要注意,尤其涉及到類時,我們應該避免返回類中的影象成員。