Laden...

[Anfänger] Frage zu BitmapData? Siehe Erklärung und Bild!

Erstellt von digi333 vor 17 Jahren Letzter Beitrag vor 17 Jahren 2.486 Views
D
digi333 Themenstarter:in
290 Beiträge seit 2006
vor 17 Jahren
[Anfänger] Frage zu BitmapData? Siehe Erklärung und Bild!

Ich hab schon einmal so etwas ähnliches gefragt. Ich bin dabei einen Canny-Edge-Algorithmus zu erstellen. Leider wird mein eigener Algorithmus zu langsam und ich habe auf einer englischen Homepage einen freien Canny (für private Zwecke) gefunden. Ich möchte jetzt ein eigenes Bild (PixelFormat.Format24bppRgb) auf seinem Algorithmus (PixelFormat.Format8bppIndexed) laufen lassen. Wenn ich das versuche entsteht ein Canny mit 1/3 Bildbreite (siehe Anhang) was ja auch logisch ist, da Scan0 von BitmapData ein Byte-Wert ist und das Bild 3*Byte=24bppRgb (Rot[Byte], Grün[Byte], Blau[Byte]) besitzt.


                            Bitmap dstImg = new Bitmap(frameWidth, frameHeight, PixelFormat.Format24bppRgb);
                            BitmapData dstData =dstImg.LockBits(new Rectangle(0, 0, frameWidth, frameHeight), ImageLockMode.ReadWrite,PixelFormat.Format24bppRgb);

                            Bitmap bmp = new Bitmap(frameWidth, frameHeight, PixelFormat.Format24bppRgb);
                            for (int x = 0; x < frameWidth; x++)
                            {
                                for (int y = 0; y < frameHeight; y++)
                                {
                                    bmp.SetPixel(x, y, Color.FromArgb(frame.GetY(x, y), frame.GetY(x, y), frame.GetY(x, y)));
                                }
                            }
                            Bmp);
                            BitmapData srcData = bmp.LockBits(new Rectangle(0, 0, frameWidth, frameHeight), ImageLockMode.ReadOnly, PixelFormat.Format24bppRgb);
                            int stride = srcData.Stride;
                             // Canny
                             //hier muß die Breite korrigiert werden, aber wie?
                            byte* src = (byte*) srcData.Scan0.ToPointer() + stride;
                            byte* dst = (byte*) dstData.Scan0.ToPointer() + stride;
                            int p = frameWidth;
                            int i, j, ir;
                            int widthM1 = frameWidth - 1;
                            int heightM1 = frameHeight - 1;
                            int offset = stride - frameWidth;
                            double v, gx, gy;
                            double orientation, toPI = 180.0/Math.PI;
                            byte[] orients = new byte[frameWidth*frameHeight];
                            byte leftPixel = 0, rightPixel = 0;

                            // Step 3 - calculate magnitude and edge orientation

                            // for each line
                            unsafe
                            {
                                for (int y = 1; y < heightM1; y++)
                                {
                                    src++;
                                    dst++;
                                    p++;

                                    // for each pixel
                                    for (int x = 1; x < widthM1; x++, src++, dst++, p++)
                                    {
                                        
                                        // for each kernel row
                                        for (i = 0; i < 3; i++)
                                        {
                                            ir = i - 1;
                                            // for each kernel column
                                            for (j = 0; j < 3; j++)
                                            {
                                                // source value
                                                v = src[ir * stride + j - 1];

                                                gx += v * xKernel[i, j];
                                                gy += v * yKernel[i, j];
                                            }
                                        }
                                        // get gradient value
                                        *dst =  //hier wurde viel Quelltext enfernt
                             
                                        }

                                        // save orientation
                                        orients[p] = (byte)orientation;
                                    }
                                    src += (offset + 1);
                                    dst += (offset + 1);
                                    p++;
                                }

Ich hoffe ihr könnt mir helfen bei


byte* src = (byte*) srcData.Scan0.ToPointer() + stride;
byte* dst = (byte*) dstData.Scan0.ToPointer() + stride;

Wie gesagt ursprünglich war es ein anderes Pixelformat bei dem es ohne Probleme funktioniert.

I
1.739 Beiträge seit 2005
vor 17 Jahren

Mmh.
Ich würde mal überlegen, woher ich das ScrImage bekomme.
Hab ichs nicht nach 32 Bpp konvertiert, müsste ich die Palette benutzen...
Also 2 Möglichkeiten.

D
digi333 Themenstarter:in
290 Beiträge seit 2006
vor 17 Jahren

Das SrcImage erstelle ich mir aus einem voherigen Algorithmus. Ich nehme die Luminance (Y-Komponente aus YCbCr) und packe sie in die Rot-, Blau- und Grünkomponente. Es entsteht ein Graustufenbild. Das funktioniert ja auch noch einwandfrei, aber ihm dann zu sagen mit Scan0, dass die Breite nicht 1Byte sondern 3Byte ist. Das versteh ich leider nicht. Entweder muß der Quelltext mit den Schleifen auf die Bytebreite angepaßt werden oder ein anderes Format müßte gewählt werden für scr und dst. Wie gesagt ich bin Anfänger und hab keine große Ahnung von Pointer und BitmapData (wie diese abgelegt werden).

Super wäre man könnte mit Pixelformat.Format8bppIndexed arbeiten, da die Y-Komponente ja auch nur von 0 bis 255 geht, aber ich hab keine Ahnung wie ich das dann mit SetPixel als Farbe deklariere. Von Palette hab ich keine Ahnung.

@Ikarus: Meinst du das Pixelformat auf 32 Bpp anheben und die dst bzw. src auf double setzen? Oder wie meinst du den zweiten Teil?

I
1.739 Beiträge seit 2005
vor 17 Jahren

Na dann:
Her mit dem Link auf Canny.
Und schieb mal das Orginalbild hoch.
Ich hab auch keine Ahnung was "Canny-Edge" machen soll(vielleicht ist das ja das gewünschte Ergebnis) 😉
Naja dafür schau dann bei Google/WikiPedia nach.

Paletteninformationen liefert übrigens die Imageklasse, wenn ich nicht irre.
Irgendwie hab ich das Gefühl das die Verwendung von 24 Bpp kontraproduktiv ist.
In welchen Format sollte das Ergebnis vorliegen?

Wär für mich Task 2, Lösung Übermorgen oder am WE(ohne Garantie).

D
digi333 Themenstarter:in
290 Beiträge seit 2006
vor 17 Jahren

Canny dient zur Kantenerkennung. Man übergibt ihm ein Graustufenbild und als Ausgabe bekommt man ein Schwarz/Weiss-Bild, wo die Kanten Weiß sind. Wie in meinem Bild.

Erster Schritt: Graustufenbild (das ist bei mir die Variable "bmp") erzeugen - funktioniert einwandfrei, da bei YCbCr (Farbraum) die Y-Komponente der Graustufenwert ist. Da besteht aber gleichzeitig das Problem. Die Color in SetPixel kann ich nur als 24Bpp speichern mit FromArgb.

Ab der Zeile mit "int stride;" ist es der Originalquellcode Canny aus dem Internet. Im Internet ist srcImage ein übergebenes Bild mit dem Pixelformat Format8bppIndexed. Aber wie will man bei SetPixel die Farbe angeben und bleibt im PixelFormat 8bpp???

Die Aufgabe wäre wie im angegebenen Sourcetext. Erzeuge das Bitmap bmp wie angegeben (24bpp), aber das Pixelformat von BitmapData sei 8bpp.

Oder die Aufgabe kann auch anders gelöst werden... Erzeuge ein Bitmap bmp mit Pixelformat 8bpp, aber die Farbe liegt als Grauwert (Variable heißt "int frame.GetY") von 0-255 vor. Wie man dann die Farbe angibt keine Ahnung. Vielleicht mit Casten oder Convert.ToByte.

I
1.739 Beiträge seit 2005
vor 17 Jahren

Die Aufgabe wäre wie im angegebenen Sourcetext. Erzeuge das Bitmap bmp wie angegeben (24bpp), aber das Pixelformat von BitmapData sei 8bpp.

Ich fürchte das funktioniert so nicht(weiss aber nicht genau, da mir das so bisher nicht untergekommen ist(ein derartiges Problem))

Oder die Aufgabe kann auch anders gelöst werden... Erzeuge ein Bitmap bmp mit Pixelformat 8bpp, aber die Farbe liegt als Grauwert (Variable heißt "int frame.GetY") von 0-255 vor. Wie man dann die Farbe angibt keine Ahnung. Vielleicht mit Casten oder Convert.ToByte.

8 - bit Graustufe? Da brauchts keine Palette, RGB sind alle gleich.

Erster Schritt: Graustufenbild (das ist bei mir die Variable "bmp") erzeugen - funktioniert einwandfrei, da bei YCbCr (Farbraum) die Y-Komponente der Graustufenwert ist. Da besteht aber gleichzeitig das Problem. Die Color in SetPixel kann ich nur als 24Bpp speichern mit FromArgb.

Frage: Graustufe heisst was? Wieviel Bit Farbtiefe? ( für zB. med. Anwendungen gibts tatsächlich Graustufen mit 16/24 bit, wahrscheinlich irrelevant). Bei reiner Graustufe würde man jedenfalls keine Paletteninformation brauchen. Bei 24/32 Bit(Bildern in Farbe)wären 3-4 bytes verschwendet(pro Pixel). Das summiert sich dann schon(logischer 3-4 höherer Rechenaufwand für nüscht(aber auch nur im Worstcase) ).

PS:
Hatte leider doch nicht die erhoffte Zeit um mich mit Canny auseinanderzusetzen.

D
digi333 Themenstarter:in
290 Beiträge seit 2006
vor 17 Jahren

Immer mit der Ruhe! Mit Graustufen meine ich halt 256-Graustufen (1Byte). Genauere Graustufentiefen sind nicht notwendig.

Wie gesagt... kopier dir die ersten paar Zeilen oder bastel dir ein kleines Miniprogramm indem du von 24bpp zu 8bpp konvertierst. Das Miniprogramm ist schnell gemacht (vielleicht 7 Zeilen). Oder wie oben erklärt eines der Probleme löst. Es muß ja irgendwie gehen, da die Pixelformate existieren. 🤔

I
1.739 Beiträge seit 2005
vor 17 Jahren

So nahm mir jetzt die Zeit zur Analyse Quellcode und Algorithmus.
Dein Problem scheint weniger im Agorithmus zu liegen.
Dein Problem besteht darin das ein 24 bit-Bild 3 Byte pro Pixel benötigt und du das nicht berücksichtigt.

>int p = frameWidth;

in

>int p = frameWidth*3;

und den entrechenden Offset bei Zugriff berücksichtigen, dann klappts auch.

Weiterhin halte ich
>for (int x = 1; x < widthM1; x++, src++, dst++, p++)
für einen Overhead, der den Code unübersichtlich macht(das ist aber nur Stilfrage). ME reicht es einen Wert zu erhöhen, möglicherweise um das PixelOffset also z.B: x += 3. die restlichen Variablen benutzen dann den Offset(x) nur als AdressBasis)
Dann klappts besser/einfacher mit dem Zugriff(sieht jedenfalls einfacher aus, Fehler lassen sich leichter korrigieren..

PS: übrigens danke ich für den Hinweis auf John Cannys Algorithmus, kann mir nur zu gute kommen(für Vektorisierung usw.)

Vielleicht bringen dir diese Links noch etwas Input:

uni dresden, eine OCR-Lib(leider kein Sourcecode, aber frei und gut dokumentiert.
http://www.informatik.htw-dresden.de/~iwe/Belege/2004/BaerSchulte/index.htm
eine Javalösung, leicht in C# konvertierbar(Downloadlink).
http://homepages.inf.ed.ac.uk/rbf/HIPR2/cannydemo.htm
codeproject, eine C#Lösung
http://www.codeproject.com/cs/media/edge_detection.asp?df=100&forumid=3567&exp=0&select=1181415

Alle möglichen Verfahren des Canny-Edge werden in diesen Implementierungen leider nicht berücksichtigt. Google bringt aber zur Theorie einige gute Artikel und Diplomarbeiten(auch in deutsch, wenns wichtig sein sollte)