Laden...

RGB-Byte[] in BGR-Bitmap?

Erstellt von blacksheep1 vor 14 Jahren Letzter Beitrag vor 14 Jahren 5.251 Views
B
blacksheep1 Themenstarter:in
6 Beiträge seit 2009
vor 14 Jahren
RGB-Byte[] in BGR-Bitmap?

Schönen guten Tag!
Ich hab ein Byte[] mit RGB-Farbinformation und im gegensatz zum PixelFormat "Format24bppRgb" ist die Reihenfolge in selbigem auch R-G-B. Leider hab ich nach längerem suchen dann auch herausgefunden, dass dieses Pixelformat scheinbar nicht wie der name denken lässt die reihenfolge R-G-B hat, sondern B-G-R. Mein problem ist nun, dass ich mein Array möglichst performant (da viele bilder/s abgearbeitet werden müssen) in ein Bitmap, mit richtigen Farbinformationen, kriegen muss! Gibt es eine klasse oder einen Trick die/der dies für mich erledigt, oder muss ich echt PixelWert für PixelWert austauschen (geht das auch in schnell?)?!

Vielen Dank schonmal!

B
blacksheep1 Themenstarter:in
6 Beiträge seit 2009
vor 14 Jahren

Nope, is zu langsam. Brauch ich über 4 sekunden/bild. Hatte vorher die hier ausprobiert http://www.dreamincode.net/forums/showtopic14788.htm , die liegt bei < 3 Sek. Weitere vorschläge?

1.130 Beiträge seit 2007
vor 14 Jahren

Wie man das wenns schnell gehen soll so macht:


unsafe RGBtoBGRA(int width, int height,byte* pSrc, int srcStride, byte* pDest, int destStride)
{
    byte* srcScan=pSrc;
    byte* destScan=pDest;
    byte* maxSrcScan=srcStride*height;
    while(srcScan<maxSrcScan)
    {
        byte* srcPixel=srcScan;
        byte* destPixel=destScan;
        byte* maxSrcPixel=srcPixel+3*width;
        while(srcPixel<maxSrcPixel)
        {
            *(destPixel+3)=255;//a
            *(destPixel+2)=*(srcPixel++);//r
            *(destPixel+1)=*(srcPixel++);//g
            *(destPixel+0)=*(srcPixel++);//b
            destPixel+=4;
        }
        srcScan+=srcStride;
        destScan+=destStride;
    }
}

Den Zeiger bekommt man mit bitmap.LockBits

Projekte:Jade, HttpSaver
Zum Rechtschreiben gibts doch schon die Politiker. Aber die bauen auch nur mist!

C
114 Beiträge seit 2008
vor 14 Jahren

Also ich frage mich, was für Bilder du hast und wie du die Werte setzt (oder auf was für einer Kiste du rechnest). Denn 4 Sekunden für ein Bild, das scheint mir doch recht extrem. Ich führe momentan die PNG-Prefilter, ColorSpace Konvertierungen, Diskrete Kosinus Transformationen und Motion Search zwischen zwei Bildern mit häufig mehr als einem Bild pro Sekunde durch. Und das ist doch deutlich rechenintensiver.

EDIT: Habe bei mir grade einen Test mit der MemBitmap gemacht und n Bild mit 1024*768 setzen lassen. Dauert 31ms!

„Heute back ich, morgen brau ich,
übermorgen cast ich die Königin nach int;
ach, wie gut dass niemand weiß,
dass ich Rumpelstilzchen heiß!“

"Bei Stylefragen sollteste nen Mac-User oder ne Frau fragen."

B
293 Beiträge seit 2008
vor 14 Jahren

hey blacksheep1,
das sind nicht zufällig die .ppm Bilder vom BWINF?

Wenn ich nicht hier bin, findest du mich auf code-bude.net.

B
blacksheep1 Themenstarter:in
6 Beiträge seit 2009
vor 14 Jahren

Sorry, hat nen bisschen gedauert. Meine Bilder kommen von einer Zeilenkamera, und haben eine dimension von etwa 6000x10000px. Habs jetzt vorrübergehend gelöst, indem ich die RB-werte per hand im Byte[] tausche, bevor das bmp erzeugt wird.


        private Byte[] ChangeRB(Byte[] Array)
        {
            Byte Temp;
            for (int x = 0; x < Features.Image.HeightAquisition.value * Features.Image.Width.value * 3; x = x + 3)
            {

                 Temp = Array[x + 2];
                 Array[x + 2] = Array[x];
                 Array[x] = Temp;
            }
            return Array;
        }

Das dauert ~350ms. Gibts ne möglichkeit, das auf der ebene noch schneller zu machen?

PS @ Chickenlord: C2D 3 GHz

1.361 Beiträge seit 2007
vor 14 Jahren

Gibts ne möglichkeit, das auf der ebene noch schneller zu machen?

1.Mit Pointer arbeiten (also unsafe), wie von Floste vorgemacht (dein byte-Array natürlich pinnen) 1.Die Berechnung von

Features.Image.HeightAquisition.value * Features.Image.Width.value * 3

aus der Schleife auslagen. (ich weiß nicht, ob compiler/Runtime checkt, dass sich die Werte nicht ändern (können)) 1.Die Schleifenlaufrichtung umdrehen (siehe Performancebetrachtungen (Schleifenrichtung) ; RangeCheckElimination wird bei dir eh nicht gemacht.)

  1. (Das ganze in eine C/C++-Methode auslagern, die SSE verwendet
  1. (Die Kamera/Treiber so konfigurieren, dass gleich das gewünschte Format rauskommt

beste Grüße
zommi

S
401 Beiträge seit 2008
vor 14 Jahren

Servus,

350ms ist doch ein guter Wert. Warum soll das noch schneller gehen?

Die Liste von zommi lässt sich noch erweitern, wenn du die GraKa mit einbeziehst
6. Vertex-Shader bzw. Fragment-Shader (Pixel-Shader) die Arbeit erledigen lassen
7. OpenCL zur Kommunikation mit der GPU nutzen

Gruß,
Thomas

B
blacksheep1 Themenstarter:in
6 Beiträge seit 2009
vor 14 Jahren

Guten tach,
hab mal die funktion zum farbwert-tausch in C++ implementiert und dann per DLLimport die erzeugte DLL in mein C# Projekt eingebunden. Meine beobachtung:
Unter C++ als eigenständiges Programm: < 1ms
Unter C# als gewrappte Klasse: > 300 ms
Wie kommt der Irrsinnige unterschid zustande?

Hier der C++-Code (<1 ms):


int Height = 1976;
int Width = 2096;
unsigned char Array[1976*2096*3];

void fill();
double diffclock(clock_t clock1,clock_t clock2);
void ChangeRB(unsigned char* xArray, int xHeight, int xWidth);

int _tmain(int argc, _TCHAR* argv[])
{
	fill();
	clock_t begin=clock();
	ChangeRB(Array, Height, Width);
	clock_t end=clock();
	cout<<"Execution time: "<<diffclock(end,begin)<<" ms."<<endl;
	_getch();
	return 0;
}

void ChangeRB(unsigned char* xArray, int xHeight, int xWidth)
{
    char xTemp;
    for (int x = 0; x < xHeight * xWidth * 3; x = x + 3)
    {
        xTemp = xArray[x + 2];
        xArray[x + 2] = xArray[x];
        xArray[x] = xTemp;
    }
}

void fill()
{
	for(int x = 0; x < Height*Width*3; x+=3)
	{
		Array[x] = 1;
		Array[x+1] = 2;
		Array[x+2] = 3;
	}
}

double diffclock(clock_t clock1,clock_t clock2)
{
	double diffticks=clock1-clock2;
	double diffms=(diffticks*10)/CLOCKS_PER_SEC;
	return diffms;
}

Die in C# zu wrappende DLL:


#include "stdafx.h"

extern "C" __declspec(dllexport)unsigned char* ChangeRB(unsigned char* xArray, int xHeight, int xWidth);

unsigned char* ChangeRB(unsigned char* Array, int Height, int Width)
{
    char Temp;
    for (int x = 0; x < Height * Width * 3; x = x + 3)
    {
        Temp = Array[x + 2];
        Array[x + 2] = Array[x];
        Array[x] = Temp;
    }
 	return Array;
}

Der C#-Code:


    public partial class Form1 : Form
    {
        [DllImport("bvDLL.dll", EntryPoint = "ChangeRB")]
        public static extern string ChangeRB(string Array, int Height, int Width);

        Stopwatch t1 = new Stopwatch();
	byte[] Array = new byte[1976 * 2096 * 3];

        private void button1_Click(object sender, EventArgs e)
        {
            fill();
	    String s = ByteArrayToString(Array);
            while (true)
            {
                    t1.Start();
                    ChangeRB(s, 1976, 2096);
                    t1.Stop();
		    MessageBox.Show(Convert.ToString(t1.ElapsedMilliseconds));	
                    t1.Reset();
            }
        }
      
        void fill()
        {
            for (int x = 0; x < 1976 * 2096 * 3; x += 3)
            {
                Array[x] = 1;
                Array[x + 1] = 2;
                Array[x + 2] = 3;
            }
        }
}
    
1.361 Beiträge seit 2007
vor 14 Jahren

Hi blacksheep1.

Ein String besteht unter C# aus chars. (Unicode, UCS-2)
Ein char ist unter C# 2 Byte groß.

Ein String besteht unter C/C++ ebenfalls aus chars. (ANSI)
Jedoch ist dort ein Char nur 1 Byte groß.

Bei nativen Aufrufen aus C# heraus, erkennt der Compiler (weil der ja schlau ist),
dass du ein string an eine native Sprache übergibst.
Und da das Ziel wohl meistens 1Byte char Arrays erwartet, ist standardmäßig das Marshalling in diese Arrays mit 1Byte eingestellt.
(siehe: Standardmäßiges Marshalling für Zeichenfolgen)

Was passiert also bei jedem Aufruf? Die Runtime holt sich neuen Speicher, kopiert deinen String darein, ändert dabei die 2Byte auf 1Byte und übergibt das dann an die C-DLL.

=> Laaaaaangsam.

Tja, man sollte schon wissen, was man tut, wenn man wild hin und her marshallet.
Und vor allem sollte man nicht strings missbrauchen, wenn man byte-Arrays meint!

beste Grüße
zommi

B
blacksheep1 Themenstarter:in
6 Beiträge seit 2009
vor 14 Jahren

Ok, das ergibt Sinn. Es war auch nur ein vorrübergehender versuch mit dem string, um zu gucken ob es überhaupt funktioniert (...meine ersten expoerimente mit dem Wrappen von C-Klassen).
Die Frage die sich mir nun selbstverständlich stellt ist, wie krig ich nun mein byte[] in die C
-Funktion und wieder zurück, ohne die geschwindigkeit die ich beim Farbwertetausch gewonnen habe wieder einzubüßen?

Edit:
Ok, wenn ich ihm gleich nen Byte[] übergebe, bin ich schon bei knapp 150 ms. Aber er Marshallt es ja immernoch von Byte zu char .... gibt es nicht ne möglichkeit, dass der Compiler den Datentyp so lässt wie er ist? Handelt sich doch wenn ich das richtig verstehe bei beiden (Char und unsigned Byte) um einen 8-Bit Datentyp ohne vorzeichen und ich übergebe doch eh nur den Pointer (oder nicht?), womit ihm doch die bei 8-Bit feldgröße die Adressen der weiteren Felder bekannt sein müssten?!

S
401 Beiträge seit 2008
vor 14 Jahren

Ein String besteht unter C# aus chars. (Unicode, UCS-2)
Ein char ist unter C# 2 Byte groß.

Ein String besteht unter C/C++ ebenfalls aus chars. (ANSI)
Jedoch ist dort ein Char nur 1 Byte groß.

wchar lösst das Problem. Müsste beim VC 2 Byte groß sein. MS weicht hier von anderen Compiler ab.

Ich sehe da noch ein anderes Problem. Welche Optimierungen hast du eingeschaltet? Eventuell optimiert dir der Compiler alle Methodenaufrufe weg. Das Array wird nirgends benötigt bzw. die Main-Methode greift darauf nicht zu. Lass dir die Ergebnisse nach der Messung ausgeben, oder schreib sie in eine Datei.

Gruß,
Thomas

B
blacksheep1 Themenstarter:in
6 Beiträge seit 2009
vor 14 Jahren

Falls du meinst, dass ich falsche Ergebnisse bekommen könnte, dem ist nicht so. Von der Funktion her läufts ohne probleme.