none
[C++] Kinect Distanzmessung RRS feed

  • Frage

  • Guten Morgen!

    Ich versuche schon seit Stunden die Distanz mittels der Kinect auf dem Bildschirm auszugeben... vergeblich... :/

    Ich habe zwei Probleme, erstens werden falsche Werte ausgeworfen und zweitens geht die Framerate extrem in die Knie, wenn ich den String für die Bildschirmausgabe einfüge. Hier mal der Code der Funktion die die Depthdaten auswertet und ausgeben soll. Der String wird in einer Renderfunktion dann aufgerufen, was auch funtkioniert. Bin noch ein ziemlicher Programmierneuling btw :P

    void updateImageFrame(NUI_IMAGE_FRAME &imageFrame, bool isDepthFrame)
    {
    	INuiFrameTexture *nuiTexture = imageFrame.pFrameTexture;
    	NUI_LOCKED_RECT lockedRect;
    	nuiTexture->LockRect(0, &lockedRect, NULL, 0);
    
    	if (lockedRect.Pitch != NULL)
    	{
    		const BYTE *pbuffer = (const BYTE*)lockedRect.pBits;
    
    		for (int i = 0; i<480; ++i)
    		{
    			const BYTE *line = pbuffer + i * lockedRect.Pitch;
    			const USHORT *bufferWord = (const USHORT*)line;
    
    			for (int j = 0; j<640; ++j)
    			{
    				if (!isDepthFrame)
    				{
    					std::cout << "No Depth-Stream" << std::endl;
    				}
    				else
    				{
    					unsigned char *pDepth = depthTexture->bits + (i * 640 + j);
    					*pDepth = (unsigned char)NuiDepthPixelToDepth(bufferWord[j]);
    
    					std::stringstream String;
    					String << "Distanz[mm]:" << *pDepth << std::endl;
    					Text = String.str();
    				}
    			}
    		}
    		TextureObject* tobj = (depthTexture);
    		glBindTexture(GL_TEXTURE_2D, tobj->id);
    		glTexImage2D(GL_TEXTURE_2D, 0, tobj->internalFormat, tobj->width, tobj->height,
    			0, tobj->imageFormat, GL_UNSIGNED_BYTE, tobj->bits);
    	}
    	nuiTexture->UnlockRect(0);
    
    } // updateImageFrame

    Also die Distanz sollte also laufend am Bildschirm ausgegeben werden.

    Lg Epi


    • Bearbeitet Epiphany99 Montag, 24. August 2015 09:03
    Montag, 24. August 2015 09:02

Antworten

  • Endlich hab ich es geschafft!!! :D

    Ich habe es aber auf eine etwas andere Weise gelöst, sondern mittels Skeleton-Tracking. Die Update-Funktion bleibt eig genau so wie sie war, nur das ich jetzt den RGB-Stream verwende und nicht mehr den Depthstream:

    void updateImageFrame(NUI_IMAGE_FRAME &imageFrame)
    {
    	// Buffer sperren
    	INuiFrameTexture *nuiTexture = imageFrame.pFrameTexture;
    	NUI_LOCKED_RECT lockedRect;
    	nuiTexture->LockRect(0, &lockedRect, NULL, 0);
    	if (lockedRect.Pitch != NULL)
    	{
    		// Bildschirmauflösung annehmen und initialisieren
    		const BYTE *buffer = (const BYTE*)lockedRect.pBits;
    		for (int i = 0; i<480; ++i)
    		{
    			const BYTE *line = buffer + i * lockedRect.Pitch;
    			const USHORT *bufferWord = (const USHORT*)line;
    
    			for (int j = 0; j<640; ++j)
    			{
    				unsigned char* ptr = colorTexture->bits + 3 * (i * 640 + j);
    				*(ptr + 0) = line[4 * j + 2];
    				*(ptr + 1) = line[4 * j + 1];
    				*(ptr + 2) = line[4 * j + 0];
    			}
    		}
    		// Texturen an OpenGL senden und ausgeben 
    		TextureObject *tobj = colorTexture;
    		glBindTexture(GL_TEXTURE_2D, tobj->id);
    		glTexImage2D(GL_TEXTURE_2D, 0, tobj->internalFormat, tobj->width, tobj->height,
    			0, tobj->imageFormat, GL_UNSIGNED_BYTE, tobj->bits);
    	}
    	// Buffer wieder entsperren
    	nuiTexture->UnlockRect(0);
    
    } // updateFrameImage

    Und nachdem ich die ganzen Verbindungen eingerichtet habe, gebe ich die Distanz einfach bei der Berechnung der einzelnen Punkte des Skeletts aus. Somit wird der String nicht mehr 307200mal (640*480) sondern nur mehr 20mal erstellt, also genau soviel, wie Punke am Skelett definiert sind und voilà... es funktioniert :D :D :D

    void updateSkeletonData(NUI_SKELETON_DATA& data)
    {
    	POINT coordInDepth;
    	USHORT depth = 0;
    
    	// Alle Verbindungen durchlaufen
    	for (int i = 0; i < NUI_SKELETON_POSITION_COUNT; i++)
    	{
    		// Verbindungspositionen erhalten und
    		// in Tiefeninformationen umrechnen
    		NuiTransformSkeletonToDepthImage(data.SkeletonPositions[i],	
    						&coordInDepth.x,	
    						&coordInDepth.y,				
    						&depth,							
    						NUI_IMAGE_RESOLUTION_640x480);
    
    		
    		// Distanz in Millimeter am Bildschirm ausgeben
    		std::stringstream depthText;
    		depthText << "Distanz[mm]: " << NuiDepthPixelToDepth(depth) << std::endl;
    		Text = depthText.str();
    
    		// Alle Koordinaten in das Array abspeichern
    		skeletonVertices[i][0] = (GLfloat)coordInDepth.x / 640.0f;
    		skeletonVertices[i][1] = 1.0f - (GLfloat)coordInDepth.y / 480.0f;
    		skeletonVertices[i][2] = (GLfloat)NuiDepthPixelToDepth(depth) * 0.00025f;
    	}
    		
    } // updateSkeletonData

    Nochmal herzlichen Dank an Iso7 für die Unterstützung.

    Lg Epi

    • Als Antwort markiert Epiphany99 Freitag, 28. August 2015 10:45
    Freitag, 28. August 2015 10:41

Alle Antworten

  • Hallo,

    die Funktion  NuiDepthPixelToDepth liefert einen USHORT Wert zurück. Richtig wäre:

    USHORT usDepth = NuiDepthPixelToDepth(bufferWord[j]);


    Und noch paar Punkte:

    Es ist empfohlen die Variablen eindeutigen Namen zu vergeben:

    std::stringstream depthString;
    depthString << "Distanz[mm]:" << usDepth << std::endl;

    Typkonvertierung:

    const USHORT *bufferWord = reinterpret_cast<const USHORT*>(line);


    Grüße




    • Bearbeitet Iso7 Montag, 24. August 2015 13:23
    Montag, 24. August 2015 10:39
  • Hey!

    Erstmal danke für die Antwort! Mir stellt sich aber die Frage, was ich dann mit

    unsigned char *pDepth = depthTexture->bits + (i * 640 + j);

    mache. Benötige ich nicht davon die Daten um die Distanz mit der Funktion NuiDepthPixeltoDepth errechnen zu können? Weil dann hab ich ja einmal einen unsigned char und einmal USHORT.

    Montag, 24. August 2015 12:08
  • Hallo,

    hier steht, dass der Depth-Wert 13 Bits lang ist. Also um den Wert zu speichern wird 2 Byte benötigt.

    Bei der  Zuweisung

    *pDepth = (unsigned char)NuiDepthPixelToDepth(bufferWord[j]);
    wird den Wert in unsigned char umgewandelt und abgeschnitten( Maximum-Wert  255).

     

    Wenn Du den Depth–Wert speichern willst, dann kannst Du über einen USHORT-Pointer auf den Speicher zugreifen( bevor muss der Speicher selbstverständlich  allokiert werden).

    USHORT *pDepth =/*Adresse berechnen*/;
    (*pDepth) = NuiDepthPixelToDepth(bufferWord[j]);
    Wie deine  Datenstruktur detphTexture aussieht, weiß ich nicht.

     

    Grüße  


    • Bearbeitet Iso7 Montag, 24. August 2015 20:25
    Montag, 24. August 2015 13:23
  • Die Struktur dephtTexture ist eine Instanz von TextureObject:

    struct TextureObject
    {
        GLuint id;
        GLenum imageFormat;
        GLint internalFormat;
        unsigned int width;
        unsigned int height;
        unsigned char* bits;
    };

    Deswegen auch meine Umwandlung in einen "unsigned char"

    Hab jetzt mal Datenstruktur gecastet und bekomme jetzt Distanzen die vl stimmen könnten. Da ich aber eine Framerate von geschätzten 0,25 habe, ist das schwer einzuschätzen. Das liegt eindeutig am String den ich erstelle, kommentiere ich ihn aus, läuft alles flüssig, versuche ich die Struktur außerhalb der Schleife anzulegen, wird nur ein graues Bild angezeigt :/

    if (lockedRect.Pitch != NULL)
    	{
    		
    		const BYTE *pbuffer = reinterpret_cast <const BYTE*>(lockedRect.pBits);
    
    		for (int i = 0; i < 480; ++i)
    		{
    			const BYTE *line = pbuffer + i * lockedRect.Pitch;
    			const USHORT *bufferWord = reinterpret_cast <const USHORT*>(line);
    
    			for (int j = 0; j < 640; ++j)
    			{
    				if (!isDepthFrame)
    				{
    					std::cout << "No Depth-Stream" << std::endl;
    				}
    				else
    				{
    					USHORT *pDepth = reinterpret_cast<USHORT*>(depthTexture->bits + (i * 640 + j));
    					*pDepth = NuiDepthPixelToDepth(bufferWord[j]);
    
    					std::stringstream depthString;
    					depthString << "Distanz[mm]:" << *pDepth << std::endl;
    					Text = depthString.str();
    				}
    			}
    		}


    • Bearbeitet Epiphany99 Montag, 24. August 2015 15:57
    Montag, 24. August 2015 15:13
  • Hallo,

    ich bin nicht sicher, ob deine Berechnung der Adresse für pDepth richtig ist. Wieviel Bytes sind für eine Pixel in depthTexture->bits vorgesehen? Ich nehme an: 2 Bytes. Dann die Berechnung wäre: 

    USHORT *pDepth = reinterpret_cast<USHORT*>(depthTexture->bits + 2*(i * 640 + j));


    Deine Text Variable wird in der Methode 307119 Mal überschrieben. Ist das so geplannt?


    Grüße


    Montag, 24. August 2015 20:24
  • Mahlzeit!

    Berechnung sollte eig so stimmen. Habs mal versucht mit 2 zu multiplizieren und bekam eine Fehlermeldung.

    Zum Thema Text, jein, ich bin mir zwar bewusst, dass der Text so oft überschrieben wird, was ja auch der Grund für die schwache Framerate sein wird, aber mir fällt gerade keine andere Variante ein, wie ich das außerhalb der Schleife ausgeben soll, da ich ja immer die aktuellen Daten von pDepth brauche. Hmmm... mit ner Liste vl?

    lg Epi

    Dienstag, 25. August 2015 10:52
  • Hallo,

    in deiner Berechnung wird die neue Adresse nicht um 2 Bytes sondern um 1 Byte  erhöht, also bei der neuen Berechnung wird das nächste Byte in unsigned char Array adressiert. Dann wird bei der Zuweisung von USHORT Typs ein Byte von dem zuletzt geschriebenen Wert überschrieben.

     

    Du speicherst schon deine Depth-Werte in depthTexture. Hier kannst Du auf die benötigte Werte zugreifen.

    Grüße       


    • Bearbeitet Iso7 Dienstag, 25. August 2015 12:22
    Dienstag, 25. August 2015 12:16
  • Nochmal danke für die Hilfe! Werd mir das am Wochenende nochmals genauer anschauen. Lg
    Dienstag, 25. August 2015 17:44
  • Endlich hab ich es geschafft!!! :D

    Ich habe es aber auf eine etwas andere Weise gelöst, sondern mittels Skeleton-Tracking. Die Update-Funktion bleibt eig genau so wie sie war, nur das ich jetzt den RGB-Stream verwende und nicht mehr den Depthstream:

    void updateImageFrame(NUI_IMAGE_FRAME &imageFrame)
    {
    	// Buffer sperren
    	INuiFrameTexture *nuiTexture = imageFrame.pFrameTexture;
    	NUI_LOCKED_RECT lockedRect;
    	nuiTexture->LockRect(0, &lockedRect, NULL, 0);
    	if (lockedRect.Pitch != NULL)
    	{
    		// Bildschirmauflösung annehmen und initialisieren
    		const BYTE *buffer = (const BYTE*)lockedRect.pBits;
    		for (int i = 0; i<480; ++i)
    		{
    			const BYTE *line = buffer + i * lockedRect.Pitch;
    			const USHORT *bufferWord = (const USHORT*)line;
    
    			for (int j = 0; j<640; ++j)
    			{
    				unsigned char* ptr = colorTexture->bits + 3 * (i * 640 + j);
    				*(ptr + 0) = line[4 * j + 2];
    				*(ptr + 1) = line[4 * j + 1];
    				*(ptr + 2) = line[4 * j + 0];
    			}
    		}
    		// Texturen an OpenGL senden und ausgeben 
    		TextureObject *tobj = colorTexture;
    		glBindTexture(GL_TEXTURE_2D, tobj->id);
    		glTexImage2D(GL_TEXTURE_2D, 0, tobj->internalFormat, tobj->width, tobj->height,
    			0, tobj->imageFormat, GL_UNSIGNED_BYTE, tobj->bits);
    	}
    	// Buffer wieder entsperren
    	nuiTexture->UnlockRect(0);
    
    } // updateFrameImage

    Und nachdem ich die ganzen Verbindungen eingerichtet habe, gebe ich die Distanz einfach bei der Berechnung der einzelnen Punkte des Skeletts aus. Somit wird der String nicht mehr 307200mal (640*480) sondern nur mehr 20mal erstellt, also genau soviel, wie Punke am Skelett definiert sind und voilà... es funktioniert :D :D :D

    void updateSkeletonData(NUI_SKELETON_DATA& data)
    {
    	POINT coordInDepth;
    	USHORT depth = 0;
    
    	// Alle Verbindungen durchlaufen
    	for (int i = 0; i < NUI_SKELETON_POSITION_COUNT; i++)
    	{
    		// Verbindungspositionen erhalten und
    		// in Tiefeninformationen umrechnen
    		NuiTransformSkeletonToDepthImage(data.SkeletonPositions[i],	
    						&coordInDepth.x,	
    						&coordInDepth.y,				
    						&depth,							
    						NUI_IMAGE_RESOLUTION_640x480);
    
    		
    		// Distanz in Millimeter am Bildschirm ausgeben
    		std::stringstream depthText;
    		depthText << "Distanz[mm]: " << NuiDepthPixelToDepth(depth) << std::endl;
    		Text = depthText.str();
    
    		// Alle Koordinaten in das Array abspeichern
    		skeletonVertices[i][0] = (GLfloat)coordInDepth.x / 640.0f;
    		skeletonVertices[i][1] = 1.0f - (GLfloat)coordInDepth.y / 480.0f;
    		skeletonVertices[i][2] = (GLfloat)NuiDepthPixelToDepth(depth) * 0.00025f;
    	}
    		
    } // updateSkeletonData

    Nochmal herzlichen Dank an Iso7 für die Unterstützung.

    Lg Epi

    • Als Antwort markiert Epiphany99 Freitag, 28. August 2015 10:45
    Freitag, 28. August 2015 10:41