2017-08-10 52 views
4

我正在加載具有透明度平面的PNG圖像。轉換爲灰度時,圖像中的透明區域顯示爲黑色,這似乎是默認背景。我需要他們變成白色。我能做什麼 ?使用透明度設置PNG的背景色

[這不是關於如何保留透明度通常的問題。]

+1

透明平面是否均勻? (即alpha組件可以有多少個值?兩個?)第一個想法:[在白色背景圖像上繪製圖像](https://stackoverflow.com/questions/20957433/how-to-draw-a-transparent- image-over-live-camera-feed-in-opencv#20958245),然後轉換爲灰度。第二個想法:首先轉換爲灰度,然後遍歷每個像素,然後在新圖像中變爲白色所有像素都在原始圖像中具有[alpha分量](https://stackoverflow.com/questions/18491998/creating-transparent-image -in-opencv)低於給定的閾值。 –

+0

@ GabrielDevillers:其實我不知道。我正在讀取PNG並獲取黑色像素的灰度圖像。我沒有看到阿爾法飛機。 –

+0

@YvesDaoust你如何加載圖片?根據你的描述,我猜是灰度。你需要用'IMREAD_UNCHANGED'加載它,以便獲得完整的4通道圖像,並對其進行一些後期處理。 –

回答

0

如果你閱讀imread一個PNG沒有通過IMREAD_UNCHANGED那麼你將有一個3通道BGR圖像。 如果有第四個alpha通道(0 =完全透明,255 =完全可見),那麼會被剪裁作爲文檔put it

由於像素的BGR部分爲黑色,因此您會得到透明像素的黑色像素。 (Vec3b(0, 0, 0))。

如果你不相信,嘗試打開爲BGRimread wihout IMREAD_UNCHANGED參數)和顯示器(imshow然後waitkey以下兩個圖像:

original logo from wikipediaenter image description here

雖然他們看起來這個頁面上相似或者在Gimp中,第一個應該有黑色背景,而第二個應該有紅色背景。

第一個解決方案:使用Michael Jep兒子的overlayImage function

#include <opencv2/highgui/highgui.hpp> 
#include <opencv2/imgcodecs.hpp> 
int main(int argc, char** argv) { 
    cv::Mat img_4_channels; 
    img_4_channels = cv::imread(argv[1], cv::IMREAD_UNCHANGED); // gives 8UC4 
    // img_4_channels = cv::imread(argv[1]); // inappropriate: gives 8UC3 

    cv::Mat background = cv::Mat(img_4_channels.size(), CV_8UC3, cv::Vec3b(255, 255, 255)); // white background 
    overlayImage(background, img_4_channels, img_3_channels, cv::Point2i(0, 0)); 

    cv::imshow("3 channels", img_3_channels); 
} 

解決方法二:this answer羅莎Gronchi

該解決方案更輕巧(沒有前景的座標,無需分配一個背景圖像)。

0

最有效的方法(內存和CPU)是讓libPNG做到這一點,利用png_set_background

如果你不需要,或無法處理,alpha通道,你可以調用 png_set_background()通過與固定的 顏色合成來刪除它。不要調用png_set_strip_alpha()來執行此操作 - 它會在此圖像的透明部分留下 虛假像素值。

png_set_background(png_ptr, &background_color, 
    PNG_BACKGROUND_GAMMA_SCREEN, 0, 1); 

的BACKGROUND_COLOR是根據 數據格式的libpng爲你會產生一個RGB或灰度值。

不幸的是,周圍的libpng OpenCV的包裝不使用它,所以你必須要修補自己的一些初步支持(通過其他選項imread能力有限阻礙)。

其他可能的方法是使用libPNG編寫自己的簡單圖像加載器來實現這個特定目的。

如果您可以承擔一些浪費,請將其作爲BGRA加載,然後執行一些後期處理。不過,我會比Gabriel中提到的代碼更進一步,並將顏色轉換納入其中。

void remove_transparency(cv::Mat const& source 
    , cv::Mat& destination 
    , uint8_t background_color) 
{ 
    CV_Assert(source.type() == CV_8UC4); 

    destination.create(source.rows, source.cols, CV_8UC1); 

    auto it_src(source.begin<cv::Vec4b>()), it_src_end(source.end<cv::Vec4b>()); 
    auto it_dest(destination.begin<uint8_t>()); 

    std::transform(it_src, it_src_end, it_dest 
     , [background_color](cv::Vec4b const& v) -> uchar 
     { 
      // Conversion constants taken from cvtColor docs... 
      float gray(v[0] * 0.114f + v[1] * 0.587f + v[2] * 0.299f); 
      float alpha(v[3]/255.0f); 
      return cv::saturate_cast<uchar>(gray * alpha + background_color * (1 - alpha)); 
     } 
     ); 
} 

當然,這仍然是單線程的,因此讓我們利用cv::parallel_for_到位進一步完善它。

class ParallelRemoveTransparency 
    : public cv::ParallelLoopBody 
{ 
public: 
    ParallelRemoveTransparency(cv::Mat const& source 
     , cv::Mat& destination 
     , uint8_t background_color) 
     : source_(source) 
     , destination_(destination) 
     , background_color_(background_color) 
    { 
     CV_Assert(source.size == destination.size); 
    } 

    virtual void operator()(const cv::Range& range) const 
    { 
     cv::Mat4b roi_src(source_.rowRange(range)); 
     cv::Mat1b roi_dest(destination_.rowRange(range)); 

     std::transform(roi_src.begin(), roi_src.end(), roi_dest.begin() 
      , [this](cv::Vec4b const& v) -> uint8_t { 
       float gray(v[0] * 0.114f + v[1] * 0.587f + v[2] * 0.299f); 
       float alpha(v[3]/255.0f); 
       return cv::saturate_cast<uint8_t>(gray * alpha + background_color_ * (1 - alpha)); 
      } 
      ); 
    } 

private: 
    cv::Mat const& source_; 
    cv::Mat& destination_; 
    uint8_t background_color_; 
}; 

void remove_transparency(cv::Mat const& source 
    , cv::Mat& destination 
    , uint8_t background_color) 
{ 
    CV_Assert(source.type() == CV_8UC4); 

    destination.create(source.rows, source.cols, CV_8UC1); 

    ParallelRemoveTransparency parallel_impl(source, destination, background_color); 
    cv::parallel_for_(cv::Range(0, source.rows), parallel_impl); 
} 

事實證明,你需要這個Python編寫的。下面是一個替代方案的簡要草案:

import numpy as np 
import cv2 

def remove_transparency(source, background_color): 
    source_img = cv2.cvtColor(source[:,:,:3], cv2.COLOR_BGR2GRAY) 
    source_mask = source[:,:,3] * (1/255.0) 

    background_mask = 1.0 - source_mask 

    bg_part = (background_color * (1/255.0)) * (background_mask) 
    source_part = (source_img * (1/255.0)) * (source_mask) 

    return np.uint8(cv2.addWeighted(bg_part, 255.0, source_part, 255.0, 0.0)) 


img = cv2.imread('smile.png', -1) 
result = remove_transparency(img, 255) 

cv2.imshow('', result) 
cv2.waitKey() 
+0

感謝所有有價值的信息。我在Python下工作,所以實現這個有點不可能,所以我正在尋找更多的OpenCVish解決方案。 –

+0

啊,我明白了。問題中沒有語言標記,因此根據您的個人資料,我認爲您需要C++ :) –

+0

@YvesDaoust添加了Python解決方案。不知道你可以輕鬆做得更好。 –