Spontaneous Projects is a chronicle of all of my projects, a reference for me, and a way to prevent others from making the same mistakes.
For the first part of migrating to using the netbook as a computer vision device, I need to get an image from it.
I used a program called uvccapture as a basis, and modified it heavily, so that it runs in YUY mode all of the time, and has a lot less options, as I plan to use a modified version of this to capture for actual processing later.
The following was developed for a EeePC 701sd, running lucid puppy and compiled using g++ and a make file, the name is still the original, as I haven't fully got my head around how the makefiles work yet, so I kept it simple.
 |
Lucid Puppy, (right) an older image showing both lack of centering and bad colours
(left) a recent pic, taken at the full res of the camera, in worse lighting, but with 1 second for the camera to adjust |
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <jpeglib.h>
#include <time.h>
#include <linux/videodev.h>
#include "v4l2uvc.h"
static const char version[] = VERSION;
int run = 1;
void
sigcatch (int sig)
{
fprintf (stderr, "Exiting...\n");
run = 0;
}
void
usage (void)
{
fprintf (stderr, "uvccapture version %s\n", version);
fprintf (stderr, "Usage is: uvccapture [options]\n");
fprintf (stderr, "Options:\n");
//fprintf (stderr, "-v\t\tVerbose\n");
//fprintf (stderr, "-o<filename>\tOutput filename(default: snap.jpg)\n");
//fprintf (stderr, "-d<device>\tV4L2 Device(default: /dev/video0)\n");
//fprintf (stderr,
//"-x<width>\tImage Width(must be supported by device)(>960 activates YUYV capture)\n");
//fprintf (stderr,
//"-y<height>\tImage Height(must be supported by device)(>720 activates YUYV capture)\n");
//fprintf (stderr,
//"-c<command>\tCommand to run after each image capture(executed as <command> <output_filename>)\n");
//fprintf (stderr,
//"-t<integer>\tTake continuous shots with <integer> seconds between them (0 for single shot)\n");
//fprintf (stderr,
//"-q<percentage>\tJPEG Quality Compression Level (activates YUYV capture)\n");
//fprintf (stderr, "-r\t\tUse read instead of mmap for image capture\n");
//fprintf (stderr,
//"-w\t\tWait for capture command to finish before starting next capture\n");
fprintf (stderr, "-m\t\tToggles capture mode to YUYV capture\n");
//fprintf (stderr, "Camera Settings:\n");
//fprintf (stderr, "-B<integer>\tBrightness\n");
//fprintf (stderr, "-C<integer>\tContrast\n");
//fprintf (stderr, "-S<integer>\tSaturation\n");
//fprintf (stderr, "-G<integer>\tGain\n");
exit (8);
}
int
spawn (char *argv[], int wait, int verbose)
{
pid_t pid;
int rv;
switch (pid = fork ()) {
case -1:
return -1;
case 0:
// CHILD
execvp (argv[0], argv);
fprintf (stderr, "Error executing command '%s'\n", argv[0]);
exit (1);
default:
// PARENT
if (wait == 1)
waitpid (pid, &rv, 0);
else {
// Clean zombies
waitpid (-1, &rv, WNOHANG);
rv = 0;
}
break;
}
return rv;
}
int
compress_yuyv_to_jpeg (struct vdIn *vd, FILE * file, int quality)
{
struct jpeg_compress_struct cinfo;
struct jpeg_error_mgr jerr;
JSAMPROW row_pointer[1];
unsigned char *line_buffer, *yuyv;
int z;
sleep(1);
line_buffer = calloc (vd->width * 3, 1);
yuyv = vd->framebuffer;
cinfo.err = jpeg_std_error (&jerr);
jpeg_create_compress (&cinfo);
jpeg_stdio_dest (&cinfo, file);
cinfo.image_width = vd->width;
cinfo.image_height = vd->height;
cinfo.input_components = 3;
cinfo.in_color_space = JCS_RGB;
jpeg_set_defaults (&cinfo);
jpeg_set_quality (&cinfo, quality, TRUE);
jpeg_start_compress (&cinfo, TRUE);
z = 0;
while (cinfo.next_scanline < cinfo.image_height) {
int x;
unsigned char *ptr = line_buffer;
for (x = 0; x < vd->width; x++) {
int r, g, b;
int y, u, v;
if (!z)
y = yuyv[0] << 8;
else
y = yuyv[2] << 8;
u = yuyv[1] - 128;
v = yuyv[3] - 128;
r = (y + (359 * v)) >> 8;
g = (y - (88 * u) - (183 * v)) >> 8;
b = (y + (454 * u)) >> 8;
*(ptr++) = (r > 255) ? 255 : ((r < 0) ? 0 : r);
*(ptr++) = (g > 255) ? 255 : ((g < 0) ? 0 : g);
*(ptr++) = (b > 255) ? 255 : ((b < 0) ? 0 : b);
if (z++) {
z = 0;
yuyv += 4;
}
}
row_pointer[0] = line_buffer;
jpeg_write_scanlines (&cinfo, row_pointer, 1);
}
jpeg_finish_compress (&cinfo);
jpeg_destroy_compress (&cinfo);
free (line_buffer);
return (0);
}
int
main (int argc, char *argv[])
{
char *videodevice = "/dev/video0";
char *outputfile = "new.jpg";
//int format = V4L2_PIX_FMT_MJPEG; // OR
int format = V4L2_PIX_FMT_YUYV;
int grabmethod = 1;
int width = 640;
int height = 480;
int brightness = 0, contrast = 0, saturation = 0;
int delay = 0; // increased, may sort out the wrap issues, results in other issues, but fixes colours
int quality = 95;
time_t ref_time;
struct vdIn *videoIn;
FILE *file;
(void) signal (SIGINT, sigcatch);
(void) signal (SIGQUIT, sigcatch);
(void) signal (SIGKILL, sigcatch);
(void) signal (SIGTERM, sigcatch);
(void) signal (SIGABRT, sigcatch);
(void) signal (SIGTRAP, sigcatch);
//Options Parsing (FIXME)
while ((argc > 1) && (argv[1][0] == '-')) {
switch (argv[1][1]) {
case 'm':
format = V4L2_PIX_FMT_YUYV;
break;
default: // this an unknown was entered case, nb -h is that
fprintf (stderr, "Unknown option %s \n", argv[1]);
format = V4L2_PIX_FMT_YUYV; // added here
usage ();
}
++argv;
--argc;
}
videoIn = (struct vdIn *) calloc (1, sizeof (struct vdIn));
if (init_videoIn
(videoIn, (char *) videodevice, width, height, format, grabmethod) < 0)
exit (1);
//Reset all camera controls
v4l2ResetControl (videoIn, V4L2_CID_BRIGHTNESS);
v4l2ResetControl (videoIn, V4L2_CID_CONTRAST);
v4l2ResetControl (videoIn, V4L2_CID_SATURATION);
//v4l2ResetControl (videoIn, V4L2_CID_GAIN);
//Setup Camera Parameters
if (brightness != 0)
v4l2SetControl (videoIn, V4L2_CID_BRIGHTNESS, brightness);
if (contrast != 0)
v4l2SetControl (videoIn, V4L2_CID_CONTRAST, contrast);
if (saturation != 0)
v4l2SetControl (videoIn, V4L2_CID_SATURATION, saturation);
//if (gain != 0) // It appears that gain does not exist, in this format, on this camera... who knows?
//v4l2SetControl (videoIn, V4L2_CID_GAIN, gain);
ref_time = time (NULL);
while (run) {
if (uvcGrab (videoIn) < 0) {
fprintf (stderr, "Error grabbing\n");
close_v4l2 (videoIn);
free (videoIn);
exit (1);
}
if ((difftime (time (NULL), ref_time) > delay) || delay == 0) {
file = fopen (outputfile, "wb");
if (file != NULL) {
switch (videoIn->formatIn) {
case V4L2_PIX_FMT_YUYV:
compress_yuyv_to_jpeg (videoIn, file, quality);
break;
default:
fwrite (videoIn->tmpbuffer, videoIn->buf.bytesused + DHT_SIZE, 1,
file);
break;
}
fclose (file);
videoIn->getPict = 0;
}
ref_time = time (NULL);
}
if (delay == 0)
break;
}
close_v4l2 (videoIn);
free (videoIn);
return 0;
}
Some Notes:
- uvccapture uses some strange settings when initialising the camera, if there is not a delay to allow the camera to choose it's own settings it can be out of alignment, invert the colours, or be over/under saturated.
- The gain function doesn't seem to exist for this setup, and throws an error 22 bug, once I commented out those lines it fixed itself. I'm not sure if it's hardware, software, or something else
- There is likely still an amount of this that could be cut down, but until I know how I need the images to use them it will stay as it is.
 |
Early images, note the wrapping issue, and red appears blue |
 |
Recent image, with these issues corrected |