­

使用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函數加入透明通道即可。下面放一個測試結果。

  原圖:

  

  加入透明通道:

  

  可以看到,一些像素變成了全透明,而一些像素是半透明。如果把這個圖貼在其他圖上的話,看的更明顯一點: