使用opencv為沒有透明通道的影象加入透明通道
在影象處理中,我們經常需要處理帶透明通道的圖片,比如為圖片或視訊新增水印,為圖片或視訊新增字幕、貼圖等。然而,我們的素材圖片未必總是帶有透明通道。比如,素材的背景本該透明的地方,卻是黑色和白色。有時,我們甚至需要讓素材本身有影象的部分半透明。接下來,我將介紹兩個方法,一種是使用opencv內建方法,另一種是自己寫程式碼,來為影象新增透明通道。文末有第二種方法的效果展示。
1.首先,是opencv中的cvtColor方法。
C++: void cvtColor(InputArray src, OutputArray dst, int code, int dstCn=0 );
引數解釋:
. InputArray src: 輸入影象即要進行顏色空間變換的原影象,可以是Mat類
. OutputArray dst: 輸出影象即進行顏色空間變換後儲存影象,也可以Mat類
. int code: 轉換的程式碼或標識,即在此確定將什麼制式的圖片轉換成什麼制式的圖片,後面會詳細將
. int dstCn = 0: 目標影象通道數,如果取值為0,則由src和code決定
我們可以令code引數為COLOR_BGRABGRA,將影象轉化為帶透明通道的圖片。這裡要注意,加上的透明通道,預設值為255,也就是說,預設將影象轉換為不透明圖。如果需要對影象的透明度進行調整,則還需要另寫程式碼。下面是部分程式碼,來驗證預設值確實為255。
1 std::string path = "E:/140.jpg"; 2 Mat image = cv::imread(image_path); 3 std::cout << "原影象通道數: " << image.channels() << std::endl; 4 cvtColor(image, image, COLOR_BGR2BGRA); 5 std::cout << "轉換後圖像通道數: " << image.channels() << std::endl; 6 for (int i = 0; i < image.rows; i++) { 7 for (int j = 0; j < image.cols; j++) { 8 std::cout<<(int)image.at<Vec4b>(i, j)[3]<<std::endl; 9 } 10 }
得到的輸出為:
程式碼中,image.at可以獲取影象畫素值,而中括號內,0代表B,1代表G,2代表R,3代表A,所以括號中為3。而強制型別轉換為int,則是因為在opencv中,單畫素的型別為uchar,如果直接標準輸出,則會輸出一大堆字元,而不是我們想要的畫素值。如果影象在讀取的時候沒有要求讀取透明通道,或者影象本身沒有透明通道,那麼影象的通道數預設為3,可以簡單地說,這個內建方法,就是為影象的通道陣列多加了一列作為透明通道,這個陣列型別為Mat型別。
2.接下來,是手寫程式碼的方法
由上述說明可知,預設方法所做的就是給影象的通道陣列再加上一列,而這一列所表示的,就是影象每個畫素的透明度。而這個透明度陣列,也是一個Mat型別陣列。
所以,我們可以新建一個Mat型別陣列,陣列大小與影象的解析度一致。這裡,我們還可以以影象的灰度圖作為參考,將影象的每個畫素以灰度值來設定透明度,這樣一來,就實現了影象按畫素值自動的調整每一個畫素點的透明度。建立透明通道的方法如下:
1 //建立透明通道 2 cv::Mat createAlpha(cv::Mat& src) 3 { 4 cv::Mat alpha = cv::Mat::zeros(src.rows, src.cols, CV_8UC1); 5 cv::Mat gray = cv::Mat::zeros(src.rows, src.cols, CV_8UC1); 6 7 //根據灰度建立透明度通道 8 cv::cvtColor(src, gray, cv::COLOR_RGB2GRAY); 9 10 for (int i = 0; i < src.rows; i++) 11 { 12 for (int j = 0; j < src.cols; j++) 13 { 14 //透明度為灰度的兩倍,可自行調整 15 16 alpha.at<uchar>(i, j) = gray.at<uchar>(i, j) * 2; 17 } 18 } 19 20 return alpha; 21 }
這個alpha,就是影象的透明通道。然而,這裡的透明通道僅僅是被建立了出來,並沒有被加入影象中。我們可以使用opencv中的split和merge函式來新增透明通道。其中,split函式作用是分割影象的通道,merge函式則是合併影象的各通道。我們可以先把原影象的各個通道分開,然後再連帶著透明通道合併,就得到了帶透明通道的影象。程式碼如下:
1 int addAlpha(cv::Mat& src, cv::Mat& dst, cv::Mat& alpha) 2 { 3 if (src.channels() == 4) 4 { 5 return -1; 6 } 7 else if (src.channels() == 1) 8 { 9 cv::cvtColor(src, src, cv::COLOR_GRAY2RGB); 10 } 11 12 dst = cv::Mat(src.rows, src.cols, CV_8UC4); 13 14 std::vector<cv::Mat> srcChannels; 15 std::vector<cv::Mat> dstChannels; 16 //分離通道 17 cv::split(src, srcChannels); 18 19 dstChannels.push_back(srcChannels[0]); 20 dstChannels.push_back(srcChannels[1]); 21 dstChannels.push_back(srcChannels[2]); 22 //新增透明度通道 23 dstChannels.push_back(alpha); 24 //合併通道 25 cv::merge(dstChannels, dst); 26 27 return 0; 28 }
再處理的過程中,先呼叫createAlpha函式建立透明通道,再呼叫addAlpha函式加入透明通道即可。下面放一個測試結果。
原圖:
加入透明通道:
可以看到,一些畫素變成了全透明,而一些畫素是半透明。如果把這個圖貼在其他圖上的話,看的更明顯一點: