admin Founder


Registrato: Oct 03, 2008 Messaggi: 261
|
Inviato: Mer Apr 15, 2009 12:01 am Oggetto: [C++] Rilevatore di contorni su frame video attraverso Sobel |
|
|
Vi posto qui un sorgente (ancora in fase iper-provvisoria, che verràin seguito espanso per un progetto più grande) che, facendo uso di Video4Linux2 per la gestione dei buffer video provenienti da webcam e delle librerie CImg per la gestione delle immagini, si interfaccia con la webcam e mostra i frame video in ingresso calcolando la corrispondente mappa di Sobel per ognuno di essi, in modo da visualizzare non l'immagine effettiva ma solo i suoi contorni. Esempio:
http://img113.imageshack.us/img113/2346/screen5hp4.png
Sorgente:
| Codice: |
/**
* Cam Sobeler using CImg and V4l2
* copyleft 2008, by BlackLight
*/
#include <sys/ioctl.h>
#include <linux/videodev2.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/mman.h>
#include <assert.h>
#include <sys/mman.h>
#include <CImg.h>
using namespace cimg_library;
#define DEV_DEFAULT "/dev/video0"
#define WIDTH 640
#define HEIGHT 480
#define DEPTH 2
#define CROP 0
#define OK "\t[ \033[92mOK\033[0m ]\n"
#define KO "\t[ \033[91mKO\033[0m ]\n"
#define CLEAR(x) memset(&x,0,sizeof(x))
#define ABS(x) ((x>=0) ? x : -x)
typedef unsigned char u8;
typedef unsigned short int u16;
inline u16 RGB(u8 r, u8 g, u8 b) {
return (((u16) r) << 16) | (((u16) g) << 8) | ((u16) b);
}
const float gx_matrix[3][3] = {
{-1,0,1},
{-2,0,2},
{-1,0,1}
};
const float gy_matrix[3][3] = {
{1,2,1},
{0,0,0},
{-1,-2,-1}
};
struct buffer {
void* start;
size_t length;
};
int fd;
struct buffer* buffers = NULL;
struct v4l2_requestbuffers req;
static int xioctl (int fd, int request, void* arg) {
int r;
do
r = ioctl (fd, request, arg);
while (-1 == r && EINTR == errno);
return r;
}
float sobel (CImg<u8> &img, int x, int y) {
float xg=0, yg=0;
int sx=0;
for (int mx=x; mx<x+3; mx++) {
int sy=0;
for (int my=y; my<y+3; my++) {
if (mx<img.dimx() && my<img.dimy()) {
u8 r = img(mx,my,0);
u8 g = img(mx,my,1);
u8 b = img(mx,my,2);
xg += (RGB(r,g,b) * gx_matrix[sx][sy]);
yg += (RGB(r,g,b) * gy_matrix[sx][sy]);
}
sy++;
}
sx++;
}
float res = ABS(xg) + ABS(yg);
return res;
}
void init_dev(char *DEV) {
struct v4l2_capability vcap;
struct v4l2_format format;
struct v4l2_cropcap cropcap;
struct v4l2_crop crop;
struct v4l2_format fmt;
struct stat st;
printf ("Testing if %s is a valid block device...\t",DEV);
fflush(stdout);
if (stat(DEV,&st)<0) {
fprintf (stderr,"%s\n\033[91m[-] Unable to identify %s: %s\n\n",KO,DEV,strerror(errno));
exit(EXIT_FAILURE);
}
if (!S_ISCHR(st.st_mode)) {
fprintf (stderr,"%s\n\033[91m[-] %s is not a valid device: %s\n\n",KO,DEV,strerror(errno));
exit(EXIT_FAILURE);
}
if ((fd=open(DEV, O_RDWR | O_NONBLOCK, 0))<0) {
fprintf (stderr,"%s\n\033[91m[-] Unable to open device %s: %s\n\n",KO,DEV,strerror(errno));
exit(EXIT_FAILURE);
}
printf (OK);
printf ("Testing if %s is compatible with V4L2...\t",DEV);
fflush(stdout);
if (xioctl(fd, VIDIOC_QUERYCAP, &vcap)<0) {
if (errno==EINVAL)
fprintf (stderr,"\t%s\033[91m[-] %s is not a valid V4L2 device: %s\n\n",KO,DEV,strerror(errno));
else
fprintf (stderr,"\t%s\033[91m[-] Error in VIDIOC_QUERYCAP: %s\n\n",KO,DEV,strerror(errno));
exit(EXIT_FAILURE);
}
printf (OK);
printf ("\n\033[1mDriver name: %s\n"
"Card name: %s\n"
"Bus info: %s\n\n",
vcap.driver, vcap.card, vcap.bus_info);
printf ("\033[0mTesting if %s is a valid device for capturing video...",DEV);
fflush(stdout);
if (!(vcap.capabilities & V4L2_CAP_VIDEO_CAPTURE)) {
fprintf (stderr,"%s\033[91m[-] %s is not a valid capture device\n\n",KO,DEV);
exit(EXIT_FAILURE);
}
printf (OK);
printf ("Testing if %s is compatible with streaming mode...",DEV);
fflush(stdout);
if (!(vcap.capabilities & V4L2_CAP_STREAMING)) {
fprintf(stderr,"%s\033[91m[-] V4L2_CAP_STREAMING mode unsupported on %s\n\n",KO,DEV);
exit(EXIT_FAILURE);
}
printf (OK);
#if CROP == 1
CLEAR(cropcap);
cropcap.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
printf ("Trying setting crop on %s...",DEV);
fflush(stdout);
if (!xioctl(fd, VIDIOC_CROPCAP, &cropcap)) {
crop.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
crop.c = cropcap.defrect;
if (xioctl(fd, VIDIOC_S_CROP, &crop)<0) {
if (errno == EINVAL) {
fprintf(stderr,"%s\033[91m[-] Cropping unsupported on %s\n\n",KO,DEV);
exit(EXIT_FAILURE);
}
}
}
#endif
CLEAR(fmt);
fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
fmt.fmt.pix.width = WIDTH;
fmt.fmt.pix.height = HEIGHT;
fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV;
fmt.fmt.pix.field = V4L2_FIELD_INTERLACED;
printf ("Setting output video format on %s...\t\t",DEV);
fflush(stdout);
if (ioctl(fd, VIDIOC_S_FMT, &fmt)<0) {
fprintf(stderr,"%s\033[91m[-] Unable to set video format on %s: %s\n\n",KO,DEV,strerror(errno));
exit(EXIT_FAILURE);
}
printf (OK);
printf ("Initializing mmap to gather frames...\t\t\t");
fflush(stdout);
CLEAR(req);
req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
req.memory = V4L2_MEMORY_MMAP;
req.count = 4;
if (xioctl(fd, VIDIOC_REQBUFS, &req)<0) {
if (errno == EINVAL)
fprintf(stderr,"%s\033[91m[-] %s does not support memory mapping\n\n",KO,DEV);
else
fprintf(stderr,"%s\033[91m[-] Error in VIDIOC_REQBUFS on %s\n\n",KO,DEV);
exit(EXIT_FAILURE);
}
printf (OK);
if (req.count<2) {
fprintf (stderr,"%s\033[91m[-] Not enough buffer memory on %s\n\n",KO,DEV);
exit(EXIT_FAILURE);
}
if (!(buffers = (struct buffer*) calloc(req.count, sizeof(*buffers)))) {
fprintf (stderr,"%s\033[91m[-] Not enough memory to allocate video buffers %s\n\n",KO,DEV);
exit(EXIT_FAILURE);
}
}
int main (int argc, char **argv) {
unsigned int n_buff;
unsigned int count = 100;
char *DEV;
struct v4l2_buffer buff;
enum v4l2_buf_type type;
u8 *map;
printf ("\033[1m*** Cam grabber by BlackLight, copyleft 2008 ***\n\n\033[0m");
if (!argv[1]) {
printf ("\033[01;93m[*] Warning: no device specified. Assuming default %s...\033[0m\n\n",DEV_DEFAULT);
DEV = strdup(DEV_DEFAULT);
} else
DEV = strdup(argv[1]);
init_dev(DEV);
printf ("Querying buffers from %s and starting mmap...\t",DEV);
fflush(stdout);
for (n_buff=0; n_buff < req.count; n_buff++) {
CLEAR(buff);
buff.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
buff.memory = V4L2_MEMORY_MMAP;
buff.index = n_buff;
if (xioctl(fd, VIDIOC_QUERYBUF, &buff)<0) {
fprintf (stderr,"%s\033[91m[-] Error while querying buffers from %s\n\n",KO,DEV);
exit(EXIT_FAILURE);
}
buffers[n_buff].length = buff.length;
buffers[n_buff].start = (u8*) mmap(NULL, buff.length, PROT_READ | PROT_WRITE, MAP_SHARED, fd, buff.m.offset);
if (buffers[n_buff].start == MAP_FAILED) {
fprintf (stderr,"%s\033[91m[-] Error while starting mmap\n\n",KO);
exit(EXIT_FAILURE);
}
}
printf (OK);
printf ("Starting capturing on device %s...\t\t",DEV);
fflush(stdout);
for (int i=0; i < n_buff; i++) {
CLEAR(buff);
buff.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
buff.memory = V4L2_MEMORY_MMAP;
buff.index = i;
if (xioctl(fd, VIDIOC_QBUF, &buff)<0) {
fprintf (stderr,"%s\033[91m[-] Error in VIDIOC_QBUF\n\n",KO);
exit(EXIT_FAILURE);
}
}
type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
if (xioctl(fd, VIDIOC_STREAMON, &type)<0) {
fprintf (stderr,"%s\033[91m[-] Error in VIDIOC_STREAMON\n\n",KO);
exit(EXIT_FAILURE);
}
printf (OK);
printf ("Reading frames from device %s...\t\t",DEV);
fflush(stdout);
CImg<u8> frame(WIDTH,HEIGHT,1,DEPTH);
frame.fill(0);
CImgDisplay disp(frame);
while (!disp.is_closed) {
while (1) {
fd_set fds;
struct timeval tv;
int ret;
FD_ZERO(&fds);
FD_SET(fd, &fds);
tv.tv_sec=2;
tv.tv_usec=0;
if ((ret = select(fd+1, &fds, NULL, NULL, &tv))<0) {
if (errno == EINTR)
continue;
else {
fprintf (stderr,"%s\033[91m[-] Fatal error in select()\n\n",KO);
exit(EXIT_FAILURE);
}
}
if (!ret) {
fprintf (stderr,"%s\033[91m[-] select() timeout\n\n",KO);
exit(EXIT_FAILURE);
}
CLEAR(buff);
buff.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
buff.memory = V4L2_MEMORY_MMAP;
if (xioctl(fd, VIDIOC_DQBUF, &buff)<0) {
if (errno == EAGAIN)
continue;
if (errno == EIO) {}
else {
fprintf (stderr,"%s\033[91m[-] Error while getting frames from the device: %s\n\n",KO,strerror(errno));
exit(EXIT_FAILURE);
}
}
assert(buff.index < n_buff);
u8* mem = (u8*) buffers[0].start;
u8 white[3] = { 0xFF, 0xFF, 0xFF };
u8 black[3] = { 0x00, 0x00, 0x00 };
frame = CImg<u8>(mem, 640, 480);
/*for (int x=0; x<WIDTH; x++)
for (int y=0; y<HEIGHT; y++) {
*frame.ptr(x,y,0) = mem[(y*WIDTH + x) * DEPTH + 0];
*frame.ptr(x,y,1) = mem[(y*WIDTH + x) * DEPTH + 1];
*frame.ptr(x,y,2) = mem[(y*WIDTH + x) * DEPTH + 2];
}*/
for (int x=0; x<WIDTH; x++)
for (int y=0; y<HEIGHT; y++) {
if (sobel(frame,x,y)>12000)
frame.draw_point(x,y,white);
else
frame.draw_point(x,y,black);
}
disp.display(frame).resize();
if (xioctl(fd, VIDIOC_QBUF, &buff)<0) {
fprintf (stderr,"%s\033[91m[-] Error on VIDIOC_QBUF: %s\n\n",KO,strerror(errno));
exit(EXIT_FAILURE);
}
break;
}
}
printf (OK);
close(fd);
printf ("\n\033[92m[+] Cap OK\n\n");
return EXIT_SUCCESS;
} |
Per la compilazione (dopo aver ovviamente installato le librerie CImg):
| Codice: | | g++ -o cap cap.cpp -lm -lX11 -lpthread |
p.s. Posso notevolmente perfezionarlo. Innanzitutto usa una mappatura dei colori YUV con depth=2, dato che è l'unica disponibile per il driver della mia cam merdosa (si, RGB24 non funziona), e quindi il risultato potrebbe non essere molto accurato su alcuni frame. Inoltre succhia un bel po' in termini di prestazioni, ma con un algoritmo complesso come quello di Sobel non posso fare altrimenti, se non calcolare la mappa di ogni singolo frame, e questo ovviamente ne riduce le prestazioni complessive. |
|