Установка 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; } |