Компьютерное зрение на Wi-Fi роутере TP-LINK TL-MR3020 при помощи tinycv


Чего уже только не сделали на малыше 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 320x240 -f 10» -o «output_http.so -p 8080»

, если же камера выдаёт данные в формате YUYV, то необходимо просто указать параметр -y и всё заработает.
# mjpg_streamer -i «input_uvc.so -d /dev/video0 -y -r 320x240 -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 и 160x120 — на других возможны проблемы.

Работа всех доступных методов библиотечки 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 root@192.168.217.1:/tmp
scp cat.bmp root@192.168.217.1:/tmp
scp video root@192.168.217.1:/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 root@192.168.217.1:/tmp/test2.bmp ./
scp root@192.168.217.1:/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 root@192.168.217.1:/tmp/test2.bmp ./
scp root@192.168.217.1:/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 root@192.168.217.1:/tmp/frame.bmp ./
scp root@192.168.217.1:/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).

Вот, собственно и всё! Используя эту библиотечку, которую можно легко дополнить требуемым функционалом,
очень просто наделить своего робота или автомат настоящим зрением!

Ссылки:
code.google.com/p/robocraft/

По теме:
Использование Lua в робототехнике
Исследование Wi-Fi-роутера TP-LINK TL-MR3020
Кросс-компиляция Lua для TP-LINK TL-MR3020
Lua — модуль для работы с последовательным портом
  • 0
  • 14 января 2013, 07:28
  • noonv

Комментарии (0)

RSS свернуть / развернуть

Только зарегистрированные и авторизованные пользователи могут оставлять комментарии.