18. OpenCV шаг за шагом. Обработка изображения — морфологические преобразования 2


1. OpenCV шаг за шагом. Введение.
2. Установка.
3. Hello World.
4. Загрузка картинки.
5. Вывод видео
6. Ползунок
7. Захват видео с камеры
8. Запись видео
9. События от мышки
10. Обработка изображения — сглаживание
11. Обработка изображения — изменение размеров
12. ROI — интересующая область изображения
13. Типы данных OpenCV
14. Матрица
15. Сохранение данных в XML
16. Генерация случайных чисел
17. Обработка изображения — морфологические преобразования
18. Обработка изображения — морфологические преобразования 2

Когда я говорил, что OpenCV реализует морфологические преобразования всего двумя функциями, я, разумеется, лукавил.
Есть ещё одна замечательная функция — cvMorphologyEx(). Она обеспечивает более сложные морфологические преобразования изображения.

CVAPI(void)  cvMorphologyEx( const CvArr* src, CvArr* dst,
                             CvArr* temp, IplConvKernel* element,
                             int operation, int iterations CV_DEFAULT(1) );

— выполняет сложное морфологическое преобразование

src — исходное изображение
dst — изображение для сохранения результата
temp — для промежуточного хранения результатов (размер изображения должен совпадать с размером исходного изображения) — требуется при определённом значении operation
operation — определяет тип морфологического преобразования:

#define CV_MOP_OPEN         2
#define CV_MOP_CLOSE        3
#define CV_MOP_GRADIENT     4
#define CV_MOP_TOPHAT       5
#define CV_MOP_BLACKHAT     6

CV_MOP_OPEN и CV_MOP_CLOSE — комбинация сужения и расширения:
CV_MOP_OPEN — сначала сужается, а затем расширяется. Обычно используется для подсчёта регионов на двоичном изображении.

CV_MOP_CLOSE — сначала расширяется, а затем сужается. Обычно используется для уменьшения шумовых выбросов на границах регионов.

! Порядок выполнения операции CV_MOP_CLOSE при числе итераций iterations==2:
Dilate-Dilate-Erode-Erode !

CV_MOP_GRADIENT:
gradient(src) = Dilate(src)–Erode(src)
Результатом этой операции над двоичным изображением станет выделение периметров существующих пятен. На картинке с градациями серого градиент покажет как быстро меняется яркость (поэтому и называется — градиент).

CV_MOP_TOPHAT и CV_MOP_BLACKHAT:
TopHat(src) = src–Open(src)
BlackHat(src) = Close(src)–src

CV_MOP_TOPHAT — изолирует яркие локальные пики

CV_MOP_BLACKHAT — изоляция тёмных регионов

//
// пример работы cvMorphologyEx()
//

#include <cv.h>
#include <highgui.h>
#include <stdlib.h>
#include <stdio.h>

IplImage* image = 0;
IplImage* dst = 0;

int radius = 1;
int radius_max=10;

//
// функция-обработчик ползунка - 
// радиус ядра
void myTrackbarRadius(int pos) {
	radius = pos;
}

int iterations = 1;
int iterations_max = 10;

//
// функция-обработчик ползунка - 
// число итераций
void myTrackbarIterations(int pos) {
	radius = pos;
}

int main(int argc, char* argv[])
{
	// имя картинки задаётся первым параметром
	char* filename = argc == 2 ? argv[1] : "Image0.jpg";
	// получаем картинку
	image = cvLoadImage(filename,1);
	// клонируем картинку 
	dst = cvCloneImage(image);

	printf("[i] image: %s\n", filename);
	assert( image != 0 );

	// окно для отображения картинки
	cvNamedWindow("original",CV_WINDOW_AUTOSIZE);
	cvNamedWindow("morphology",CV_WINDOW_AUTOSIZE);

	cvCreateTrackbar("Radius", "original", &radius, radius_max, myTrackbarRadius);
	cvCreateTrackbar("Iterations", "original", &iterations, iterations_max, myTrackbarIterations);


	while(1){

		// показываем картинку
		cvShowImage("original",image);

		// создаём ядро
		IplConvKernel* Kern = cvCreateStructuringElementEx(radius*2+1, radius*2+1, radius, radius, CV_SHAPE_ELLIPSE);

		// картинка для промежуточного хранения результатов cvCreateImage
		IplImage* Temp = 0;
		Temp = cvCreateImage(cvSize(image->width, image->height) , IPL_DEPTH_8U, 1);
		// выолняем преобразование
		cvMorphologyEx(image, dst, Temp, Kern, CV_MOP_OPEN, iterations);


		// показываем результат
		cvShowImage("morphology",dst);
		
		cvReleaseStructuringElement(&Kern);
                cvReleaseImage(&Temp);

		char c = cvWaitKey(33);
		if (c == 27) { // если нажата ESC - выходим
			break;
		}
	}

	// освобождаем ресурсы
	cvReleaseImage(&image);
	cvReleaseImage(&dst);
	// удаляем окна
	cvDestroyAllWindows();
	return 0;
}

а теперь соберём все параметры преобразования cvMorphologyEx() вместе:

//
// пример работы cvMorphologyEx()
//
// покажем все методы сразу
//

#include <cv.h>
#include <highgui.h>
#include <stdlib.h>
#include <stdio.h>

IplImage* image = 0;
IplImage* open = 0;
IplImage* close = 0;
IplImage* gradient = 0;
IplImage* tophat = 0;
IplImage* blackhat = 0;

int radius = 1;
int radius_max=10;

//
// функция-обработчик ползунка - 
// радиус ядра
void myTrackbarRadius(int pos) {
	radius = pos;
}

int iterations = 1;
int iterations_max = 10;

//
// функция-обработчик ползунка - 
// число итераций
void myTrackbarIterations(int pos) {
	radius = pos;
}

int main(int argc, char* argv[])
{
	// имя картинки задаётся первым параметром
	char* filename = argc == 2 ? argv[1] : "Image0.jpg";
	// получаем картинку
	image = cvLoadImage(filename,1);
	// клонируем картинку 
	open = cvCloneImage(image);
	close = cvCloneImage(image);
	gradient = cvCloneImage(image);
	tophat = cvCloneImage(image);
	blackhat = cvCloneImage(image);

	printf("[i] image: %s\n", filename);
	assert( image != 0 );

	// окно для отображения картинки
	cvNamedWindow("original",CV_WINDOW_AUTOSIZE);
	cvNamedWindow("CV_MOP_OPEN",CV_WINDOW_AUTOSIZE);
	cvNamedWindow("CV_MOP_CLOSE",CV_WINDOW_AUTOSIZE);
	cvNamedWindow("CV_MOP_GRADIENT",CV_WINDOW_AUTOSIZE);
	cvNamedWindow("CV_MOP_TOPHAT",CV_WINDOW_AUTOSIZE);
	cvNamedWindow("CV_MOP_BLACKHAT",CV_WINDOW_AUTOSIZE);

	cvCreateTrackbar("Radius", "original", &radius, radius_max, myTrackbarRadius);
	cvCreateTrackbar("Iterations", "original", &iterations, iterations_max, myTrackbarIterations);

	while(1){

		// показываем картинку
		cvShowImage("original",image);

		// создаём ядро
		IplConvKernel* Kern = cvCreateStructuringElementEx(radius*2+1, radius*2+1, radius, radius, CV_SHAPE_ELLIPSE);

		// картинка для промежуточного хранения результатов cvCreateImage
		IplImage* Temp = 0;
		Temp = cvCreateImage(cvSize(image->width, image->height) , IPL_DEPTH_8U, 1);
		// выолняем преобразования
		cvMorphologyEx(image, open, Temp, Kern, CV_MOP_OPEN, iterations);
		cvMorphologyEx(image, close, Temp, Kern, CV_MOP_CLOSE, iterations);
		cvMorphologyEx(image, gradient, Temp, Kern, CV_MOP_GRADIENT, iterations);
		cvMorphologyEx(image, tophat, Temp, Kern, CV_MOP_TOPHAT, iterations);
		cvMorphologyEx(image, blackhat, Temp, Kern, CV_MOP_BLACKHAT, iterations);


		// показываем результат
		cvShowImage("CV_MOP_OPEN",open);
		cvShowImage("CV_MOP_CLOSE",close);
		cvShowImage("CV_MOP_GRADIENT",gradient);
		cvShowImage("CV_MOP_TOPHAT",tophat);
		cvShowImage("CV_MOP_BLACKHAT",blackhat);
		
		cvReleaseStructuringElement(&Kern);
                cvReleaseImage(&Temp);

		char c = cvWaitKey(33);
		if (c == 27) { // если нажата ESC - выходим
			break;
		}
	}

	// освобождаем ресурсы
	cvReleaseImage(&image);
	cvReleaseImage(&open);
	cvReleaseImage(&close);
	cvReleaseImage(&gradient);
	cvReleaseImage(&tophat);
	cvReleaseImage(&blackhat);
	// удаляем окна
	cvDestroyAllWindows();
	return 0;
}

CVAPI(void) cvDestroyAllWindows(void);

— удаляет все окна

Читать далее: 19. Обработка изображения — заливка части изображения


0 комментариев на «“18. OpenCV шаг за шагом. Обработка изображения — морфологические преобразования 2”»

  1. В первом примере есть хорошая утечка памяти так как не освобождается временное изображение (переменная Temp).

    Нужно пере строкой
    cvReleaseStructuringElement(&Kern);

    добавить следующее:
    cvReleaseImage(&Temp);

    🙂

    • Всегда пожалуйста 🙂

      А потом будет открыта тайна какое практическое значение можно выудить из этих методов, а то уже интрига есть. Уж больно интересно 🙂

  2. Есть предложение по поводу кода, дабы не делать большую нагрузку на компьютер лучше обновлять картинки по изменению ползунков. Для этого достаточно немного модифицировать код.
    1. cvWaitKey поставить с параметром 0.
    2. В обработчики ползунков, после присваивания значений добавить следующую строчку:
    keybd_event(VK_SPACE,0,KEYEVENTF_EXTENDEDKEY,0); //Нажата клавиша пробел

    • keybd_event(VK_SPACE,0,KEYEVENTF_EXTENDEDKEY,0)

      не могу понять зачем всовывать ее в обработчик ползунков. и вообще странные эти обработчики, которые вызываются однажды и работают как то постоянно, т.е. это не противоречит концепции с++, что выполненная функция должна стереться с лица памяти?)
      где можно посмотреть прототип функции keybd_event() она из она библиотеки openCV?

  3. Читая код с листа кажется, что код
    void myTrackbarIterations(int pos) { radius = pos;}

    должен выглядеть как
    void myTrackbarIterations(int pos) { iterations = pos;}

    Я что-то упускаю?

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

Arduino

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

Разделы

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

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

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

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