# Bazaar merge directive format 2 (Bazaar 0.90) # revision_id: pasky@zkamenelina-20110519001254-xke6vcnv0ns96ahh # target_branch: http://repo.joe.cz/brmbot/ # testament_sha1: 80704bc2a7d723a5fff18d0977b9cf6cacead76a # timestamp: 2011-05-19 02:29:06 +0200 # base_revision_id: joe@joe.cz-20110510162148-xhnsltv7blaebgm6 # # Begin patch === modified file 'src/apc.c' --- src/apc.c 2010-09-18 12:20:07 +0000 +++ src/apc.c 2011-05-19 00:12:54 +0000 @@ -43,8 +43,10 @@ static int fd = -1; struct buffer *buffers; static unsigned int n_buffers; -static int out_buf; static int force_format; +static FILE *dump_raw, *dump_proc; +static FILE *read_raw; +extern int square_thres; static int frame_count = 70; struct v4l2_format fmt; @@ -65,7 +67,18 @@ return r; } -void process_image(const void *p, int size, struct v4l2_format *fmt); +void process_image(void *p, int size, struct v4l2_format *fmt); + +static void single_frame(void *p, int size, struct v4l2_format *fmt) +{ + if (dump_raw) { + fwrite(p, size, 1, dump_raw); + } + process_image(p, size, fmt); + if (dump_proc) { + fwrite(p, size, 1, dump_proc); + } +} static int read_frame(void) { @@ -89,7 +102,7 @@ } } - process_image(buffers[0].start, buffers[0].length, &fmt); + single_frame(buffers[0].start, buffers[0].length, &fmt); break; case IO_METHOD_MMAP: @@ -115,7 +128,7 @@ assert(buf.index < n_buffers); - process_image(buffers[buf.index].start, buf.bytesused, &fmt); + single_frame(buffers[buf.index].start, buf.bytesused, &fmt); if (-1 == xioctl(fd, VIDIOC_QBUF, &buf)) errno_exit("VIDIOC_QBUF"); @@ -149,7 +162,7 @@ assert(i < n_buffers); - process_image((void *)buf.m.userptr, buf.bytesused, &fmt); + single_frame((void *)buf.m.userptr, buf.bytesused, &fmt); if (-1 == xioctl(fd, VIDIOC_QBUF, &buf)) errno_exit("VIDIOC_QBUF"); @@ -409,66 +422,68 @@ struct v4l2_crop crop; unsigned int min; - if (-1 == xioctl(fd, VIDIOC_QUERYCAP, &cap)) { - if (EINVAL == errno) { - fprintf(stderr, "%s is no V4L2 device\n", - dev_name); - exit(EXIT_FAILURE); - } else { - errno_exit("VIDIOC_QUERYCAP"); - } - } - - if (!(cap.capabilities & V4L2_CAP_VIDEO_CAPTURE)) { - fprintf(stderr, "%s is no video capture device\n", - dev_name); - exit(EXIT_FAILURE); - } - - switch (io) { - case IO_METHOD_READ: - if (!(cap.capabilities & V4L2_CAP_READWRITE)) { - fprintf(stderr, "%s does not support read i/o\n", - dev_name); - exit(EXIT_FAILURE); - } - break; - - case IO_METHOD_MMAP: - case IO_METHOD_USERPTR: - if (!(cap.capabilities & V4L2_CAP_STREAMING)) { - fprintf(stderr, "%s does not support streaming i/o\n", - dev_name); - exit(EXIT_FAILURE); - } - break; - } - - - /* Select video input, video standard and tune here. */ - - - CLEAR(cropcap); - - cropcap.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - - if (0 == xioctl(fd, VIDIOC_CROPCAP, &cropcap)) { - crop.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - crop.c = cropcap.defrect; /* reset to default */ - - if (-1 == xioctl(fd, VIDIOC_S_CROP, &crop)) { - switch (errno) { - case EINVAL: - /* Cropping not supported. */ - break; - default: - /* Errors ignored. */ - break; - } - } - } else { - /* Errors ignored. */ - } + if (-1 != fd) { + if (-1 == xioctl(fd, VIDIOC_QUERYCAP, &cap)) { + if (EINVAL == errno) { + fprintf(stderr, "%s is no V4L2 device\n", + dev_name); + exit(EXIT_FAILURE); + } else { + errno_exit("VIDIOC_QUERYCAP"); + } + } + + if (!(cap.capabilities & V4L2_CAP_VIDEO_CAPTURE)) { + fprintf(stderr, "%s is no video capture device\n", + dev_name); + exit(EXIT_FAILURE); + } + + switch (io) { + case IO_METHOD_READ: + if (!(cap.capabilities & V4L2_CAP_READWRITE)) { + fprintf(stderr, "%s does not support read i/o\n", + dev_name); + exit(EXIT_FAILURE); + } + break; + + case IO_METHOD_MMAP: + case IO_METHOD_USERPTR: + if (!(cap.capabilities & V4L2_CAP_STREAMING)) { + fprintf(stderr, "%s does not support streaming i/o\n", + dev_name); + exit(EXIT_FAILURE); + } + break; + } + + + /* Select video input, video standard and tune here. */ + + + CLEAR(cropcap); + + cropcap.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + + if (0 == xioctl(fd, VIDIOC_CROPCAP, &cropcap)) { + crop.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + crop.c = cropcap.defrect; /* reset to default */ + + if (-1 == xioctl(fd, VIDIOC_S_CROP, &crop)) { + switch (errno) { + case EINVAL: + /* Cropping not supported. */ + break; + default: + /* Errors ignored. */ + break; + } + } + } else { + /* Errors ignored. */ + } + } CLEAR(fmt); @@ -480,7 +495,7 @@ fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV; fmt.fmt.pix.field = V4L2_FIELD_INTERLACED; - if (-1 == xioctl(fd, VIDIOC_S_FMT, &fmt)) + if (-1 != fd && -1 == xioctl(fd, VIDIOC_S_FMT, &fmt)) errno_exit("VIDIOC_S_FMT"); /* Note VIDIOC_S_FMT may change width and height. */ @@ -556,14 +571,18 @@ "-m | --mmap Use memory mapped buffers [default]\n" "-r | --read Use read() calls\n" "-u | --userp Use application allocated buffers\n" - "-o | --output Outputs stream to stdout\n" + "-o | --output file Outputs raw stream to given file\n" + "-O | --outprc file Outputs processed stream to given file\n" + "-i | --input file Use raw stream instead of device\n" + " Overrides -i, implies -f -r\n" "-f | --format Force format to 640x480 YUYV\n" + "-s | --sqthres num Assessment value threshold for square coloring\n" "-c | --count Number of frames to grab [%i]\n" "", argv[0], dev_name, frame_count); } -static const char short_options[] = "d:hmruofc:"; +static const char short_options[] = "d:hmruo:i:fc:"; static const struct option long_options[] = { @@ -572,8 +591,11 @@ { "mmap", no_argument, NULL, 'm' }, { "read", no_argument, NULL, 'r' }, { "userp", no_argument, NULL, 'u' }, - { "output", no_argument, NULL, 'o' }, + { "output", required_argument, NULL, 'o' }, + { "outprc", required_argument, NULL, 'O' }, + { "input", required_argument, NULL, 'i' }, { "format", no_argument, NULL, 'f' }, + { "sqthres",required_argument, NULL, 's' }, { "count", required_argument, NULL, 'c' }, { 0, 0, 0, 0 } }; @@ -617,13 +639,38 @@ break; case 'o': - out_buf++; + dump_raw = fopen(optarg, "wb"); + if (!dump_raw) + perror(optarg); + break; + + case 'O': + dump_proc = fopen(optarg, "wb"); + if (!dump_proc) + perror(optarg); + break; + + case 'i': + read_raw = fopen(optarg, "rb"); + if (!read_raw) { + perror(optarg); + exit(EXIT_FAILURE); + } + force_format++; + io = IO_METHOD_READ; break; case 'f': force_format++; break; + case 's': + errno = 0; + square_thres = strtol(optarg, NULL, 0); + if (errno) + errno_exit(optarg); + break; + case 'c': errno = 0; frame_count = strtol(optarg, NULL, 0); @@ -637,8 +684,11 @@ } } - open_device(); - init_device(); + if (!read_raw) + open_device(); + init_device(); + if (read_raw) + fd = fileno(read_raw); start_capturing(); mainloop(); stop_capturing(); === modified file 'src/brmbotimg.c' --- src/brmbotimg.c 2010-11-18 01:07:05 +0000 +++ src/brmbotimg.c 2011-05-19 00:12:54 +0000 @@ -15,6 +15,8 @@ char y1, u, y2, v; }; +int square_thres = 50; + /* Negative value: likely road. * Positive value: likely outside. */ int assess_pixel(const struct dpix *pixel) @@ -23,7 +25,20 @@ } /* w,h is square radius, not diameter! */ -int assess_square(const struct dpix *image, struct v4l2_format *fmt, int x, int y, int w, int h) + +void color_square(struct dpix *image, struct v4l2_format *fmt, int x, int y, int w, int h, int u, int v) +{ + x /= 2; w /= 2; // each dpix is two physical pixels + int j, i; + for (j = y - h; j < y + h; j++) + for (i = x - w; i < x + w; i++) { + struct dpix *pixel = &image[j * fmt->fmt.pix.width / 2 + i]; + pixel->u = u; + pixel->v = v; + } +} + +int assess_square(struct dpix *image, struct v4l2_format *fmt, int x, int y, int w, int h, int color_thres) { int assess = 0; x /= 2; w /= 2; @@ -31,22 +46,22 @@ for (j = y - h; j < y + h; j++) for (i = x - w; i < x + w; i++) assess += assess_pixel(&image[j * fmt->fmt.pix.width / 2 + i]); - return assess / (h * w); + assess /= (h * w); + if (assess >= color_thres) { + color_square(image, fmt, x, y, w, h, 0, 0); + } + return assess; } -void process_image(const void *p, int size /* 614400 */, struct v4l2_format *fmt) +void process_image(void *p, int size /* 614400 */, struct v4l2_format *fmt) { -#if 1 - fwrite(p,size,1,stdout); -#else - struct timeval tv; gettimeofday(&tv, NULL); printf("[%ld,%ld] frame (size %d w %d h %d bpl %d)\n", tv.tv_sec, tv.tv_usec, size, fmt->fmt.pix.width, fmt->fmt.pix.height, fmt->fmt.pix.bytesperline); /* Each two bytes are (Y, U, Y, V) representing two * adjecent pixels. */ - const struct dpix *image = p; + struct dpix *image = p; /* Camera image: * @@ -56,23 +71,9 @@ */ /* We look at three squares. */ - int front = assess_square(image, fmt, 160, 240, 20, 20); - int right = assess_square(image, fmt, 250, 120, 20, 20); - int left = assess_square(image, fmt, 250, 360, 20, 20); + int front = assess_square(image, fmt, 160, 240, 20, 20, square_thres); + int right = assess_square(image, fmt, 250, 120, 20, 20, square_thres); + int left = assess_square(image, fmt, 250, 360, 20, 20, square_thres); printf("CAMINFO %d, %d, %d\n", front, right, left); -#endif -} - -#if 0 -int main(void) -{ - struct v4l2_format fmt = { .fmt = { .pix = { .width = 640, .height = 480, .bytesperline = 1280 } } }; - char frame[614400]; - while (!feof(stdin)) { - int i = fread(frame, 614400, 1, stdin); - process_image(frame, 614400, &fmt); - } - return 0; -} -#endif +} # Begin bundle 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