La façon la plus efficace (mémoire et CPU) serait de laisser libPNG le faire, en utilisant png_set_background
:
Si vous n'avez pas besoin, ou ne peut pas gérer, le canal alpha, vous pouvez appeler png_set_background() pour l'enlever en composant avec une couleur fixe . N'appelez pas png_set_strip_alpha() pour cela - cela laissera fausses valeurs de pixels dans les parties transparentes de cette image.
png_set_background(png_ptr, &background_color,
PNG_BACKGROUND_GAMMA_SCREEN, 0, 1);
Le background_color est une valeur RVB ou en niveaux de gris selon le format des données libpng produira pour vous.
Malheureusement, l'emballage OpenCV autour libpng ne pas utiliser, de sorte que vous auriez à patcher dans un certain soutien rudimentaire vous (entravée par la capacité limitée à passer des options supplémentaires à imread
).
Une autre approche possible consisterait simplement à écrire votre propre chargeur d'image simple en utilisant libPNG dans ce but spécifique.
Si vous pouvez vous permettre un peu de gaspillage, chargez-le en tant que BGRA et effectuez un post-traitement. Cependant, je vais aller plus loin que le code mentionné par Gabriel et incorporer la conversion de couleur en elle.
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));
}
);
}
Bien sûr, cela est encore un seul thread, donc nous allons levier cv::parallel_for_
pour améliorer un peu plus loin.
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);
}
Il se trouve que vous avez besoin de ce en Python. Voici un petit brouillon d'une alternative:
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()
Le plan de transparence est-il uniforme? (c'est-à-dire combien de valeurs le composant alpha peut-il prendre? Deux?) Première idée: [dessiner l'image sur une image de fond blanc] (https://stackoverflow.com/questions/20957433/how-to-draw-a-transparent- image-sur-live-caméra-feed-in-opencv # 20958245) avant de convertir en niveaux de gris. Deuxième idée: convertir en niveaux de gris d'abord, puis itérer sur chaque pixel et transformer en blanc dans la nouvelle image tous les pixels qui ont dans l'image originale leur [composant alpha] (https://stackoverflow.com/questions/18491998/creating-transparent-image -in-opencv) en dessous d'un seuil donné. –
@GabrielDevillers: en fait je ne sais pas. Je lis le PNG et obtient l'image en niveaux de gris avec les pixels noirs. Je ne vois pas le plan alpha. –
@YvesDaoust Comment chargez-vous l'image? Je suppose que les niveaux de gris, en fonction de votre description. Vous devrez le charger avec 'IMREAD_UNCHANGED', de sorte que vous obteniez l'image complète à 4 canaux et que vous effectuiez un post-traitement. –