Opencv c++ Bild mit Maske kopieren



  • Na klar, es war das zweite.

    Kannst du mir vielleicht noch einen keinen Tipp geben?
    Ich versuche ein Transparentes PNG(selbe Größe wie der Hintergrund) über den Hintergrund zu legen. Ich nehme an ich muss das Bild auch wieder Pixel für Pixel auslesen und dann über den Hintergrundbildpixel 😉 legen aber wie mache ich das mit der Transparenz?

    Gruß



  • Das ist doch genau das gleiche und auch von meinem Beispiel abgedeckt. 😉
    http://daiw.de/share/Forum/2013-04-26_cpp/mask.png ist in dem Fall dann halt der Alpha-Kanal vom Bild, dass du drüberlegen willst.
    Falls du die PNG direkt mit Alpha-Kanal als 4-Kanal-Bild im Speicher hast, kannst du dir den vierten Kanal mit cv::split ( http://opencv.willowgarage.com/documentation/cpp/core_operations_on_arrays.html#split ) rausholen.
    Aber Laden mit cv::imread ( http://opencv.willowgarage.com/documentation/cpp/highgui_reading_and_writing_images_and_video.html?highlight=imread#imread ) verwirft den Alpha-Kanal.

    <0 the loaded image will be loaded as-is (note that in the current implementation the alpha channel, if any, is stripped from the output image, e.g. 4-channel RGBA image will be loaded as RGB if flags >= 0 ).



  • Moin Dobi,

    Kann ich meine Maske nicht via.

    Mat alpha_mask2(imread("maske2.png", CV_LOAD_IMAGE_UNCHANGED));

    laden? Dabei sollte sie ja unangetastet bleiben und auch noch den Alpha-Kanal behalten oder liege ich da falsch. Nur mit der Split-funktion stehe ich noch auf dem Kriegsfuß. 😡



  • Für CV_LOAD_IMAGE_UNCHANGED steht doch bestimmt irgendwo ein #define im header, was das auf -1 setzt. Und dann bist du wieder hier:

    <0 the loaded image will be loaded as-is (note that in the current implementation the alpha channel, if any, is stripped from the output image, e.g. 4-channel RGBA image will be loaded as RGB if flags >= 0 ).

    OpenCV kann keine Alpha-Kanäle. Auch nicht einlesen.

    Falls maske2.png ein rgba-Bild ist, mach halt mal

    Mat alpha_mask2(imread("maske2.png", CV_LOAD_IMAGE_UNCHANGED));
    std::cout << alpha_mask2.channels() << "\n";
    

    Das wird dann 3 sein, und nicht 4. Der Alpha-Kanal, der in deiner maske2.png drin ist, ist dann weg. (Wenn da allerdings doch 4 stehen sollte, kannst du das Ding raussplitten und wie im Beispiel oben verwernden.)
    Du kannst höchstens schon mit irgendnem anderen Programm/lib den alpha-Kanal als 1-Kanal-Bild irgendwo ablegen wie ich oben in meinem Beispiel.

    OpenCV ist halt mehr auf computer vision (Bilder zerlegen) und nicht auf solche Malerein (Bilder zusammenbauen) ausgelegt. Für sowas ist lib imagemagick oder CImg eventuell besser geeignet.



  • Ich ziehe alles zurück und behaupte das Gegenteil. 😉
    (Entweder ist die Doku veraltet oder ich lese sie falsch.)

    OpenCV (zumindeste meine Version 4.2, die ich auf dem Rechner hier gerade installiert habe) kann mit CV_LOAD_IMAGE_UNCHANGED *doch* den Alpha-Kanal einer PNG mit laden. Man bekommt dann tatsächlich eine cv::Mat mit 4 channels, und dann kannst du genau das machen, was ich meinte. Hier der code:

    #include <opencv2/opencv.hpp>
    #include <iostream>
    using namespace cv;
    using namespace std;
    int main()
    {
        Mat bg(imread("bg.png", CV_LOAD_IMAGE_COLOR)); // BGR
        Mat img(imread("Png-logo.png", CV_LOAD_IMAGE_UNCHANGED)); // BGRA
        cout << "img.channels(): " << img.channels() << "\n"; // 4, yeah!
    
        // Bild in die 4 Kanaele splitten.
        Mat b, g, r, a;
        vector<Mat> img_channels;
        img_channels.push_back(b);
        img_channels.push_back(g);
        img_channels.push_back(r);
        img_channels.push_back(a);
        split(img, img_channels);
    
    	// Ohne Alpha-Kanal wieder zusammenkleben.
        Mat alpha_mask(img_channels[3]);
        img_channels.pop_back();
        merge(img_channels, img);
    
        // Alpha Kanal auf 3 Kanaele ausbreiten.
        vector<Mat> alpha_channels;
        alpha_channels.push_back(alpha_mask);
        alpha_channels.push_back(alpha_mask);
        alpha_channels.push_back(alpha_mask);
        merge(alpha_channels, alpha_mask);
    
     	// float wie ueblich
        bg.convertTo(bg, CV_32FC3, 1.0/255.0);
        img.convertTo(img, CV_32FC3, 1.0/255.0);
        alpha_mask.convertTo(alpha_mask, CV_32FC3, 1.0/255.0);
    
        Mat dest = img.mul(alpha_mask) + bg.mul(Scalar(1.0, 1.0, 1.0) - alpha_mask);
    
        // zurueck aus der Floatigkeit
        dest.convertTo(dest, CV_8UC3, 255.0);
    
        imwrite("dest.png", dest);
    }
    

    Die Bilder dazu liegen hier: http://daiw.de/share/index.php?dir=Forum%2F2013-04-28_cpp%2F

    Statt die Kanaele (ab Zeile 20) wieder zusammenzumergen koennte man das Bild auch einfach nochmal mit CV_LOAD_IMAGE_COLOR laden. Dann haette man es auch ohne Alpha-Kanal. Aber du willst ja lernen wie split und merge funktioniert. Deshalb habe ich das so gemacht. 😉



  • Danke Dobi, das hat mir geholfen 🙂

    Ich habe ein kleines Problem bei der Umwandlung, ich versuche folgende Berechnung und erhalte diese Fehlermeldung

    Mat C= ((B > A) ? A:B);

    Fehler: »cv::operator>(const cv::Mat&, const cv::Mat&)(((const cv::Mat)(& A)))« konnte nicht von »cv::MatExpr« nach »bool« umgewandelt werden

    😞



  • Da hast du aber Glück, dass ich deinen Edit zufällig noch gesehen hab.

    Was willst du denn da machen?
    C soll A sein wenn B größer ist als A und ansonsten B?
    also sowas wie C=min(A,B)?
    Was soll der operator> denn da von A und B vergleichen? Die Anzahl Zeilen oder Spalten? Die Summe aller Elemente?



  • Hallo Dobi,

    ich habe eine Internet-Seite gefunden auf der Blend-Modies von Photoshop erklärt sind und wollte manche übernehmen.

    #define ChannelBlend_Normal(A,B)     ((uint8)(A))
    #define ChannelBlend_Lighten(A,B)    ((uint8)((B > A) ? B:A))
    #define ChannelBlend_Darken(A,B)     ((uint8)((B > A) ? A:B))
    #define ChannelBlend_Multiply(A,B)   ((uint8)((A * B) / 255))
    #define ChannelBlend_Average(A,B)    ((uint8)((A + B) / 2))
    #define ChannelBlend_Add(A,B)        ((uint8)(min(255, (A + B))))
    #define ChannelBlend_Subtract(A,B)   ((uint8)((A + B < 255) ? 0:(A + B - 255)))
    #define ChannelBlend_Difference(A,B) ((uint8)(abs(A - B)))
    #define ChannelBlend_Negation(A,B)   ((uint8)(255 - abs(255 - A - B)))
    #define ChannelBlend_Screen(A,B)     ((uint8)(255 - (((255 - A) * (255 - B)) >> 8)))
    #define ChannelBlend_Exclusion(A,B)  ((uint8)(A + B - 2 * A * B / 255))
    #define ChannelBlend_Overlay(A,B)    ((uint8)((B < 128) ? (2 * A * B / 255):(255 - 2 * (255 - A) * (255 - B) / 255)))
    #define ChannelBlend_SoftLight(A,B)  ((uint8)((B < 128)?(2*((A>>1)+64))*((float)B/255):(255-(2*(255-((A>>1)+64))*(float)(255-B)/255))))
    #define ChannelBlend_HardLight(A,B)  (ChannelBlend_Overlay(B,A))
    #define ChannelBlend_ColorDodge(A,B) ((uint8)((B == 255) ? B:min(255, ((A << 8 ) / (255 - B)))))
    #define ChannelBlend_ColorBurn(A,B)  ((uint8)((B == 0) ? B:max(0, (255 - ((255 - A) << 8 ) / B))))
    #define ChannelBlend_LinearDodge(A,B)(ChannelBlend_Add(A,B))
    #define ChannelBlend_LinearBurn(A,B) (ChannelBlend_Subtract(A,B))
    #define ChannelBlend_LinearLight(A,B)((uint8)(B < 128)?ChannelBlend_LinearBurn(A,(2 * B)):ChannelBlend_LinearDodge(A,(2 * (B - 128))))
    #define ChannelBlend_VividLight(A,B) ((uint8)(B < 128)?ChannelBlend_ColorBurn(A,(2 * B)):ChannelBlend_ColorDodge(A,(2 * (B - 128))))
    #define ChannelBlend_PinLight(A,B)   ((uint8)(B < 128)?ChannelBlend_Darken(A,(2 * B)):ChannelBlend_Lighten(A,(2 * (B - 128))))
    #define ChannelBlend_HardMix(A,B)    ((uint8)((ChannelBlend_VividLight(A,B) < 128) ? 0:255))
    #define ChannelBlend_Reflect(A,B)    ((uint8)((B == 255) ? B:min(255, (A * A / (255 - B)))))
    #define ChannelBlend_Glow(A,B)       (ChannelBlend_Reflect(B,A))
    #define ChannelBlend_Phoenix(A,B)    ((uint8)(min(A,B) - max(A,B) + 255))
    #define ChannelBlend_Alpha(A,B,O)    ((uint8)(O * A + (1 - O) * B))
    #define ChannelBlend_AlphaF(A,B,F,O) (ChannelBlend_Alpha(F(A,B),A,O))
    

    Nur Funktionieren die mit True or False Operator "?" nicht 😞



  • Da geht es um Skalare (Pixelwerte). Was sind in deinem code

    Mat C= ((B > A) ? A:B);
    

    A und B denn für Typen? cv::Mat?



  • Hallo,

    genau A und B sind Mat Typen.

    Mat A(imread("Bild1.jpg", CV_LOAD_IMAGE_COLOR));
    Mat B(imread("Bild2.jpg", CV_LOAD_IMAGE_COLOR));

    Gruß



  • Gut, also machst du da jetzt das:

    Mat A(imread("Bild1.jpg", CV_LOAD_IMAGE_COLOR));
    Mat B(imread("Bild2.jpg", CV_LOAD_IMAGE_COLOR));
    Mat C= ((B > A) ? A:B);
    

    Und das ist das gleiche wie das:

    Mat A(imread("Bild1.jpg", CV_LOAD_IMAGE_COLOR));
    Mat B(imread("Bild2.jpg", CV_LOAD_IMAGE_COLOR));
    Mat C = std::min(A, B);
    

    Und das ist das gleiche wie das:

    Mat A(imread("Bild1.jpg", CV_LOAD_IMAGE_COLOR));
    Mat B(imread("Bild2.jpg", CV_LOAD_IMAGE_COLOR));
    Mat C;
    if (B > A)
    {
        C = A;
    }
    else
    {
        C = B;
    }
    

    Du willst das aber pixelweise haben, jedoch verrätst du deinem Compiler das nicht. 😉



  • Hier ist mein Code

    #include "opencv2/imgproc/imgproc.hpp"
    #include "opencv2/highgui/highgui.hpp"
    #include <stdio.h>
    #include <iostream>
    
    using namespace std;
    using namespace cv;
    char window_name[] = "Mein Fenster";
    
     int main( int argc, char** argv )
     {
    
    Mat A(imread("Bild1.jpg", CV_LOAD_IMAGE_COLOR));
    Mat B(imread("Bild2.jpg", CV_LOAD_IMAGE_COLOR));
    
    Mat C;
    if (B > A)
    {
        C = A;
    }
    else
    {
        C = B;
    }
    
    imshow( window_name, C );
    waitKey(0);
    
    return 0;
    }
    

    Wenn ich versuche es zu kompilieren bekomme ich immer folgende Fehlermeldung :

    mul.cpp: In Funktion »int main(int, char**)«:
    mul.cpp:29:10: Fehler: »cv::operator>(const cv::Mat&, const cv::Mat&)(((const cv::Mat)(& A)))« konnte nicht von »cv::MatExpr« nach »bool« umgewandelt werden



  • Ja, schon klar. Was soll der Compiler beispielsweise auch mit dieser Zeile anfangen?

    if (B > A)
    

    Wann ist eine Matrix größer als eine andere?

    Was du willst, sind pixelweise Vergleiche. Die machst du aber gar nicht.



  • Na gut, will ich mal nicht so sein. 😉

    #include <opencv2/opencv.hpp>
    using namespace cv;
    int main()
    {
    	Mat img_a(imread("1.png"));
    	Mat img_b(imread("2.png"));
    
    	// Beide Bilder sollten gleich gross sein.
    	assert(img_a.size() == img_b.size());
    
    	// Beide Bilder sollten vom gleichen Typ sein.
    	assert(img_a.type() == img_b.type());
    
    	// darken blend
    	Mat img_c(cv::min(img_a, img_b));
    
    	// Speichern
    	imwrite("out.png", img_c);
    }
    

    Für dein Channel-Blend-Darken bekommt es also noch so hin. Jenachdem, was du vor hast, musst du aber eh früher oder später lernen, wie man auf die einzelnen Pixel zugreift. Hier der gleiche Algorithmus nur per Hand:

    #include <opencv2/opencv.hpp>
    using namespace cv;
    int main()
    {
    	Mat img_a(imread("1.png", CV_LOAD_IMAGE_GRAYSCALE));
    	Mat img_b(imread("2.png", CV_LOAD_IMAGE_GRAYSCALE));
    
    	// Beide Bilder sollten gleich gross sein.
    	assert(img_a.size() == img_b.size());
    
    	// Beide Bilder sollten vom gleichen Typ sein.
    	assert(img_a.type() == img_b.type());
    
    	// Zielbild anlegen
    	Mat img_c(img_a.size(), img_a.type());
    
    	// darken blend
    	typedef unsigned char byte;
    	for (int y = 0; y < img_c.rows; ++y)
    	{
    		for (int x = 0; x < img_c.cols; ++x)
    		{
    			byte a = img_a.at<byte>(y, x);
    			byte b = img_b.at<byte>(y, x);
    			img_c.at<byte>(y, x) = b > a ? a : b; // img_c.at<byte>(y, x) = std::min(a, b)
    		}
    	}	
    
    	// Speichern
    	imwrite("out.png", img_c);
    }
    

    Hier die Bilder dazu: http://daiw.de/share/index.php?dir=Forum%2F2013-05-03_cpp%2F

    Was hast du eigentlich generell vor? Ich frage, weil das, was du da tust, hat ja erstmal wenig mit Computer Vision zu tun.



  • Hallo Dobi,

    vielen Dank für deinen Post, schon habe ich wieder was gelernt, ich versuche generell immer noch meine Filter die ich bisher in Imagemagick vorliegen habe mit opencv zu realisieren im Moment laufen die Filter die ich fertig habe mit ca. 0.08 ms bis 0.5ms. Imagemagick im Gegensatz dazu mit 1.5s bis 3s und das obwohl Imagemgick mit mehreren CPUś arbeitet.

    Gruß



  • Das ist interessant. Normalerweise würde man ja erwarten, dass Imagemagick schon ziemlich durchoptimiert ist. Kann es sein, dass du bei deiner Version nur die Filterei an sich mit deiner Stopuhr misst und bei Imagemagick alles, also Programm starten, Bilder laden, Filtern und Ergebnis speichern?



  • Ich mache mit Imagemagick die selben Arbeitsschritte wie mit opencv, sprich Bild laden, Maske laden, Maske an Bildgröße anpassen, Maske mit Bild verrechnen und Bild anschließend speichern.

    Ich messe die Zeit mit dem "time" Befehl

    loonix@ubuntu:~/opencv-2.4.5/test$ time ./mul

    real 0m0.041s
    user 0m0.036s
    sys 0m0.004s

    Ich habe mal deinen zweiten Programmcode kopiert und bei mir eingefügt.
    Wenn ich den Modus der beiden Bilder von GRAYSCALE zu COLOR ändere und das ganz ausführe, wird zwar die Ausgabedatei erstellt aber das Bild ist nur zu einem drittel vorhanden der Rest ist Schwarz. Wo dran kann das liegen?

    Gruß



  • Du misst also die Zeit, die der Programmstart usw. dauert, mit. Das ist ungefähr so als wollten wir beide gucken, wer von uns beiden im 100m-Sprint schneller ist, aber die Zeitmessung incl. Zug-Anreise zum Sportplatz, umziehen, aufwärmen, hinterher duschen und wieder nach Hause fahren messen. 😉

    Ja, der von vom gepostete Code funktioniert nur für Bilder vom TYP CV_8UC1, weil ich

    cv::Mat::at<unsigned char>(y, x)
    

    benutze. Bei Farbbildern (meistens CV_8UC3) ist jeder Pixel aber 3 Byte groß. Du brauchst also entweder noch eine weitere Verschachtelung in den Schleifen in der Art von

    for (int c = 0; c < img_c.channels(); ++c)
    

    durch die Kanäle doppelst. Oder du liest die Pixel als cv::vec3b statt als unsigned char aus.

    Schau dir mal die beiden Tutorials hier an. Da wird erklärt, wie Bilder im Speicher liegen usw:
    Mat - The Basic Image Container
    How to scan images, lookup tables and time measurement with OpenCV


  • Mod

    Dobi schrieb:

    Du misst also die Zeit, die der Programmstart usw. dauert, mit. Das ist ungefähr so als wollten wir beide gucken, wer von uns beiden im 100m-Sprint schneller ist, aber die Zeitmessung incl. Zug-Anreise zum Sportplatz, umziehen, aufwärmen, hinterher duschen und wieder nach Hause fahren messen. 😉

    real world metapher par excellence 👍 😃



  • Guter Vergleich ;-).

    okay habe es hinbekommen :-).

    Danke


Anmelden zum Antworten