Чего уже только не сделали на малыше TP-LINK TL-MR3020!
Ведь, это действительно доступный крохотный одноплатник на базе Linux с Wi-Fi, Ethernet, USB и UART-ом на борту (и несколькими GPIO).
Простой робот телеприсутствия, система для умного дома и многое-многое другое.
Как помним, в прошивке OR-WRT — уже идёт утилита mjpg_streamer, которая может захватывать кадр с веб-камеры,
сжимать его в JPEG (если камера отдаёт кадры в формате YUYV) и отдавать кадр в сокет.
При подключения веб-камеры, поддерживающей UVC, в каталоге /dev появится видео-устройство /dev/video0,
данные с которого можно считывать.
Ручной запуск mjpg_streamer выглядит так:
# mjpg_streamer -i «input_uvc.so -d /dev/video0 -r 320×240 -f 10» -o «output_http.so -p 8080»
, если же камера выдаёт данные в формате YUYV, то необходимо просто указать параметр -y и всё заработает.
# mjpg_streamer -i «input_uvc.so -d /dev/video0 -y -r 320×240 -f 10» -o «output_http.so -p 8080»
— при таком режиме работы, нагрузка на процессор роутера будет составлять более 90%
Получается, что использовать камеры, выдающие данные в формате YUYV — не очень выгодно, но зато это может быть удобно если мы хотим организовать систему компьютерного зрения прямо на роутере!
Действительно, раз mjpg_streamer может считывать картинку с камеры через свой плагин input_uvc.so,
то почему бы не реализовать свой видеозахват самостоятельно?
Сказано — сделано!
Так на свет появилась tinycv — крохотная библиотечка компьютерного зрения на C под ОС Linux.
Библиотечка состоит всего из нескольких C-ных файлов и позволяет считывать bmp-картинки, работать с камерой и
осуществлять некоторые базовые операции над изображением.
Скачать библиотечку можно с google code — tinycv
Для работы с картинкой используется следующая структура:
typedef struct image { int width; int height; int type; int n_channels; int size; int step; char* data; } image;
возможные типы данных картинок:
enum {CV_DEPTH_8S=0, CV_DEPTH_8U, CV_DEPTH_16S, CV_DEPTH_16U, CV_DEPTH_32F};
Простой пример, загружающий картинку из bmp-файла и выполняющий конвертацию цветной картинки в градации серого.
#include <stdio.h> #include <stdlib.h> #include "tinycv.h" int main(int argc, char* argv[]) { printf("[i] Start...\n"); char file_name[] = "test.bmp"; char* filename=file_name; if(argc >= 2) { filename = argv[1]; } printf("[i] file: %s\n", filename); // test image loading image* img2 = image_load(filename); if(!img2) { printf("[!] Error: image_load()\n"); return -1; } printf("[i] image size: %dx%dx%d (%d)\n", img2->width, img2->height, img2->n_channels, img2->size); image_save(img2, "test2_load_save.bmp"); // test convert image to grayscale printf("[i] image_convert_color \n"); image* img_gray = image_create(img2->width, img2->height, 1, CV_DEPTH_8U); image_convert_color(img2, img_gray, CV_RGB2GRAY); image_save(img_gray, "test3_gray.bmp"); image_delete(&img2); image_delete(&img_gray); printf("[i] End.\n"); return 0; }
Пример работы с камерой — захват 10 кадров с камеры и конвертация их в градации серого с
сохранением результатов в файл frame_gray.bmp
#include <stdio.h> #include <stdlib.h> #include "tinycv.h" #define VIDEO_WIDTH 160 //320 #define VIDEO_HEIGHT 120 //240 int main(int argc, char* argv[]) { printf("[i] Start...\n"); image* img = image_create(VIDEO_WIDTH, VIDEO_HEIGHT, 3, CV_DEPTH_8U); image* img_gray = image_create(VIDEO_WIDTH, VIDEO_HEIGHT, 1, CV_DEPTH_8U); if(!img) { printf("[!] Error: image_create()\n"); } int res = 0; videocapture_dev dev; memset(&dev, 0, sizeof(dev)); dev.width = VIDEO_WIDTH; dev.height = VIDEO_HEIGHT; dev.fps = 10; videocapture_init("/dev/video0", &dev); res = videocapture_open(&dev); if(res != 0) return -1; int counter=0; while(1) { image* frame = videocapture_update(&dev); image_copy(frame, img); image_save(img, "frame.bmp"); image_convert_color(img, img_gray, CV_RGB2GRAY); image_save(img_gray, "frame_gray.bmp"); if(counter++>10) { break; } } videocapture_close(&dev); image_delete(&img); image_delete(&img_gray); printf("[i] End.\n"); return 0; }
Работа с BMP-файлами проверялась только на картинках 320х240 и 160×120 — на других возможны проблемы.
Работа всех доступных методов библиотечки tinycv, приводятся в двух тестовых файлах:
test/test.c — методы обработки изображения
test/video.c — видеозахват
В состав библиотечки, так же, добавлены методы для определения доминирующих цветов на изображении
(см. dominatecolors.c) — которые являются простым переносом OpenCV-ой версии.
Сборка tinycv для TP-LINK TL-MR3020
Для сборки под TP-LINK TL-MR3020
нужно выполнить
export PATH=»$HOME/dev/toolchains/tplink/toolchain-mips_r2_gcc-4.3.3+cs_uClibc-0.9.30.1/usr/bin:$PATH»
и сделать
make CC=mips-openwrt-linux-gcc AR=mips-openwrt-linux-ar
собралось!
проверяем:
scp test [email protected]:/tmp
scp cat.bmp [email protected]:/tmp
scp video [email protected]:/tmp
# ./test cat.bmp [i] Start... [i] file: cat.bmp [i] image size: 320x240x3 (230400) [i] BmpHeader: 14 BmpImageInfo: 40 [i] BMP header: [i] signature: BM [i] fileSize: 914621184 [i] reserved: 0 offset: 905969664 [i] BMP Info: [i] headerSize: 671088640 [i] size: 1073807360x-268435456 [i] planeCount: 256 [i] bitDepth: 6144 [i] compression: 0 [i] compressedImageSize: 8651520 [i] horizontalResolution: 319488000 [i] verticalResolution: 319488000 [i] numColors: 0 [i] importantColors: 0 [i] End.
root@OpenWrt:/tmp# ls -asl 0 drwxrwxrwt 10 root root 400 Jan 1 00:02 . 0 drwxr-xr-x 1 root root 0 Jan 1 00:00 .. 0 drwx------ 2 root root 40 Jan 1 00:00 .uci 4 -rw-r--r-- 1 root root 4 Jan 1 00:00 TZ 228 -rw-r--r-- 1 root root 230454 Jan 1 00:01 cat.bmp 0 -rw-r--r-- 1 root root 0 Jan 1 00:00 dhcp.leases 0 drwxr-xr-x 2 root root 80 Jan 1 00:00 etc 4 -rw-r--r-- 1 root root 85 Jan 1 00:00 fstab 0 drwxr-xr-x 2 root root 60 Jan 1 00:00 lock 0 drwxr-xr-x 2 root root 80 Jan 1 00:00 log 0 drwxr-xr-x 2 root root 40 Jan 1 00:00 overlay 4 -rw-r--r-- 1 root root 32 Jan 1 00:00 resolv.conf 0 -rw-r--r-- 1 root root 0 Jan 1 00:00 resolv.conf.auto 0 drwxr-xr-x 2 root root 220 Jan 1 00:00 run 0 drwxr-xr-x 2 root root 60 Jan 1 00:00 state 0 drwxr-xr-x 2 root root 80 Jan 1 00:00 sysinfo 32 -rwxr-xr-x 1 root root 30066 Jan 1 00:01 test 4 -rw-r--r-- 1 root root 54 Jan 1 00:03 test2.bmp 228 -rw-r--r-- 1 root root 230454 Jan 1 00:03 test3.bmp 64 -rwxr-xr-x 1 root root 62354 Jan 1 00:02 video
посмотрим:
scp [email protected]:/tmp/test2.bmp ./
scp [email protected]:/tmp/test3.bmp ./
тээкс — файлы не алё, да и по данным загрузки BMP видно, что что-то не так.
посмотрим размер файла (230454), а нам показано:
[i] fileSize: 914621184
914621184 = 0x36840300
нужный размер получается при перевороте:
0x00038436 = 230454
напишем пару макросов:
#ifdef MIPS #define to_mips2(x) ( ((x) >> 8) | (((x) & 0xFF) << 8) ) #define to_mips4(x) ( ((x) >> 24) | ((((x)>>16) & 0xFF) << 8) | ((((x)>>8) & 0xFF) << 16) | (((x) & 0xFF)<<24) ) #endif
и прогоним через них все параметры BMP-файла
# ./test cat.bmp [i] Start... [i] file: cat.bmp [i] image size: 320x240x3 (230400) [i] BmpHeader: 14 BmpImageInfo: 40 [i] BMP header: [i] signature: BM [i] fileSize: 230454 [i] reserved: 0 offset: 54 [i] BMP Info: [i] headerSize: 40 [i] size: 320x240 [i] planeCount: 1 [i] bitDepth: 24 [i] compression: 0 [i] compressedImageSize: 230400 [i] horizontalResolution: 2835 [i] verticalResolution: 2835 [i] numColors: 0 [i] importantColors: 0 [i] End. root@OpenWrt:/tmp# # ls -asl | grep test 32 -rwxr-xr-x 1 root root 32658 Jan 1 01:02 test 228 -rw-r--r-- 1 root root 230454 Jan 1 01:02 test2.bmp 228 -rw-r--r-- 1 root root 230454 Jan 1 01:02 test3.bmp
скачиваем:
scp [email protected]:/tmp/test2.bmp ./
scp [email protected]:/tmp/test3.bmp ./
заработало!
подключим камеру
# ls -asl /dev 0 drwxr-xr-x 5 root root 1080 Jan 1 00:06 . 0 drwxr-xr-x 1 root root 0 Jan 1 00:00 .. ... 0 crw-r--r-- 1 root root 81, 0 Jan 1 00:06 video0 ... # ./video [i] Start... [i] file: test.bmp [!] Error: VIDIOC_S_FMT errno: 16, Device or resource busy
прибьём mjpg_streamer
# ps | grep mjpg 1341 root 6428 S /usr/bin/mjpg_streamer --input input_uvc.so --device 1402 root 1492 S grep mjpg
# killall mjpg_streamer
# ./video [i] Start... [i] file: test.bmp capture: size: 320 x 240 format: VYUY framesize: 153600 [i] End.
забираем файлы
scp [email protected]:/tmp/frame.bmp ./
scp [email protected]:/tmp/frame_gray.bmp ./
ураааааааааааа! получилось 🙂 правда, картинка перевёрнута, ну так это ерунда 🙂
Добавим в Makefile соответствующую цель и теперь, чтобы собрать библиотечку для роутера TP-LINK TL-MR3020
остаётся просто выполнить
make mips
Быстродействие tinycv
на TL-MR3020:
[i] == Performance == [t] image_convert_color: 0.320251 s [t] image_thin_borders: 2.708240 s [t] image_min_max_loc: 0.099041 s [t] image_threshold: 0.075435 s [t] image_resize: 0.372802 s [t] image_kmeans_colorer: 22.603177 s [t] image_rgb2hsv: 2.389729 s [t] image_hsv_colorer: 3.882528 s ./video [i] Start... capture: size: 320 x 240 format: VYUY framesize: 153600 [t] videocapture_update: 0.990277 s
на Raspberry Pi:
[i] == Performance == [t] image_convert_color: 0.018003 s [t] image_thin_borders: 0.154594 s [t] image_min_max_loc: 0.011518 s [t] image_threshold: 0.010576 s [t] image_resize: 0.070244 s [t] image_kmeans_colorer: 9.684125 s [t] image_rgb2hsv: 0.075674 s [t] image_hsv_colorer: 0.219964 s # ./video [i] Start... capture: size: 320 x 240 format: YUYV framesize: 153600 [t] videocapture_update: 0.077876 s [t] image_hsv_colorer: 0.295682 s
Поэтому, видеозахват на роутере TL-MR3020 лучше вести с разрешением поменьше- например, 160x120 😉
Работа библиотеки tinycv проверялась на x86 (Debian 6 и Ubuntu), на TP-LINK TL-MR3020 (OR-WRT) и на Raspberry Pi (raspbian wheezy).
Вот, собственно и всё! Используя эту библиотечку, которую можно легко дополнить требуемым функционалом,
очень просто наделить своего робота или автомат настоящим зрением!
Ссылки
http://code.google.com/p/robocraft/
По теме
Использование Lua в робототехнике
Исследование Wi-Fi-роутера TP-LINK TL-MR3020
Кросс-компиляция Lua для TP-LINK TL-MR3020
Lua - модуль для работы с последовательным портом