Компьютерное зрение на 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 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 - модуль для работы с последовательным портом


Добавить комментарий

Arduino

Что такое Arduino?
Зачем мне Arduino?
Начало работы с Arduino
Для начинающих ардуинщиков
Радиодетали (точка входа для начинающих ардуинщиков)
Первые шаги с Arduino

Разделы

  1. Преимуществ нет, за исключением читабельности: тип bool обычно имеет размер 1 байт, как и uint8_t. Думаю, компилятор в обоих случаях…

  2. Добрый день! Я недавно начал изучать программирование под STM32 и ваши уроки просто бесценны! Хотел узнать зачем использовать переменную типа…

3D-печать AI Android Arduino Bluetooth CraftDuino DIY IDE iRobot Kinect LEGO OpenCV Open Source Python Raspberry Pi RoboCraft ROS swarm ИК автоматизация андроид балансировать бионика версия видео военный датчик дрон интерфейс камера кибервесна манипулятор машинное обучение наше нейронная сеть подводный пылесос работа распознавание робот робототехника светодиод сервомашинка собака управление ходить шаг за шагом шаговый двигатель шилд юмор

OpenCV
Робототехника
Будущее за бионическими роботами?
Нейронная сеть - введение