Amis! Il y a deux images d'entrée. Une image d'arrière-plan, une autre image de masque. J'ai besoin de la partie colorée du masque.
Ce dont j'ai besoin: background , masque , image résultat a>
Mais j'obtiens quelque chose de complètement différent: background , masque , image résultat
Mon code est en C #:
//Read files Mat img1 = CvInvoke.Imread(Environment.CurrentDirectory + "\\Test\\All1.jpg"); Mat img = CvInvoke.Imread(Environment.CurrentDirectory + "\\Test\\OriginalMask.jpg"); // Threshold and MedianBlur mask CvInvoke.Threshold(img, img, 0, 255, Emgu.CV.CvEnum.ThresholdType.BinaryInv); CvInvoke.MedianBlur(img, img, 13); // without this conversion, an error appears: (mtype == CV_8U || mtype == CV_8S) && _mask.sameSize(*psrc1) CvInvoke.CvtColor(img, img, Emgu.CV.CvEnum.ColorConversion.Rgb2Gray); CvInvoke.BitwiseNot(img1, img1, img); //Save file img1.Save(Environment.CurrentDirectory + "\\Test\\Result.jpg");
Premier question: comment puis-je obtenir le résultat indiqué dans l'image?
La deuxième question: pourquoi j'obtiens une erreur si je ne convertis pas le masque: (mtype == CV_8U || mtype == CV_8S) && _mask.sameSize (* psrc1)
La troisième question: comment créer un fond transparent au lieu d'un arrière-plan blanc dans l'image finale?
La solution n'a pas besoin d'être en C #. La solution convient à n'importe quel langage de programmation, car la syntaxe OpenCV est à peu près la même. Merci d'avance.
3 Réponses :
J'utiliserai C ++ dans ma réponse, car je suis le plus familier avec celui-ci.
Voici ma suggestion:
// Load background as color image.
cv::Mat background = cv::imread("background.jpg", cv::IMREAD_COLOR);
// Load mask image as grayscale image.
cv::Mat mask = cv::imread("mask.jpg", cv::IMREAD_GRAYSCALE);
// Start time measurement.
auto start = std::chrono::system_clock::now();
// There are some artifacts in the JPG...
cv::threshold(mask, mask, 128, 255, cv::THRESH_BINARY);
// Initialize result image.
cv::Mat result = background.clone().setTo(cv::Scalar(255, 255, 255));
// Copy pixels from background to result image, where pixel in mask is 0.
for (int x = 0; x < background.size().width; x++)
for (int y = 0; y < background.size().height; y++)
if (mask.at<uint8_t>(y, x) == 0)
result.at<cv::Vec3b>(y, x) = background.at<cv::Vec3b>(y, x);
// End time measurement.
auto end = std::chrono::system_clock::now();
// Output duration duration.
std::chrono::duration<double> elapsed_seconds = end - start;
std::cout << elapsed_seconds.count() << "\n";
// Write result.
cv::imwrite("result.png", result);
// Start time measurement.
start = std::chrono::system_clock::now();
// Generate new image with alpha channel.
cv::Mat resultTransparent = cv::Mat(result.size(), CV_8UC4);
// Copy pixels in BGR channels from result to transparent result image.
// Where pixel in mask is not 0, set alpha to 0.
for (int x = 0; x < background.size().width; x++)
{
for (int y = 0; y < background.size().height; y++)
{
resultTransparent.at<cv::Vec4b>(y, x)[0] = result.at<cv::Vec3b>(y, x)[0];
resultTransparent.at<cv::Vec4b>(y, x)[1] = result.at<cv::Vec3b>(y, x)[1];
resultTransparent.at<cv::Vec4b>(y, x)[2] = result.at<cv::Vec3b>(y, x)[2];
if (mask.at<uint8_t>(y, x) != 0)
resultTransparent.at<cv::Vec4b>(y, x)[3] = 0;
else
resultTransparent.at<cv::Vec4b>(y, x)[3] = 255;
}
}
// End time measurement.
end = std::chrono::system_clock::now();
// Output duration duration.
elapsed_seconds = end - start;
std::cout << elapsed_seconds.count() << "\n";
// Write transparent result.
cv::imwrite("resultTransparent.png", resultTransparent);
Résultats dans ces deux sorties (vous ne verra pas la transparence sur la deuxième image ici sur fond blanc StackOverflow):
Merci pour la solution proposée. Dites-moi, combien de temps l'opération entière est-elle effectuée en millisecondes, sans compter le temps de lecture et d'écriture dans le fichier?
J'ai édité ma réponse et ajouté deux mesures de temps à l'aide de chrono, c'est-à-dire que vous devez ajouter #include . En mode de libération, la première opération nécessite environ 12 ms et la seconde environ 39 ms. Mais je doute fortement que ce soient des chiffres significatifs - aucune optimisation utilisée, quel est le système exécutant le code, ....
plutôt interessant. sur mon processeur Intel Core i5-8250U, la première opération prend 0,47 seconde et la deuxième opération prend 1,4 seconde
C'est ce que j'obtiens (à peu près) en mode débogage - vous l'avez vérifié?
je regarde les informations de la console std :: cout << elapsed_seconds.count () << "\ n";
Juste pour ajouter un petit détail à la réponse de HanseHirse :
Si vous ajoutez un flou gaussien au masque (comme vous l'avez fait dans votre question avec CvInvoke.MedianBlur (img, img, 13); ) les bords du masque seront plus lisses et l'image de sortie sera plus jolie lorsqu'elle sera placée au-dessus d'une autre image .
Vous pouvez le faire en définissant simplement le quatrième canal de l'image de sortie directement sur le masque flou.
Donc au lieu de
resultTransparent.at<cv::Vec4b>(y, x)[3] = mask.at<uint8_t>(y, x);
vous pouvez essayer
if (mask.at<uint8_t>(y, x) != 0)
resultTransparent.at<cv::Vec4b>(y, x)[3] = 0;
else
resultTransparent.at<cv::Vec4b>(y, x)[3] = 255;
Vous avez tout à fait raison. J'ai laissé la partie "jolies" à l'auteur de la question. Merci pour votre ajout.
Même résultat en Python, s'il peut vous inspirer:
import cv2
import numpy as np
# Read files
img1 = cv2.imread("All1.jpg",cv2.IMREAD_COLOR);
img = cv2.imread("OriginalMask.jpg",cv2.IMREAD_GRAYSCALE) # loading in grayscale
# Threshold and MedianBlur mask
_, img = cv2.threshold(img, 127, 255, cv2.THRESH_BINARY_INV) # corrected to 127 instead of 0
img = cv2.medianBlur(img, 13)
# fill with white
dest= np.full(np.shape(img1),255,np.uint8)
# Assuming dst and src are of same sizes
# only copy values where the mask has color > 0
dest[img>0] = img1[img>0] # after @T.Kau's suggestion
cv2.imshow('dest',dest)
cv2.waitKey(0)
cv2.destroyAllWindows()
En python, vous pouvez enregistrer quelques lignes en faisant dest [img> 0] = img1 [img> 0]
Concernant votre première et troisième question, veuillez lire ma réponse ci-dessous. Pour la deuxième question: je pense que
imgest (également) chargé comme une image à trois canaux (couleur), car il n'y a pas de spécificateur défini.thresholdetmedianBlurfonctionnent également sur les images couleur, tandis quebitwise_notne fonctionne que sur les images à un seul canal (niveaux de gris). Par conséquent, le message d'erreur s'il n'y a pas de conversion au préalable.