Установка OpenCV
OpenCV (Open Computer Vision) – это открытая библиотека C++, которая содержит алгоритмы для: интерпретации изображений, калибровки камеры по эталону, устранения оптических искажений, определения сходства, распознавания жестов и т.д.
Установить OpenCV несложно (пример для ArchLinux):
[orca@blizzard ~]$ sudo pacman -S opencv opencv-samples |
Для проверки собрать и запустить тестовую программу со следующим исходным кодом (файл helloworld.cpp):
#include <opencv/cv.h>
#include <opencv/highgui.h>
int main( int argc, char** argv )
{
// Задать высоту и ширину картинки
int height = 620;
int width = 440;
// Задать точку для вывода текста
CvPoint pt = cvPoint( height/4, width/2 );
// Создать 8-битную, 3-канальную картинку
IplImage* hw = cvCreateImage(cvSize(height, width), 8, 3);
// Залить картинку чёрным цветом
cvSet(hw,cvScalar(0,0,0));
// Инициализация шрифта
CvFont font;
cvInitFont( &font, CV_FONT_HERSHEY_COMPLEX,1.0, 1.0, 0, 1, CV_AA);
// Используя шрифт вывести на картинку текст
cvPutText(hw, "Testing OpenCV...", pt, &font, CV_RGB(150, 0, 150) );
// Создать окно
cvNamedWindow("Hello World", 0);
// Показать картинку в созданном окне
cvShowImage("Hello World", hw);
// Ждать нажатия клавиши
cvWaitKey(0);
// Освободить ресурсы
cvReleaseImage(&hw);
cvDestroyWindow("Hello World");
return 0;
}
|
При сборке не забыть указать обязательные параметры:
[orca@blizzard my_c]$ g++ `pkg-config opencv --libs` helloworld.cpp -o helloworld |
Запустить программу:
[orca@blizzard my_c]$ ./helloworld |
Эксперимент №1. Захват видео с веб-камеры
Подключить веб-камеру, убедиться, что она работает. Собрать и запустить тестовую программу (captureCamera.cpp):
#include <opencv/cv.h>
#include <opencv/highgui.h>
#include <stdlib.h>
#include <stdio.h>
CvCapture* capture =0;
IplImage* frame =0;
int main(int argc, char* argv[])
{
// Получить подключенную камеру
CvCapture* capture = cvCreateCameraCapture(CV_CAP_ANY);
assert( capture && "Не удалось найти камеру");
// Определить ширину и высоту кадра
double width = cvGetCaptureProperty(capture, CV_CAP_PROP_FRAME_WIDTH);
double height = cvGetCaptureProperty(capture, CV_CAP_PROP_FRAME_HEIGHT);
//printf("[i] %.0f x %.0f\n", width, height );
/* Используемые переменные:
* counter счётчик для снимков
* filename имя файла для снимков
* currentPosition текущий номер кадра
* message сообщение с указанием номера кадра
* font шрифт, используемый в сообщении
* pt позиция где отображается сообщение
* size размер кадра
*/
int counter=0;
char filename[512];
int currentPosition=0;
char message [30];
CvFont font;
cvInitFont( &font, CV_FONT_HERSHEY_SIMPLEX, 0.5, 0.5, 0, 1, CV_AA);
CvPoint pt = cvPoint( 20, 20 );
cvNamedWindow("capture", CV_WINDOW_AUTOSIZE);
CvSize size = cvSize(width,height);
CvVideoWriter *writer = cvCreateVideoWriter("myVideo.avi",CV_FOURCC('D','I','V','X'),15,size);
printf("[i] press Enter for capture image and Esc for quit!\n\n");
while(true){
// Получить кадр
frame = cvQueryFrame( capture );
// Показать кадр в окне
cvShowImage("capture", frame);
// Напечатать в кадре сообщение с номером этого кадра
sprintf(message, "position: %d", currentPosition);
cvPutText(frame, message, pt, &font, CV_RGB(150, 150, 150) );
// Записать кадр в видеофайл, увеличить счётчик кадров
cvWriteFrame(writer, frame);
currentPosition++;
// Следить за нажатием клавиш клавиатуры
char c = cvWaitKey(33);
if (c == 27) {
// Нажата клавиша ESC -> завершить программу
break;
}
else if(c == 10) {
// Нажата клавиша Enter -> сохранить кадр как JPG-файл
// Для ОС MS Windows код клавиши: 13
sprintf(filename, "Image%d.jpg", counter);
printf("[i] capture... %s\n", filename);
cvSaveImage(filename, frame);
counter++;
}
}
// Освободить ресурсы
cvReleaseCapture(&capture);
cvReleaseVideoWriter(&writer);
cvDestroyWindow("capture");
return 0;
}
|
Эксперимент №2. Интеллектуальный захват видео
Изменив предыдущий код, можно написать программу, которая записывает видео только в случае изменения сюжета кадра. Программу можно использовать в системе видеонаблюдения. Камера неподвижна и направлена на наблюдаемый объект. После запуска программы начинается запись видео в файл, но если изображение статично, т.е. не происходит никаких изменений, то кадры пропускаются.
В основе алгоритма лежит сравнение двух соседних кадров (текущего и предыдущего). Если разницы нет, то кадр пропускается. Сравнение кадров осуществляется сличением их гистограмм.
Код программы (intrusionDetect.cpp):
#include <opencv/cv.h>
#include <opencv/highgui.h>
#include <stdlib.h>
#include <stdio.h>
#include <ctime>
/* Используемые переменные:
* counter счётчик для снимков
* filename имя файла для снимков
* currentPosition текущий номер кадра
* message сообщение с указанием номера кадра
* font шрифт, используемый в сообщении
* pt позиция где отображается сообщение
* size размер кадра
* capture указатель на структуру чтения видео-потока с камеры
* writer указатель на структуру для видеозаписи
* currentFrame указатель на текущий кадр
* previousFrame указатель на предыдущий или вспомогательный кадр
* HSV каналы одноимённой модели (тон, насыщенность, яркость)
*/
int counter = 0;
char filename [512];
int currentPosition = 0;
char message [30];
double histogramComparasion = 0.0;
time_t seconds = time(NULL);
tm* timeinfo = localtime(&seconds);
const char* format = "%A, %B %d, %Y %I:%M:%S";
CvFont font;
CvPoint pt;
CvSize size;
CvCapture* capture = 0;
CvVideoWriter* writer = 0;
IplImage* currentFrame = 0;
IplImage* tempFrame = 0;
CvHistogram* histogram = 0;
CvHistogram* currentHistogram = 0;
CvHistogram* previousHistogram = 0;
CvHistogram* calculateHistogram(IplImage* sourceImage);
IplImage* drawHistogram (CvHistogram* histogram, int HBins, int SBins);
int main(int argc, char* argv[])
{
Получить подключенную камеру
capture = cvCreateCameraCapture(CV_CAP_ANY);
assert( capture && "Не удалось найти камеру");
// Определить ширину и высоту кадра
double width = cvGetCaptureProperty(capture, CV_CAP_PROP_FRAME_WIDTH);
double height = cvGetCaptureProperty(capture, CV_CAP_PROP_FRAME_HEIGHT);
//printf("[i] %.0f x %.0f\n", width, height );
cvInitFont( &font, CV_FONT_HERSHEY_SIMPLEX, 0.5, 0.5, 0, 1, CV_AA);
pt = cvPoint( 20, 20 );
// результат работы в файле myVideo.avi
cvNamedWindow("capture", CV_WINDOW_AUTOSIZE);
size = cvSize(width,height);
writer = cvCreateVideoWriter("myVideo.avi",CV_FOURCC('D','I','V','X'),15,size);
printf("[i] press Enter for capture image and Esc for quit!\n\n");
while(true){
// Получить кадр
currentFrame = cvQueryFrame( capture );
// Напечатать в кадре сообщение с номером этого кадра
//sprintf(message, "position: %d", currentPosition);
seconds = time(NULL);
timeinfo = localtime(&seconds);
strftime(message, 80, format, timeinfo);
cvPutText(currentFrame, message, pt, &font, CV_RGB(150, 0, 0) );
// Показать кадр в окне
cvShowImage("capture", currentFrame);
// Вычисление гистограмм двух соседних кадров
if (currentPosition == 0) {
cvWriteFrame(writer, currentFrame);
previousHistogram = calculateHistogram(currentFrame);
}
currentHistogram = calculateHistogram(currentFrame);
// Сравнение гистограмм по корреляционному методу
// 1 – максимальное соответствие, -1 – максимальное несоответствие, 0 – нет никакой корреляции.
histogramComparasion = cvCompareHist( previousHistogram, currentHistogram, CV_COMP_CORREL );
// 0.997 - порог срабатывания (подбирается экспериментально)
if (histogramComparasion < 0.997)
{
printf("Method CV_COMP_CORREL. Difference at position %d: %f \n", currentPosition, histogramComparasion);
cvWriteFrame(writer, currentFrame);
}
previousHistogram = currentHistogram;
// Увеличить счётчик кадров
currentPosition++;
// Следить за нажатием клавиш клавиатуры
char c = cvWaitKey(33);
if (c == 27) {
// Нажата клавиша ESC -> завершить программу
break;
}
else if(c == 10) {
// Нажата клавиша Enter -> сохранить кадр как JPG-файл
// Для ОС MS Windows код клавиши: 13
sprintf(filename, "Image%d.jpg", counter);
printf("[i] capture... %s\n", filename);
cvSaveImage(filename, currentFrame);
histogram = calculateHistogram(currentFrame);
tempFrame = drawHistogram ( histogram, 30, 32);
sprintf(filename, "Histogram%d.jpg", counter);
printf("[i] capture... %s\n", filename);
cvSaveImage(filename, tempFrame);
counter++;
}
}
// Освободить ресурсы
cvReleaseCapture(&capture);
cvReleaseVideoWriter(&writer);
cvDestroyWindow("capture");
return 0;
}
// Функция вычисления/построения гистограммы
CvHistogram* calculateHistogram(IplImage* sourceImage)
{
int HBins = 30;
int SBins = 32;
int histogramSize[] = { HBins, SBins };
float HRanges[] = { 0, 180 };
float SRanges[] = { 0, 255 };
float* ranges[] = { HRanges, SRanges };
IplImage* HSVImage = 0;
IplImage* channelH = 0;
IplImage* channelS = 0;
IplImage* channelV = 0;
CvHistogram* histogram = 0;
HSVImage = cvCreateImage( cvGetSize(sourceImage), 8, 3 );
cvConvertImage( sourceImage, HSVImage, CV_BGR2HSV );
channelH = cvCreateImage( cvGetSize(sourceImage), 8, 1 );
channelS = cvCreateImage( cvGetSize(sourceImage), 8, 1 );
channelV = cvCreateImage( cvGetSize(sourceImage), 8, 1 );
IplImage* channelsHS[] = { channelH, channelS };
cvCvtPixToPlane( HSVImage, channelH, channelS, channelV, 0 );
histogram = cvCreateHist(
2, histogramSize, CV_HIST_ARRAY, ranges, 1
);
cvCalcHist( channelsHS, histogram, 0, 0 );
cvNormalizeHist( histogram, 1.0 );
cvReleaseImage( &HSVImage );
cvReleaseImage( &channelH );
cvReleaseImage( &channelS );
cvReleaseImage( &channelV );
return histogram;
}
IplImage* drawHistogram (CvHistogram* histogram, int HBins, int SBins)
{
int scale = 10;
float maxValue = 0;
IplImage* histogramImage = 0;
histogramImage = cvCreateImage( cvSize( HBins * scale, SBins * scale ), 8, 3);
cvZero( histogramImage );
cvGetMinMaxHistValue( histogram, 0, &maxValue, 0, 0 );
for( int h = 0; h < HBins; h++ )
{
for( int s = 0; s < SBins; s++ )
{
float binValue = cvQueryHistValue_2D( histogram, h, s );
int intensity = cvRound( binValue * 255 / maxValue );
cvRectangle( histogramImage,
cvPoint( h*scale, s*scale ),
cvPoint( (h+1)*scale - 1,
(s+1)*scale - 1),
CV_RGB(intensity,intensity,intensity),
CV_FILLED);
}
}
return histogramImage;
}
|
