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
img
est (également) chargé comme une image à trois canaux (couleur), car il n'y a pas de spécificateur défini.threshold
etmedianBlur
fonctionnent également sur les images couleur, tandis quebitwise_not
ne 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.