Nowe posty

xx Problem ze sterownikami. (5)
2024-04-13, 21:25:16
xx Instalacja xfce4 (2)
2024-04-13, 16:20:17
xx Serie kompilacji bez instalacji dla “emerge” w Gentoo (2)
2024-04-08, 18:40:04
xx Plasma 6 w Neonie ssie trochę mniej ... (17)
2024-04-05, 10:03:46
xx Problem z Linux Lite po instalacji (3)
2024-04-03, 14:23:40
xx Jak właczyć num locka przy starcie systemu debian 12? (12)
2024-04-02, 17:43:54
xx Brak dźwieku w systemie. (5)
2024-04-02, 16:13:41
xx Dystrybucja pod HP Omen (7)
2024-03-29, 11:33:05
xx [Poradnik] Wyszukiwanie Sterowników (2)
2024-03-27, 21:08:23
xx Ile pingwinów? (1)
2024-03-27, 08:59:24

Autor Wątek: format obrazu firewire a video4linux  (Przeczytany 3087 razy)

msuma

  • Gość
format obrazu firewire a video4linux
« dnia: 2012-02-27, 19:27:11 »
Znalazłem program cvEyeTracker dzięki któremu można przy użyciu kamery zgodnej ze standardem firewire śledzić ruch źrenicy oka i obsługiwać komputer bez użycia rąk. Program powstał dla osób sparaliżowanych, które mogą poruszać tylko gałkami ocznymi. Nie mam kamery firewire więc chciałem zmodyfikować program tak, aby współpracował z kamerą Playstation 3 Eye. Kamera ta nadaje się doskonale do tego zastosowania - zalecają ją między innymi twórcy EyeWritera (pod Windows i Mac). Powstawiałem gdzie trzeba kod z przykładowego programu do obsługi urządzeń v4l o nazwie capturer oraz wykomentowałem fragmenty dotyczące przechwytywania z urządzeń standardu IEEE1394. Niestety wyświetlany obraz jest zniekształcony. Widzę dwa czaro-białe obrazy nałożone na siebie i przesunięte o połowę szerokości, jeden w lewo, drugi w prawo. Wydaje mi się że chodzi o różnicę w kodowaniu obrazu w urządzeniach firewire i w kamerach internetowych. Proszę o pomoc w szczególności osoby, którym znane są różnice w kodowaniu pojedynczych, nieskompresowanych klatek. Kamera PS3 Eye używa standardu YUYV, każda klatka zajmuje więc 640×480×2 bajtów. Problem może leżeć gdzieś w okolicach poniższej linijki. Kiedy zmieniam typ danych drugiego parametru funkcji wygląd obrazu nieco się zmienia.
memcpy(eye_image->imageData, (char*)buffers[0].start, monobytesperimage);
Kamerę wybieramy tutaj:
open_device (&fd, "/dev/video1");
/dev/video0 to kamerka wbudowana w mojego laptopa, /dev/video1 to PS3 Eye.
Kod źródłowy po moich przeróbkach w ostatnim poście.

Offline vanhelzing

  • Users
  • Prawie jak Guru
  • ****
  • Wiadomości: 314
    • Zobacz profil
format obrazu firewire a video4linux
« Odpowiedź #1 dnia: 2012-02-27, 20:42:50 »
Z tego, co widzę, program operuje na formacie YUV411, czyli 12 bitów na piksel zamiast YUV422 i 16 bitów na piksel. Stąd obraz Ci się rozjeżdża.

Powinieneś raczej zamienić funkcję FirewireFrame_to_RGBIplImage wywoływaną po pobraniu ramki, na konwersję z YUV422 na RGB24 zamiast obecnego YUV411 na RGB24.

msuma

  • Gość
format obrazu firewire a video4linux
« Odpowiedź #2 dnia: 2012-02-27, 22:04:02 »
Znalazłem takie specyfikacje obydwu formatów:V4L2_PIX_FMT_YUV420 i V4L2_PIX_FMT_YUYV

Będzie się to dało zrobić modyfikując ten fragment:
#define YUV2RGB(y, u, v, r, g, b)\\
  r = y + ((v*1436) >> 10);\\
  g = y - ((u*352 + v*731) >> 10);\\
  b = y + ((u*1814) >> 10);\\
  r = r < 0 ? 0 : r;\\
  g = g < 0 ? 0 : g;\\
  b = b < 0 ? 0 : b;\\
  r = r > 255 ? 255 : r;\\
  g = g > 255 ? 255 : g;\\
  b = b > 255 ? 255 : b
Czy będę też musiał coś zmieniać tutaj?
void uyyvyy2rgb (unsigned char *src, unsigned char *dest, unsigned long long int NumPixels)
{
  register int i = NumPixels + ( NumPixels >> 1 )-1;
  register int j = NumPixels + ( NumPixels << 1 )-1;
  register int y0, y1, y2, y3, u, v;
  register int r, g, b;

  while (i > 0) {
    y3 = (unsigned char) src[i--];
    y2 = (unsigned char) src[i--];
    v  = (unsigned char) src[i--] - 128;
    y1 = (unsigned char) src[i--];
    y0 = (unsigned char) src[i--];
    u  = (unsigned char) src[i--] - 128;
    YUV2RGB (y3, u, v, r, g, b);
    dest[j--] = r;
    dest[j--] = g;
    dest[j--] = b;
    YUV2RGB (y2, u, v, r, g, b);
    dest[j--] = r;
    dest[j--] = g;
    dest[j--] = b;
    YUV2RGB (y1, u, v, r, g, b);
    dest[j--] = r;
    dest[j--] = g;
    dest[j--] = b;
    YUV2RGB (y0, u, v, r, g, b);
    dest[j--] = r;
    dest[j--] = g;
    dest[j--] = b;
  }
}
Czy pierwszy dotyczy pojedynczego piksela, a drugi całej klatki czy jest to bardziej skomplikowane?

Offline vanhelzing

  • Users
  • Prawie jak Guru
  • ****
  • Wiadomości: 314
    • Zobacz profil
format obrazu firewire a video4linux
« Odpowiedź #3 dnia: 2012-02-27, 22:50:33 »
YUV2RGB to makro dotyczące konwersji wartości pojedynczego piksela. Będziesz musiał przerobić "uyyvyy2rgb".

Popatrz sobie na to:

http://paulbourke.net/dataformats/yuv/

W tej chwili masz 411, a musisz przekształcić na 422.

Musisz tylko upewnić się, że wcześniej masz zaalokowaną wystarczającą ilość pamięci w buforze, bo rozmiar ramki Ci się zwiększy.

msuma

  • Gość
format obrazu firewire a video4linux
« Odpowiedź #4 dnia: 2012-03-01, 16:02:31 »
Działa, dzięki!
/* YUV2 (YUYV, YUV422) to RGB */
typedef struct
{
  char red;
  char green;
  char blue;
//char alpha;
} artifact;
/* artifact is the name of structure, pixel will be the name of the variable */

artifact YUV_to_Bitmap(int y,int u,int v)
{
   int r,g,b;
   artifact pixel;

   pixel.red =   0;
   pixel.green = 0;
   pixel.blue =  0;
// pixel.alpha = 0;

   // u and v are +-0.5
   u -= 128;
   v -= 128;

   // Conversion
   r = y + 1.370705 * v;
   g = y - 0.698001 * v - 0.337633 * u;
   b = y + 1.732446 * u;
/*
   r = y + 1.402 * v;
   g = y - 0.344 * u - 0.714 * v;
   b = y + 1.772 * u;
*/

/*
   y -= 16;
   r = 1.164 * y + 1.596 * v;
   g = 1.164 * y - 0.392 * u - 0.813 * v;
   b = 1.164 * y + 2.017 * u;
*/
   // Clamp to <0, 1> range
   if (r < 0) r = 0;
   if (g < 0) g = 0;
   if (b < 0) b = 0;
   if (r > 255) r = 255;
   if (g > 255) g = 255;
   if (b > 255) b = 255;

   pixel.red =   r;
   pixel.green = g;
   pixel.blue =  b;
// pixel.alpha = 0;

   return(pixel);
}

void yuyv2rgb (unsigned char *src, unsigned char *dest, unsigned long long int NumPixels)
{
artifact rgb1;
artifact rgb2;
  /* Poprawic bo nieelegancko */
  // register int i = (NumPixels + ( NumPixels >> 1 ))*4/3-1;
  /* Juz lepiej */
  register int i = (NumPixels << 1 )-1;                 /* 614400 Bytes in 640×480 YUYV frame */
  register int j = NumPixels + ( NumPixels << 1 )-1;    /* 921600 Bytes in 640×480 RGB frame */
  int y1, y2, u, v;
  int r, g, b;

  while (i > 0)
  {
    u  = (unsigned char) src[i--];
    y1 = (unsigned char) src[i--];
    v  = (unsigned char) src[i--];
    y2 = (unsigned char) src[i--];

   rgb1 = YUV_to_Bitmap(y1,u,v);
    dest[j--] = rgb1.red;
    dest[j--] = rgb1.green;
    dest[j--] = rgb1.blue;
   rgb2 = YUV_to_Bitmap(y2,u,v);
    dest[j--] = rgb2.red;
    dest[j--] = rgb2.green;
    dest[j--] = rgb2.blue;
  }
}

void v4lFrame_to_RGBIplImage(void *FirewireFrame, IplImage *OpenCV_image)
{
  yuyv2rgb((unsigned char *)FirewireFrame, (unsigned char *)OpenCV_image->imageData, 640*480);
}
Przy okazji dopisałem funkcję konwertującą obraz w formacie YUYV (bezpośrednio, bez konwersji do RGB) do jednokanałowego obrazu w skali szarości. Funkcja pobiera ramkę w formacie YUYV, przeskakuje bajty zawierające informacje o kolorze, a zapisuje tylko te z informacją o jasności:
/* YUV2 (YUYV, YUV422) to grayscale */
void yuyv2grey(unsigned char *src, /*IplImage *OpenCV_image*/unsigned char *dest, unsigned long long int NumPixels)
{
  register int i = (NumPixels <<1)-1;   /* 614400 Bytes in 640×480 YUYV frame */
  register int j = NumPixels-1;         /* 307200 Bytes in 640×480 grayscale frame */

  while (i > 0)
  {
  /*u  - we are skipping this byte */ src[i--];
  /*y1 - we are copying this byte */ dest[j--] = (unsigned char) src[i--];
  /*v  - we are skipping this byte */ src[i--];
  /*y2 - we are copying this byte */ dest[j--] = (unsigned char) src[i--];
  }
}

void v4lFrame_to_grayscaleIplImage(void *FirewireFrame, IplImage *OpenCV_image)
{
  yuyv2grey((unsigned char *)FirewireFrame, (unsigned char *)OpenCV_image->imageData, 640*480);
}