none
2D-Bild + Tiefenbild = 3D-Bild RRS feed

  • Frage

  • Hat sich schon jemand mit dem Konvertieren eines 2D-Bildes und eines Tiefenbildes in zwei nebeneinanderliegende Stereobilder per c# befaßt?

    Ich möchte das mit dem Tiefenbild der Kinect machen, finde aber nichts für c#, nur etwas für vb, das ist aber nicht performant und außerdem anaglyph. Ich will zwei Vollfarbenbilder nebeneinander, die man durch schielen zu 3D konvertiert.

    Tiefenbildbasierte Wiedergabe; Depth Image Based Rendering

     Ich habe ein byte[] array mit BGRABGRA-Folge. Das heißt, Wert für Blau,Grün,Rot,Alpha,Blau,Grün,... Das ganze 480 *  640 mal. Dann habe ich ein short[] array das aneinandergereiht die Tiefeninformationen enthält.

    Ich habe es geschafft zum Bild auf Seite 7 zu kommen. Wie komme ich jetzt - ich habe also ein byte[] array Blau,Grün,Rot,Alpha,Blau,Grün,..., das mit einer BitmapSource das erste bzw. zweite Bild von Rechts auf Seite 7 ergibt - auf das dritte von Links auf Seite 8 und letztlich auf das erste von rechts auf Seite 8 per C# 2010?

    Code / Links zu Code sind hilfreicher als Beschreibungen. Wichtig ist, Version 2010, .net 4. Denn viele Codebeispiele funktionieren damit nicht.


    Samstag, 12. Mai 2012 03:41

Alle Antworten

  • Hallo Richard,

    Anscheinend hat sich hier im Forum noch niemand mit der Synthese von stereoskopischen Bildern befaßt. Und wenn das Thema fremd ist, ist es auch kein Wunder, dass die Antworten so spärlich ausfallen ;-)

    Versuch mal deine Fragestellung aus der Thematik herauszulösen, in Teile zu zerlegen und als pures technisches Problem darzustellen, dass mit C# gelöst werden soll.

    Gruß
    Marcel

    Dienstag, 15. Mai 2012 06:59
    Moderator
  • Gute Idee, Marcel. Aktuell sieht es so aus, daß ich zwei Image-Objekte nebeneinander habe. Die Daten für die Image-Objekte baue ich mit einer BitmapSource:


    BRechts.Source = BitmapSource.Create(AktuellesTiefenBild.Width,

    AktuellesTiefenBild.Height,96, 96, PixelFormats.Bgr32, null, Rechts, stride);


    BLinks.Source = BitmapSource.Create(AktuellesTiefenBild.Width,

    AktuellesTiefenBild.Height,96, 96, PixelFormats.Bgr32, null, Links, stride);


    Das ist soweit in Ordnung, sodaß ich das Video sehen kann. Jetzt möchte ich aber die Ausgabe in eine AVI-Datei speichern. Und zwar beide Bilder in einem Video. Also wie gewünscht zerlegt:

    1. Es gibt zwei byte[] Arrays mit BGRABGRA.... Das eine byte[] array heißt Links, das andere Rechts.
    2. Diese möchte ich nebeneinander in Echtzeit als AVI speichern. Ich kann also nach Vollendung jedes Frames ein fertiges Links und Rechts anbieten, das der Algorithmus kombinieren und in eine AVI schreiben soll.
    3. Jetzt suche ich einen Weg um dies unter c# mit .net 4 zu erreichen. Ich schreibe .net 4, weil die meisten Codeschnipsel weder eine imagesource kennen noch .net 4.
    4. Es soll performant sein, aktuelle Technologien(etwa CUDA) ausnutzen, damit das fertige Video nicht ruckelt.
    Wenn du mehr Details brauchst, schreib es ruhig.

    Eisenanstreicher


    Dienstag, 15. Mai 2012 08:22
  • Hallo Richart,

    Ich werde nicht wirklich klug aus deinen Postings, wahrscheinlich aber liegt's an mir.
    Im Sinne der Aufklärung daher folgende Fragen und Feststellungen:

    1. Erwartest Du, dass dir hier jemand einen hole-filling-Algorithmus (linear, hierarchisch oder wie auch immer) in C# vorstellt?
    2. Erwartest Du, dass dir hier jemand erklärt, wie eine AVI-Datei über C# erstellt werden kann (avifil32.dll PInvoke)?
    3. Erwartest Du, dass dir hier jemand erklärt, wie man zwei Bitmaps zu einem einzigen Bitmap (nebeneinander) zusammenfügt (GDI+)?
    4. Anwendungen die in VB.NET geschrieben wurden sind per se nicht langsamer als C#-Anwendungen, ihnen fehlt lediglich die Möglichkeit unsicheren Code zu schreiben. Diesen kann man bei Bedarf aber in C#-Bibliotheken verschieben (aber wenn Du sowieso CUDA verwenden willst, ist das wirklich nicht sehr relevant).
    5. Sowohl BitmapSource als auch ImageSource sind bereits im .NET 3.0-Framework vorhanden.
    6. CUDA-Wrapper-Bibliotheken werden gewöhnlich in C++ erstellt werden. C++/CLI-Bibliotheken könnten allerdings in C# verwendet werden.
    7. Die Kombination von .NET und CUDA macht einiges an Performanz wieder weg, was durch den Einsatz von CUDA erreicht werden könnte, da beim Hin- und Herkopieren der Daten einges an Performanz auf der Strecke bleibt. Etwas Kompensation durch die Verwendung von Parallel.For in C# dennoch möglich.
    8. Die Real-Time-Ansprüche an .NET sind fehl am Platz, auch wenn wir über GCLatencyMode.LowLatency die Sensivität des GC beeinflussen können. .NET tut sich schwer mit Garantien.

    Wie Du anhand der Anzahl und Thematik der Fragen sehen kannst, ist der Fragenkomplex nicht ganz so kompliziert wie Erdölsuche in Alaska, aber auch nicht gerade point-and-shoot. Ich schlage deshalb vor, dass Du versuchst, auf *einzelne* Fragen Antworten zu erhalten. Mit anderen Worten: Das von dir angesprochene Thema ist m.E. noch immer viel zu weit gefächert.

    Gruß
    Marcel

    Dienstag, 15. Mai 2012 13:09
    Moderator
  • Zu Zweitens: Die Bibliothek habe bereits gefunden, schau dir aber mal die Verwendung unter dieser Verknüpfung an:

          AviWriter aw = new AviWriter();
          Bitmap bmp = aw.Open("test.avi", 25,w,h);
    ....
            MandelBrot.CalcMandelBrot(
              bmp,
    ....
    ....
            aw.AddFrame();
    ....
          aw.Close();
        }
        catch (AviWriter.AviException e) {
          Console.WriteLine("AVI Exception in: " + e.ToString());
        }
      }
    }

    Das Problem ist, wie gewinne ich BitMap aus BitMapSource? Ich möchte

    bmp = Aktueller Inhalt von BRechts oder BLinks oder beide kombiniert

    erreichen. Wie wandle ich also etwa

    BRechts.Source = BitmapSource.Create(AktuellesTiefenBild.Width,

    AktuellesTiefenBild.Height,96, 96, PixelFormats.Bgr32, null, Rechts, stride);


    bzw. den Inhalt von Rechts oder Links oder beide kombiniert in das Bitmap bmp um, damit es als Frame zugefügt werden kann?


    Eisenanstreicher


    Mittwoch, 16. Mai 2012 00:27
  • Da Du ja sowieso unsafe verwendest, kannst Du über BitmapSource.CopyPixels() arbeiten.
    Ein Code-Beispiel findest du hier.

    Gruß
    Marcel

    Mittwoch, 16. Mai 2012 06:09
    Moderator
  • Das Problem ist, wie gewinne ich BitMap aus BitMapSource?

    Hallo Richart Spindler,

    In WPF geht das wie folgt (shieldSource ist ein BitmapSource):

    //********************************************************************
    //BitmapSource --> BitmapImage
    PngBitmapEncoder encoder = new PngBitmapEncoder();
    MemoryStream memoryStream = new MemoryStream();
    BitmapImage bImg = new BitmapImage();
    encoder.Frames.Add(BitmapFrame.Create(shieldSource));
    encoder.Save(memoryStream);
    bImg.BeginInit();
    bImg.StreamSource = new MemoryStream(memoryStream.ToArray());
    bImg.EndInit();
    memoryStream.Close();
    //********************************************************************

    Quelle: UAC Shield in Button anzeigen

    Grüße,

    Robert


    Robert Breitenhofer, MICROSOFT  Twitter Facebook
    Bitte haben Sie Verständnis dafür, dass im Rahmen dieses Forums, welches auf dem Community-Prinzip „Entwickler helfen Entwickler“ beruht, kein technischer Support geleistet werden kann oder sonst welche garantierten Maßnahmen seitens Microsoft zugesichert werden können.

    Montag, 21. Mai 2012 16:37
    Moderator
  • Ich habe nun ein Programm, das die Grundzüge des Problems löst. Es gibt zwei Bilder, Links und Rechts. Diese werden in Echtzeit berechnet. Werden sie aber in jpg-Dateien ausgegeben, kommt es aber zu Aussetzern bei den ausgegebenen Bildern und zum Ruckeln bei der Bilddarstellung. Mein Code sieht so vom Aufbau her aus:

    using Microsoft.Kinect; using System; using System.Diagnostics; using System.Drawing; using System.IO; using System.Windows.Media.Imaging; using System.Windows.Media; using System.Windows; using System.Windows.Forms; using System.Threading; using System.Threading.Tasks; using System.Collections.Generic; namespace StereoKinect { public partial class MainWindow : Window { public MainWindow() { InitializeComponent(); } private KinectSensor newSensor

    Thread mm;

    BitmapSource image; Byte[] Beide; string a; FileStream stream; JpegBitmapEncoder encoder; //andere deklarationen private void Window_Loaded(object sender, RoutedEventArgs e) { kinectSensorChooser1.KinectSensorChanged += new DependencyPropertyChangedEventHandler(kinectSensorChooser1_KinectSensorChanged); } void kinectSensorChooser1_KinectSensorChanged(object sender, DependencyPropertyChangedEventArgs e) { var oldSensor = (KinectSensor)e.OldValue; //stop the old sensor if (oldSensor != null) { oldSensor.Stop(); oldSensor.AudioSource.Stop(); } //get the new sensor newSensor = (KinectSensor)e.NewValue; if (newSensor == null) { return; } newSensor.ColorStream.Enable(ColorImageFormat.RgbResolution640x480Fps30); newSensor.DepthStream.Enable(DepthImageFormat.Resolution640x480Fps30); newSensor.AllFramesReady += new EventHandler<AllFramesReadyEventArgs>(newSensor_AllFramesReady); try { newSensor.Start(); } catch (System.IO.IOException) { //this happens if another app is using the Kinect kinectSensorChooser1.AppConflictOccurred(); } } void newSensor_AllFramesReady(object sender, AllFramesReadyEventArgs e) { using (ColorImageFrame FarbEinzelbild = e.OpenColorImageFrame()) { if (FarbEinzelbild != null) { Debug.Assert(FarbEinzelbild.Width == 640 && FarbEinzelbild.Height == 480, "This app only uses 640x480."); if (RoheFarbPixel.Length != FarbEinzelbild.PixelDataLength) { RoheFarbPixel = new byte[FarbEinzelbild.PixelDataLength]; Bitmap_helfer = new WriteableBitmap(640, 480, 96.0, 96.0, PixelFormats.Bgr32, null); //this.BRechts.Source = Bitmap_helfer; } FarbEinzelbild.CopyPixelDataTo(RoheFarbPixel); gotColor = true; Links = new byte[FarbEinzelbild.Height * FarbEinzelbild.Width * 4]; Rechts = new byte[FarbEinzelbild.Height * FarbEinzelbild.Width * 4]; } } using (DepthImageFrame TiefenEinzelbild = e.OpenDepthImageFrame()) { //gleiches für TiefenEinzelbild //_mappedDepthLocations zuweisen } try { this.newSensor.MapDepthFrameToColorFrame(DepthImageFormat.Resolution640x480Fps30, RoheTiefenPixel, ColorImageFormat.RgbResolution640x480Fps30, _mappedDepthLocations); } catch (Exception g) { //MessageBox.Show(g.ToString()); } //Variablen //const int CodeFuerBlau = 0; //... for (int Zeile = 1; Zeile <= 480; Zeile++) { for (int Spalte = 1; Spalte < 640; Spalte++) { int Tiefe = RoheTiefenPixel[DurchTiefenbildZaehlen] >> DepthImageFrame.PlayerIndexBitmaskWidth; ColorImagePoint point = _mappedDepthLocations[DurchTiefenbildZaehlen]; //Array Links und Rechts entsprechende Werte zuweisen } } BRechts.Source = BitmapSource.Create(640, 480, 96, 96, PixelFormats.Bgr32, null, Rechts, stride); if (aufnahme) { BLinks.Source = null; Beide = new byte[2 * 480 * 640 * 4]; var list = new List<byte>(); list.AddRange(Links); list.AddRange(Rechts); Beide = list.ToArray(); //Links & Rechts untereinander durch = true; } else { BLinks.Source = BitmapSource.Create(640, 480, 96, 96, PixelFormats.Bgr32, null, Links, stride); } } bool durch = false; private void thread() { while (aufnahme) //Dann true, wenn das Beide-Array gefüllt ist. { if (durch == true) { durch = false; a = Convert.ToString(nummer); stream = new FileStream("E:\\Temp\\3D-Video " + a.PadLeft(12, '0') + ".jpg", FileMode.Create); encoder = new JpegBitmapEncoder(); encoder.FlipHorizontal = true; encoder.QualityLevel = 85; encoder.Frames.Add(BitmapFrame.Create(BitmapSource.Create(640, 480 * 2, 96, 96, PixelFormats.Bgr32, null, Beide, stride))); encoder.Save(stream); nummer++; } } }

    private void Window_Closing(object sender, System.ComponentModel.CancelEventArgs e) { StopKinect(); }

    private void StopKinect() { if (newSensor != null) { if (newSensor.IsRunning) { newSensor.Stop(); } } }

    private void button1_Click(object sender, RoutedEventArgs e) //Aufnahmebutton { if (aufnahme == false) { button1.Content = "Pause"; mm = new Thread(thread); mm.Start(); aufnahme = true; } else { mm.Abort(); aufnahme = false; button1.Content = "Als AVI speichern"; } } } }


    Es gibt noch einen button1, der die Aufnahme in jpg-Dateien startet. Wie könnte ich den Code performanter machen? 


    Eisenanstreicher



    Mittwoch, 30. Mai 2012 01:35
  • Hallo Richart,

    Einige Ideen zur Performanzsteigerung:

    1. Allein schon das Encoding an sich verbraucht wertvolle Zeit. Muss das wirklich sein, wenn's nur um die Anzeige geht?
    2. Festplatten sind langsam. Ersetzte FileStream mit einem MemoryStream, dass Du zu einer List<MemoryStream> hinzufügst.
    3. Wrappe BitmapSource in einem WriteableBitmap. Übergib das WriteableBitmap weiter an BitmapFrame.Create().
    4. Für die Dauer der Aufnahme stelle GCSettings.LatencyMode auf LowLatency ein.

    Gruß
    Marcel

    Mittwoch, 30. Mai 2012 07:27
    Moderator
  • Hallo Marcel,

    Einige Ideen zur Performanzsteigerung:

    1. Allein schon das Encoding an sich verbraucht wertvolle Zeit. Muss das wirklich sein, wenn's nur um die Anzeige geht?

    -> Welchen Abschnitt meinst du?

    2. Festplatten sind langsam. Ersetzte FileStream mit einem MemoryStream, dass Du zu einer List<MemoryStream> hinzufügst.
    3. Wrappe BitmapSource in einem WriteableBitmap. Übergib das WriteableBitmap weiter an BitmapFrame.Create().

    -> Ich verstehe dich nicht. Bitte ein Beispiel.

    4. Für die Dauer der Aufnahme stelle GCSettings.LatencyMode auf LowLatency ein.

    -> Implementiert

    Gruß
    Richart
    Mittwoch, 30. Mai 2012 11:32
  • 2. Festplatten sind langsam. Ersetzte FileStream mit einem MemoryStream, dass Du zu einer List<MemoryStream> hinzufügst.
    3. Wrappe BitmapSource in einem WriteableBitmap. Übergib das WriteableBitmap weiter an BitmapFrame.Create().

    -> Ich verstehe dich nicht. Bitte ein Beispiel.

    Hallo Richart Spindler,

    Aufgepasst: In WPF im folgendem Beitrag:

    http://social.msdn.microsoft.com/Forums/de-DE/visualcsharpde/thread/a48577e7-0cfb-49fc-b2ab-a2f392d4a9d4#33c4ec97-13c3-4a56-8b13-31e6020622a1

    Grüße,

    Robert


    Robert Breitenhofer, MICROSOFT  Twitter Facebook
    Bitte haben Sie Verständnis dafür, dass im Rahmen dieses Forums, welches auf dem Community-Prinzip„Entwickler helfen Entwickler“ beruht, kein technischer Support geleistet werden kann oder sonst welche garantierten Maßnahmen seitens Microsoft zugesichert werden können.

    Mittwoch, 30. Mai 2012 11:48
    Moderator
  • Hallo ihr beide.

    2. Festplatten sind langsam. Ersetzte FileStream mit einem MemoryStream, dass Du zu einer List<MemoryStream> hinzufügst.

    Nachdem mir nicht klar ist, wie das gemeint ist, habe ich es wie folgt implementiert. Leider bricht das Programm nach etwa einer Minute wegen einem Speicherproblem ab. Ideen?

    bool durch = false;
    List<byte> HilfsListe;
    List<Array> ArrayAnsammlung;
    BitmapSource image;
    Byte[] Beide;
    string Nummerierung;
    MemoryStream stream;
    JpegBitmapEncoder encoder;
    
    void newSensor_AllFramesReady(object sender, AllFramesReadyEventArgs e)
    {
        //...
    
        //create image
        BRechts.Source =
            BitmapSource.Create(640, 480,
            96, 96, PixelFormats.Bgr32, null, Rechts, stride);
        if (aufnahme)
        {
            BLinks.Source = null;
            Beide = new byte[2 * 480 * 640 * 4];
            HilfsListe.AddRange(Links);
            HilfsListe.AddRange(Rechts);
            Array.Clear(Links, 0, Links.Length);
            Array.Clear(Rechts, 0, Links.Length);
            Beide = HilfsListe.ToArray();
            HilfsListe.Clear();
            ArrayAnsammlung.Add(Beide);
    
            //durch = true;
        }
        else
        {
            BLinks.Source =
            BitmapSource.Create(640, 480,
            96, 96, PixelFormats.Bgr32, null, Links, stride);
        }
    }
    
    private void button1_Click(object sender, RoutedEventArgs e)
    {
        if (aufnahme == false)
        {
            button1.Content = "Pause";
            ArrayAnsammlung = new List<Array>();
            HilfsListe = new List<byte>();
            //GCSettings.LatencyMode = GCLatencyMode.LowLatency;
            mm = new Thread(thread);
            mm.Start();
            aufnahme = true;
        }
        else
        {
            mm.Abort();
            aufnahme = false;
            button1.Content = "Als AVI speichern";
            //GCSettings.LatencyMode = GCLatencyMode.Interactive;
        }
    }
    
    private void thread()
    {
        while (aufnahme)
        {
            if (ArrayAnsammlung != null)
            {
                int l = ArrayAnsammlung.Count - 1;
                for (int DurchLaeufer = 0; DurchLaeufer < l; DurchLaeufer++)
                {
                    Nummerierung = Convert.ToString(Nummerierungszaehler);
                    encoder = new JpegBitmapEncoder();
                    encoder.FlipHorizontal = true;
                    encoder.QualityLevel = 70;
                    FileStream stream2 = new FileStream("E:\\Temp\\3D-Video " + Nummerierung.PadLeft(12, '0') + ".jpg", FileMode.Create);
                    encoder.Frames.Add(BitmapFrame.Create(BitmapSource.Create(640, 480 * 2,
                    96, 96, PixelFormats.Bgr32, null, ArrayAnsammlung[DurchLaeufer], stride)));
                    encoder.Save(stream2);
                    stream2 = null;
                    encoder = null;
                    Nummerierungszaehler++;
                }
                if (l > 0)
                {
                    ArrayAnsammlung.RemoveRange(0, l);
                }
                Thread.Sleep(400);
            }
        }
    
        //k.AddFrame();
    }
    
    


    Eisenanstreicher

    Freitag, 1. Juni 2012 19:44
  • bool durch = false;
    List<byte> HilfsListe;
    List<Array> ArrayAnsammlung;
    BitmapSource image;
    Byte[] Beide;
    string Nummerierung;
    MemoryStream stream;
    JpegBitmapEncoder encoder;
    
    ...

    Hallo Richart Spindler,

    Ich sehe Du hast in Deinem Code die Zeile MemoryStream stream; geschrieben. Wo hast Du dann diesen stream weiter im Code verwendet?

    Grüße,

    Robert


    Robert Breitenhofer, MICROSOFT  Twitter Facebook
    Bitte haben Sie Verständnis dafür, dass im Rahmen dieses Forums, welches auf dem Community-Prinzip „Entwickler helfen Entwickler“ beruht, kein technischer Support geleistet werden kann oder sonst welche garantierten Maßnahmen seitens Microsoft zugesichert werden können.

    Montag, 18. Juni 2012 07:10
    Moderator