Başlangıç > Donanım/Yazılım > Video 4 Linux 2 (v4l2) Bölüm 1 – Fotoğraf çekmek

Video 4 Linux 2 (v4l2) Bölüm 1 – Fotoğraf çekmek

Giriş bölümünde de bahsetiğim gibi video formatımız YUV olacağından biraz bu formatı açıklayayım.

YUV, parlaklık (Luminance – Y) ve renk (Chroma – UV) bilgisinden oluşan bir renk sistemidir. Renkli televizyon yayınlarının siyah beyaz sistemler tarafından da izlenebilmesi için tasarlanmıştır. Yani Y bileşeni görüntünün siyah beyaz kısmını oluşturur. Buna ek olarak renk bilgisini içeren chroma sinyali buna eklenmiştir. Bir görüntünün bütün bileşenlerinin (RGB – Kırmızı Yeşil Mavi) alıcıya gönderilmesi yüksek band genişliği sorunuyla birlikte geldiğinden, renk bilgisini içeren UV bileşeni band genişliği nedeniyle aslında kayıplı bir sıkıştırma yöntemi olarak tasarlanmıştır. Esasen renk bilgisi kırmızı ve mavinin farkından oluşur. Kabaca, U maviden yeşile, V de yeşilden kırmızıya değişen bir değerdir ve ayrıca bu sinyaller işaretli değerlerdir. Y ise işaretsizdir. YUV bazen YCbCr olarak da adlandırılsa da YCbCr bu formatın işaretsiz halidir. Bu yüzden bazı kameralarda çıkış YUV denmesine rağmen aslında çıkış YCbCr dır. (OV5640 gibi)

YUV veya YCbCr bir renk sistemi olduğundan, insan gözünün anlayabildiği tek renk sistemi RGB ye geçişte veya RGB den YUV ye geçişte her bir bileşenin yeniden hesaplanması gerekir. Aşşağıda bu denklemler verilmiştir.

YUV (YCbCr) den RGB ye

yuv2rgb

RGB den YUV (YCbCr) ye

rgb2yuv

Yukarıda verilen tüm çevrim denklemleri 8 bit için geçerlidir ve her bir bileşen 0 ila 255 arasında bir değer alır. Her hangi bir değerin 0 ın altında veya 255 in üzerinde çıkması muhtemeldir. Böyle bir durumda alt limit 0, üst limit ise 255 seçilmelidir.

Aslında denklemlere bakıldığında bazı şeylerin YUV sisteminde yapılmasının daha kolay olduğu aşikardır ki günümüzde bile hala kameraların bu sistemleri desteklemesi de bu yüzdendir. Mesela YUV sisteminde parlaklık ayarı için sadece Y bileşeni ile oynamak yeterlidir veya beyaz dengesi için U ve V bileşenlerini birbirlerine eşitlemek yeterlidir. RGB sistemi için işler bu kadar kolay değildir.

İnsan gözü renkden ziyade parlaklığa daha duyarlı olduğundan ve sınırlı band genişliği sorunlarından dolayı, renk bilgisini daha düşük seviyede yeniden örneklenmiştir. Aşağıda farklı YUV formatları gösterilmiştir.

yuvfmt

YUV444 herkesin aşina olduğu kayıpsız RGB formatı gibidir. Her bir bileşen her piksel için birkez örneklenir. YUV422 formatında Y her pikselde birkez örneklenirken, renk bilgisi her iki pikselde bir kez örneklenir.  YUV420 da ise renk bilgisi her dört pikselde birkez örneklenir ki böyle resmin çıktısı renk bakımından oldukça fakirdir.

Dijital ortamda YUV422 resmin (kullanacağım format) data formatı yine adına uygun şekilde önce resmin boyutu kadar Y bileşeni, boyutun yarısı kadar da U ve V bileşenlerinden oluşur. Örneğin resim 640×480 boyutunda ise 307200 bayt (640*480) Y bileşeni (siyah-beyaz kısım) yine 307200 bayt da renk bilgisidir (UV). Fakat renk bilgisinin yarısı U bileşenine (153600 bayt) kalan kısmı ise (153600 bayt) diğer bileşen olan V bileşenine aittir. Yani toplamda parlaklık ve renk bilgisi olarak data uzunluğu 614400 bayttır. Bu durumda resim formatını RGB (640*480*3 byte = 921600  byte) ile kıyaslarsak yaklaşık %33 civarında  bir sıkıştırma söz konusudur.

Aşşağıda 640×480 boyutlarında YUV422 formatında bir görüntünün 640×960 (tam boyutlu YUV422) boyutlarında çıktısı görülmektedir.

image_all

Dikkat edilirse U ve V, Y bileşeninin yarısı kadar örneklendiğinden siyah beyaz görüntünün hemen altında dört adet küçük boyutta resimcikler oluşmuştur. Y bileşeninden hemen sonra U bileşeni ve daha sonra V bileşeni geldiğinden resimciklerin ikisi aynı tonlara sahiptir. Fakat bu resimcikler aslında renk bilgisidir.

Yukarıda YUV den RGB ye dönüşüm için verilen denklemi kullanarak YUV422 formatındaki resmi RGB ye çevirirsek sonuç aşşağıdaki gibi olacaktır.

imageRGB

 

Ayrıca resmin siyah beyaz kısmıda görüntü işleme işlemi için rahatlıkla kullanılabilir.

image_bw

Gerekli program aşşağıda verilmiştir.

capture.c

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <errno.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/time.h>
#include <sys/mman.h>
#include 	<linux/videodev2.h>
#include 	<libv4l2.h></pre>
#include "bmp.h"

#define CLEAR(x) memset(&(x), 0, sizeof(x))

struct buffer {
void *start;
size_t length;
};

static void xioctl(int fh, int request, void *arg)
{
int r;

do {
r = v4l2_ioctl(fh, request, arg);
} while (r == -1 && ((errno == EINTR) || (errno == EAGAIN)));

if (r == -1) {
fprintf(stderr, "error %d, %s\n", errno, strerror(errno));
exit(EXIT_FAILURE);
}
}

#define IMAGE_WIDTH 640 //1280
#define IMAGE_HEIGHT 480 //960
#define OUTPUT_FORMAT V4L2_PIX_FMT_YUV422P

int main(int argc, char **argv)
{
struct v4l2_format VideoFormat;
struct v4l2_buffer VideoBuffer;
struct v4l2_requestbuffers RequestedVideoBuffers;
enum v4l2_buf_type BufferType;
fd_set fds;
struct timeval tv;
int r, VideoDeviceHandle = -1;
unsigned int i, NumberOfBuffers;
char *dev_name = "/dev/video0";
char out_name[256];
FILE *fout;
struct buffer *Buffers;

VideoDeviceHandle = v4l2_open(dev_name, O_RDWR | O_NONBLOCK, 0);
if (VideoDeviceHandle < 0) {
perror("Cannot open device");
exit(EXIT_FAILURE);
}

CLEAR(VideoFormat);
VideoFormat.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
VideoFormat.fmt.pix.width = IMAGE_WIDTH;
VideoFormat.fmt.pix.height = IMAGE_HEIGHT;
VideoFormat.fmt.pix.pixelformat = OUTPUT_FORMAT;
VideoFormat.fmt.pix.field = V4L2_FIELD_INTERLACED;
xioctl(VideoDeviceHandle, VIDIOC_S_FMT, &VideoFormat);
if (VideoFormat.fmt.pix.pixelformat != OUTPUT_FORMAT) {
printf("Unsupported video format \n");
exit(EXIT_FAILURE);
}
if ((VideoFormat.fmt.pix.width != IMAGE_WIDTH) || (VideoFormat.fmt.pix.height != IMAGE_HEIGHT))
printf("Warning: driver is sending image at %dx%d\n", VideoFormat.fmt.pix.width, VideoFormat.fmt.pix.height);

//========= Print settings
printf(" type %d\n width %d\n height %d\n format %d\n field %d\n",
VideoFormat.type,
VideoFormat.fmt.pix.width,
VideoFormat.fmt.pix.height,
VideoFormat.fmt.pix.pixelformat,
VideoFormat.fmt.pix.field);
//=========

CLEAR(RequestedVideoBuffers);
RequestedVideoBuffers.count = 2;
RequestedVideoBuffers.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
RequestedVideoBuffers.memory = V4L2_MEMORY_MMAP;
xioctl(VideoDeviceHandle, VIDIOC_REQBUFS, &RequestedVideoBuffers);

Buffers = calloc(RequestedVideoBuffers.count, sizeof(*Buffers));
for (NumberOfBuffers = 0; NumberOfBuffers < RequestedVideoBuffers.count; ++NumberOfBuffers) {
CLEAR(VideoBuffer);

VideoBuffer.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
VideoBuffer.memory = V4L2_MEMORY_MMAP;
VideoBuffer.index = NumberOfBuffers;

xioctl(VideoDeviceHandle, VIDIOC_QUERYBUF, &VideoBuffer);

Buffers[NumberOfBuffers].length = VideoBuffer.length;
Buffers[NumberOfBuffers].start = v4l2_mmap(NULL, VideoBuffer.length, PROT_READ | PROT_WRITE, MAP_SHARED, VideoDeviceHandle, VideoBuffer.m.offset);

if (MAP_FAILED == Buffers[NumberOfBuffers].start) {
perror("mmap");
exit(EXIT_FAILURE);
}
}

for (i = 0; i < NumberOfBuffers; ++i) {
CLEAR(VideoBuffer);
VideoBuffer.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
VideoBuffer.memory = V4L2_MEMORY_MMAP;
VideoBuffer.index = i;
xioctl(VideoDeviceHandle, VIDIOC_QBUF, &VideoBuffer);
}
BufferType = V4L2_BUF_TYPE_VIDEO_CAPTURE;

xioctl(VideoDeviceHandle, VIDIOC_STREAMON, &BufferType);
for (i = 0; i < 1; i++) {
do {
FD_ZERO(&fds);
FD_SET(VideoDeviceHandle, &fds);

/* Timeout. */
tv.tv_sec = 2;
tv.tv_usec = 0;

r = select(VideoDeviceHandle + 1, &fds, NULL, NULL, &tv);
} while ((r == -1 && (errno = EINTR)));
if (r == -1) {
perror("select");
return errno;
}

CLEAR(VideoBuffer);
VideoBuffer.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
VideoBuffer.memory = V4L2_MEMORY_MMAP;
xioctl(VideoDeviceHandle, VIDIOC_DQBUF, &VideoBuffer);

vSaveLuminanceAsBMP("image_bw.bmp", IMAGE_WIDTH, IMAGE_HEIGHT, Buffers[VideoBuffer.index].start);
vSaveLuminanceAsBMP("image_all.bmp", IMAGE_WIDTH, 2*IMAGE_HEIGHT, Buffers[VideoBuffer.index].start);
vSaveYuvAsBMP("imageRGB.bmp", IMAGE_WIDTH, IMAGE_HEIGHT, Buffers[VideoBuffer.index].start);

xioctl(VideoDeviceHandle, VIDIOC_QBUF, &VideoBuffer);
}

BufferType = V4L2_BUF_TYPE_VIDEO_CAPTURE;
xioctl(VideoDeviceHandle, VIDIOC_STREAMOFF, &BufferType);
for (i = 0; i < NumberOfBuffers; ++i)
v4l2_munmap(Buffers[i].start, Buffers[i].length);
v4l2_close(VideoDeviceHandle);

return 0;
}

Program şu şekilde çalışmaktadır; Video formatı ve gerekli bellek ayarlandıktan sonra kamera açılır. Sadece bir adet video karesi alınarak bu karedeki YUV formatından siyah-beyaz ve renkli görüntü elde edilip bmp formatında kaydedilir. Kayıt sonunda kamera kapatılır ve program sonlandırılır.

Programı derlemek için

gcc capture.c bmp.c -o capture -lv4l2

bmp.c ve bmp.h dosyalarını bölüm 0 da bulabilirsiniz.

Devam edecek…

Reklamlar
Kategoriler:Donanım/Yazılım
  1. Henüz yorum yapılmamış.
  1. No trackbacks yet.

Bir Cevap Yazın

Aşağıya bilgilerinizi girin veya oturum açmak için bir simgeye tıklayın:

WordPress.com Logosu

WordPress.com hesabınızı kullanarak yorum yapıyorsunuz. Çıkış  Yap / Değiştir )

Twitter resmi

Twitter hesabınızı kullanarak yorum yapıyorsunuz. Çıkış  Yap / Değiştir )

Facebook fotoğrafı

Facebook hesabınızı kullanarak yorum yapıyorsunuz. Çıkış  Yap / Değiştir )

Google+ fotoğrafı

Google+ hesabınızı kullanarak yorum yapıyorsunuz. Çıkış  Yap / Değiştir )

Connecting to %s