Перейти к содержимому
Compvision.ru
tii20

Автоматический поиск цвета

Recommended Posts

Добрый день. У меня такая задача, необходимо сегментировать пятна на фото(фото больных псариазом).

У меня алгоритм в голове такой.

Картинку перевожу в HSV->разбиваю на 3 канала-> ищу по цвету, контрасту и яркости-> соединяю все в один бинарник-> и с помощью CANNY выделяю пятна и вот она радость.

Но у меня проблема, у меня стоит задача автоматизировать поиск по цвету, то есть задавать его не самому, а чтобы какой нибудь обработчик,бегал и выцеплял все значения.

Это необходимо сделать потому что, диапазон значений по H S и V у пятен достаточно большой и если задавать тривиальный например от 0 до 11 H, и т.д. многие пятна не находятся.

И собственно вопрос, как можно сделать такой обработчик, если кто нибудь сталкивался или видел, то можно плиз ссылочку, если есть такая тема в закромах форума можно ее тоже, потому что я вроде бы тут все облазил и по поиску искал, ничего не нашел.

Заранее спасибо.

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

Могу предложить подумать на тему обучения какого-нибудь классификатора (SVM например, или нейронную сеть), для определения принадлежности цвета к классу пятен.

В качестве параметров можно принять цвет кожи (гистограмма), цвет пятна (гистограмма).

Количество измерений пространства получается N1+N2 , где N1 - количество столбцов гистограммы цвета чистой кожи, и N2 - количество столбцов гистограммы пятен.

Обучающая пара будет: n1,n2,..,nN1, m1,m2,..,mN2, C

,где n1-nN1 - это высота столбцов гистограммы цвета чистой кожи, m1-mN2 - это высота столбцов гистограммы цвета пятен, С - класс (пятно С=1, не пятно С=0).

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

Спасибо огромное за идею. Буду идти тогда в сторону обучения нейронных сетей.

И если есть такая возможность, если есть примеры по нейро сетям с opencv, то можно ссылочку как бы это по круче реализовать.

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

Мне лично больше по душе SVM.

http://www.compvision.ru/forum/index.php?showtopic=404

Но можно и нейросети использовать, например FANN: http://leenissen.dk/fann/wp/

Кстати, для того, чтобы получить гистограмму, надо будет работать не с отдельными пикселями, а с фрагментами (небольшая квадратная окрестность пикселя например).

Но вначале я бы попробовал обойтись шестимерным вектором признаков R1,G1,B1,R2,G2,B2.

Плюс к этому,для выделений пятен нужно будет в качестве входных данных, указать программе участок чистой кожи

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

О спасибо большое! Начинаю мучить мозг, то что получится, обязательно поделюсь!

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

У меня такой вопрос

Я разбиваю картинку на 3 канала. HSV. Можно ли как нибудь сделать так, чтобы допустим наводишь мышкой, на пиксель в канале ,допустим, H, и по щелчку можно было бы вывести в поток какое значение H имеет пиксель. Примерно также как и с координатами:

void myMouseCallback( int event, int x, int y, int flags, void* param )

{

IplImage* img = (IplImage*) param;

switch( event ){

case CV_EVENT_MOUSEMOVE:

break;

case CV_EVENT_LBUTTONDOWN:

printf("%d x %d\n", x, y);

drawTarget(img, x, y, 10);

break;

case CV_EVENT_LBUTTONUP:

break;

}

}

Есть ли какой нибудь распознаватель у opencv для каналов HSV? Просто хочу знать с каким диапазоном работать.

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

Потому что уж слишком тяжело мне даются опорные векторы.

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

А в принципе смысл я придумал такой, навожу на original image мышкой на пятно, он смотрит параметры, ищет такие же параметры по всей картинке и уже начинает бинарить hsv и складывать в общую бинарку.

Я смотрю дальше , ага не все выделилось, вижу какой сегмент не выделился, тыкаю на него в любой пиксель и он бежит дальше. И так пока не помру или пока не выделю все.

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

Лучше с матрицами:


using namespace std;
using namespace cv;
Mat Img=imread("C:\\ImagesForTest\\lena.jpg");
cv::cvtColor(Img,Img,COLOR_BGR2Lab);
Vec3d val=Img.at<Vec3d>(y,x);
int l = val[0];
int a = val[1];
int b = val[2];[/code]

PS: Для обучения нужны еще и отрицательные примеры, а то в конце-концов все забинарит.

И еще пришла одна мысль, тут может быть хорошо использовать суперпиксели: http://www.compvision.ru/forum/index.php?showtopic=821

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

Набросал программку обучающуюся нужному цвету (в принципе, можно ему любой классификатор подсунуть, я пользовался примером points_classifier.cpp и вынул оттуда SVM).

Юзать так:

Левой кнопкой мыши набираем образцы относящиеся к нашему объекту (положительные примеры),

а правой не относящиеся (отрицательные примеры).

Затем жмем 'r' и видим результат.

Что то типа этого (слева результат работы классификатора, справа картинка в пространстве HSV):

post-1-0-92282600-1336916254_thumb.png

post-1-0-63717600-1336920785_thumb.png

Вот код:

 
#include <iostream>
#include <vector>
#include <stdio.h>
#include "opencv2/core/core.hpp"
#include "opencv2/core/gpumat.hpp"
#include "opencv2/core/opengl_interop.hpp"
#include "opencv2/gpu/gpu.hpp"
#include "opencv2/ml/ml.hpp"
#include "opencv2/highgui/highgui.hpp"
#include "opencv2/contrib/contrib.hpp"
#include "opencv2/video/tracking.hpp"
#include "opencv2/imgproc/imgproc.hpp"
#include "fstream"
#include "iostream"
using namespace std;
using namespace cv;
// 
Mat img, imgDst;
const int testStep = 5;
// Файловый поток для сохранения обучающих пар
ofstream fs;
// Сохраняем патч (20х20=400 точек)
void SavePatch(ofstream &fs, Mat &Img, bool isSkin)
{
	for( int r = 0; r < Img.rows; r++ )
	{
		for( int c = 0; c < Img.cols; c++ )
		{
			Vec3b val=Img.at<Vec3b>(r,c);
			fs << (int)val[0] << " " << (int)val[1] << " " << (int)val[2] << " " << (int)isSkin << endl;
		}
	}
}
// Заполнение матриц для обучения
void prepare_train_data( Mat& samples, Mat& classes )
{
	string line;	
	vector<Vec3b>	samples_vector;
	vector<int>		labels_vector;
	ifstream ifs("Samples.dat");
	stringstream ss;
	int N;
	Vec3b Col;
	int Label;
	if (ifs.is_open())
	{
		while ( ifs.good() )
		{
			getline (ifs,line);
			//cout << line << endl;
			ss = stringstream(line);
			ss >> N;
			Col[0]=N;
			ss >> N;
			Col[1]=N;
			ss >> N;
			Col[2]=N;
			ss >> N;
			Label=N;
			samples_vector.push_back(Col);
			labels_vector.push_back(Label);
		}
		ifs.close();
	}
	samples=Mat(samples_vector.size(),3,CV_32FC1);
	classes=Mat(labels_vector.size(),1,CV_32FC1);
	for(int i=0;i<samples_vector.size();i++)
	{
	samples.at<Vec3f>(i)[0]=samples_vector[i][0];
	samples.at<Vec3f>(i)[1]=samples_vector[i][1];
	samples.at<Vec3f>(i)[2]=samples_vector[i][2];
	classes.at<float>(i)=labels_vector[i];
	}
	samples_vector.clear();
	labels_vector.clear();
}
//-----------------------------------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------------------------------
void mouseHandler(int event, int x, int y, int flags, void *param)
{
	Mat* Img=(Mat*)param;
	Mat Img1;
	Img1=Img->clone();
	switch(event) 
	{
	// Левая кнопка мыши (положительные образцы)
	case CV_EVENT_LBUTTONDOWN:
		fprintf(stdout, "Left button down (%d, %d).\n", x, y);
		if( x>10 && y>10 && x<Img1.cols-10 && y<Img1.rows-10)
		{
			Mat tmp = Img1(Rect(x-10,y-10,20,20));
			SavePatch(fs,tmp,1);
		}
		break;
	// Правая кнопка мыши (отрицательные образцы)
	case CV_EVENT_RBUTTONDOWN:
		fprintf(stdout, "Right button down (%d, %d).\n", x, y);
		if( x>10 && y>10 && x<Img1.cols-10 && y<Img1.rows-10)
		{
			cv::Mat tmp = Img1(cv::Rect(x-10,y-10,20,20));
			SavePatch(fs,tmp,0);
		}
		break;
	// Перемещение мыши
	case CV_EVENT_MOUSEMOVE:
		// рисуем прямоугольник
		rectangle(Img1, Point(x-10,y-10),Point(x+10,y+10),Scalar(0, 0, 255, 0), 1, 8, 0);
		imshow("Image", Img1);
		break;
	}
}

void find_decision_boundary_SVM( CvSVMParams params )
{
	img.copyTo( imgDst );
	Mat trainSamples, trainClasses;
	prepare_train_data( trainSamples, trainClasses );
	// Обучаем классификатор
	CvSVM svmClassifier( trainSamples, trainClasses, Mat(), Mat(), params );
	// Сохраняем обученный классификатор (можно потом его грузить в другой программе)
	svmClassifier.save("Classifier.dat");
	// Проверим как обучился
	Mat testSample( 1, 3, CV_32FC1 );
	for( int y = 2; y < img.rows-2; y += testStep )
	{
		for( int x = 2; x < img.cols-2; x += testStep )
		{
			// Берем цвет точки изображения
			Vec3b val=img.at<Vec3b>(y,x);
			// Сооружаем вектор признаков (feature)
			testSample.at<float>(0) = val(0);
			testSample.at<float>(1) = val(1);
			testSample.at<float>(2) = val(2);
			// Вычисляем прогноз
			float response = svmClassifier.predict( testSample );
			//cout << response << endl;
			if(response)
			{
			circle( imgDst, Point(x,y), 2, cvScalar(255.0*response,0,0,0), 1 );
			}
		}
	}
}

//-----------------------------------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------------------------------

int main( int argc, char** argv )
{
	int width;
	int height;
	Mat Img=imread("C:\\ImagesForTest\\hand2.jpg");
	// Здесь можно задать любую цветовую схему
	cv::cvtColor(Img,Img,cv::COLOR_BGR2HSV);
	width=Img.cols;
	height=Img.rows;
	img=Img.clone();
	imgDst.create( width, height, CV_8UC3 );
	fs.open("Samples.dat");
	imshow("Image", Img);
	setMouseCallback("Image",mouseHandler,(void*)&Img);
	while(1) 
	{
		// Ждем кнопку
		char key = waitKey(0);
		// По ESC выходим
		if (key == 27) break;
		switch(key) 
		{
		// r - запустить обучение и тестирование
		case 'r':
			if(fs.is_open()){fs.close();}
			CvSVMParams params;
			params.svm_type = CvSVM::NU_SVC;
			params.kernel_type = CvSVM::POLY;
			params.degree = 0.5;
			params.gamma = 1;
			params.coef0 = 1;
			params.C = 50;
			params.nu = 0.5;
			params.p = 0;
			params.term_crit = cvTermCriteria(CV_TERMCRIT_ITER, 1000, 0.01);
			find_decision_boundary_SVM( params );
			namedWindow( "Результат классификации SVM", WINDOW_AUTOSIZE );
			cv::cvtColor(imgDst,imgDst,cv::COLOR_HSV2BGR);
			imshow( "Результат классификации SVM", imgDst );
			break;
		}
	}
	if(fs.is_open()){fs.close();}
	return 0;
}
  • Like 1

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

Спасибо ограмное! Очень крутая штука, хотел посмотреть как код работает

и застрял на такой ошибки

c:\program files\microsoft visual studio 9.0\vc\include\sstream(333) : error C2248: 'std::basic_streambuf<_Elem,_Traits>::operator =' : cannot access private member declared in class 'std::basic_streambuf<_Elem,_Traits>'

1> with

1> [

1> _Elem=char,

1> _Traits=std::char_traits<char>

1> ]

1> c:\program files\microsoft visual studio 9.0\vc\include\streambuf(22) : see declaration of 'std::basic_streambuf<_Elem,_Traits>::operator ='

1> with

1> [

1> _Elem=char,

1> _Traits=std::char_traits<char>

1> ]

1> This diagnostic occurred in the compiler generated function 'std::basic_stringbuf<_Elem,_Traits,_Alloc> &std::basic_stringbuf<_Elem,_Traits,_Alloc>::operator =(const std::basic_stringbuf<_Elem,_Traits,_Alloc> &)'

1> with

1> [

1> _Elem=char,

1> _Traits=std::char_traits<char>,

1> _Alloc=std::allocator<char>

1> ]

Не могу понять в чем дело, если сталкивались с таким можете помочь? Гуглил и гугл мне ничего не нашел толкового

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

У меня все компилировалось без ошибок в VS2010.

Это связано с файловым вводом-выводом, можно просто эти операции на C - стиль перевести (fopen, fprintf, fscanf,...), тогда ошибок быть не должно.

Здесь можно убрать ss вообще и считывать прямо из ifs, может ошибка уйдет.


getline (ifs,line);
ss = stringstream(line);
ss >> N;
Col[0]=N;
ss >> N;
Col[1]=N;
ss >> N;
Col[2]=N;
ss >> N;
Label=N;
[/code] то есть вместо этого куска поставить такой
[code]
ifs >> N;
Col[0]=N;
ifs >> N;
Col[1]=N;
ifs >> N;
Col[2]=N;
ifs >> N;
Label=N;

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

Да действительно из 8, поставил 10 все заработало) Спасибо еще раз за помощь эта штука мне очень помогла)!

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

Еще одна проблема, я поменял v2.3.1 на v2.4

перегрузил комп и у меня появляются теперь ошибки,

Необработанное исключение в "0x7c812afb" в "Obrabot4ik_v3.2.exe": Исключение Microsoft C++: cv::Exception по адресу 0x0011ca34..

Я так понимаю что ошибки эти, из-за того что он не может найти картинку, но я описал все верно

Mat Img=imread("C:\\proj\\proj2\\Obrabot4ik_v3.2\\Obrabot4ik_v3.2\\21.jpg");

и даже распихал эту картинку по всем директориям проекта и все без результатно, не подскажите как быть.

А если вернуться в версию 2.3.1 то тоже Необработанное исключение в "0x7c812afb" в "Obrabot4ik_v3.2.exe": Исключение Microsoft C++: cv::Exception по адресу 0x0011f130.. но вылетает только после того когда нажимаю r?))))

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

У меня тоже иногда по r вылетает, я не разбирался почему, но предполагаю, что возникают проблемы с созданием классификатора по предоставленным данным.

По первому случаю не могу ничего сказать, у меня кстати 2.4, и работает вполне сносно.

Это кстати, может быть, если проект построить сначала в другом профиле, "Debug" например, а затем перейти в "Release" и нажать не "Rebuild", а "Build".

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

добавил Габора, пробовал, чтобы найти глаза, если тренировать на 1 фото, то находит много лишнего(возможно надо много тренировочных образцов) и фичи габора долго считаются.

#include <iostream>

#include <vector>

#include <stdio.h>

#include "opencv2/core/core.hpp"

#include "opencv2/ml/ml.hpp"

#include "opencv2/highgui/highgui.hpp"

#include "opencv2/contrib/contrib.hpp"

#include "opencv2/video/tracking.hpp"

#include "opencv2/imgproc/imgproc.hpp"

#include "fstream"

#include "cvgabor.h"


using namespace std;

using namespace cv;



//расширить пример на несколько классов?


Mat img, imgDst; //перенести?

const int N_scales=5;

const int N_orientations=8;

const int patch_size=10;

int N_Descriptors=N_scales*N_orientations;

// создаем вектор дескрипторов по количеству фильтров в банке

vector<Mat> Descriptors(N_Descriptors);

vector<float> mem_labels;//флоат только для того чтобы потом переделать в матрицу 

//Mat samples(0,N_Descriptors,CV_32FC1);

Mat samples(0,N_Descriptors*patch_size*patch_size,CV_32FC1);


const int testStep = 5;//?


// Файловый поток для сохранения обучающих пар

ofstream fs;


// Сохраняем патч (20х20=400 точек)

void SavePatch(ofstream &fs, Mat &Img, bool isSkin)

{

    for( int r = 0; r < Img.rows; r++ )

    {

        for( int c = 0; c < Img.cols; c++ )

        {

            Vec3b val=Img.at<Vec3b>(r,c);//легче просто развернуть в вектор?

            fs << (int)val[0] << " " << (int)val[1] << " " << (int)val[2] << " " << (int)isSkin << endl;

        }

    }

}


// Заполнение матриц для обучения

void prepare_train_data( Mat& samples, Mat& classes )

{ 

    vector<Vec3b> samples_vector;

    vector<int> labels_vector;

    ifstream ifs("Samples.dat");

    int N;

    Vec3b Col;

    int Label;


    if (ifs.is_open())

    {

        while ( ifs.good() )

        {

			ifs >> N;

            Col[0]=N;

            ifs >> N;

            Col[1]=N;

            ifs >> N;

            Col[2]=N;

            ifs >> N;

            Label=N;


            samples_vector.push_back(Col);

            labels_vector.push_back(Label);

        }

        ifs.close();

    }

    samples=Mat(samples_vector.size(),3,CV_32FC1);

    classes=Mat(labels_vector.size(),1,CV_32FC1);


    for(int i=0;i<samples_vector.size();i++)

    {

		samples.at<float>(i,0)=samples_vector[i][0];

		samples.at<float>(i,1)=samples_vector[i][1];

		samples.at<float>(i,2)=samples_vector[i][2];

		classes.at<float>(i)=labels_vector[i];

    }

    samples_vector.clear();

    labels_vector.clear();

}


//-----------------------------------------------------------------------------------------------------

//

//-----------------------------------------------------------------------------------------------------

void get_gabor_feature_for_point(vector<Mat>& Descriptors,Mat& feature_vector,Point& pt)

{

	//должно представлять собой строку в общей матрице семплов

	//возможно есть какой то способ скопировать сразу строку?


	//проходимся по всем изображениям и берем с них точку и кладем в вектор

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

	for(int i=0;i<Descriptors.size();++i)

	{

		feature_vector.at<float>(0,i)= Descriptors[i].at<float>(pt.y,pt.x);

	}

}

void get_gabor_feature_for_patch(vector<Mat>& Descriptors,Mat& feature_vector,Point& pt,int patch_size)

{

	//должно представлять собой строку в общей матрице семплов

	//возможно есть какой то способ скопировать сразу строку?


	int d=patch_size/2;

	int k=0;

	//есть разница как мы компануем вектор?

	for(int y=pt.y-d;y<pt.y+d;++y)

	{

		for(int x=pt.x-d;x<pt.x+d;++x)

		{

			for(int i=0;i<Descriptors.size();++i)

			{

				feature_vector.at<float>(0,k)= Descriptors[i].at<float>(y,x);

				++k;

			}

		}

	}

}

void mouseHandler(int event, int x, int y, int flags, void *param)

{

	//надо еще добавить отрисовку точек

	//и очистку всех


    Mat* Img=(Mat*)param;

    Mat Img1;

    Img1=Img->clone();


    switch(event) 

    {

		// Левая кнопка мыши (положительные образцы)

		case CV_EVENT_LBUTTONDOWN:

			{

				fprintf(stdout, "Left button down (%d, %d).\n", x, y);


				//берется небольшой патч

				/*if( x>10 && y>10 && x<Img1.cols-10 && y<Img1.rows-10)

				{

						Mat tmp = Img1(Rect(x-10,y-10,20,20));

						SavePatch(fs,tmp,1);

				}*/


				////берем фичи габора для точки

				//Mat feature_vector(1,Descriptors.size(),CV_32FC1);

				//get_gabor_feature_for_point(Descriptors,feature_vector,Point(x,y));

				//mem_labels.push_back(1);

				//samples.push_back(feature_vector.row(0));


				//берем фичи габора для патча

				Mat feature_vector(1,Descriptors.size()*patch_size*patch_size,CV_32FC1);

				get_gabor_feature_for_patch(Descriptors,feature_vector,Point(x,y),patch_size);

				samples.push_back(feature_vector.row(0));

				mem_labels.push_back(1);

			}


			break;


		// Правая кнопка мыши (отрицательные образцы)

		case CV_EVENT_RBUTTONDOWN:

			{

				fprintf(stdout, "Right button down (%d, %d).\n", x, y);


				//берется небольшой патч

				/*if( x>10 && y>10 && x<Img1.cols-10 && y<Img1.rows-10)

				{

						cv::Mat tmp = Img1(cv::Rect(x-10,y-10,20,20));

						SavePatch(fs,tmp,0);

				}*/


				////берем фичи габора для точки

				//Mat feature_vector(1,Descriptors.size(),CV_32FC1);

				//get_gabor_feature_for_point(Descriptors,feature_vector,Point(x,y));

				//samples.push_back(feature_vector.row(0));

				//mem_labels.push_back(0);


				//берем фичи габора для патча

				Mat feature_vector(1,Descriptors.size()*patch_size*patch_size,CV_32FC1);

				get_gabor_feature_for_patch(Descriptors,feature_vector,Point(x,y),patch_size);

				samples.push_back(feature_vector.row(0));

				mem_labels.push_back(0);

			}

			break;


		// Перемещение мыши

		case CV_EVENT_MOUSEMOVE:

			{

				int d=patch_size/2; 

				// рисуем прямоугольник

				rectangle(Img1, Point(x-d,y-d),Point(x+d,y+d),Scalar(0, 0, 255, 0), 1, 8, 0);

				imshow("Image", Img1);

			}

			break;

    }

}


void find_decision_boundary_SVM_( CvSVMParams params)

{

    img.copyTo(imgDst);


	//переводим вектор в матрицу

	Mat trainClasses(mem_labels);


    // Обучаем классификатор

    CvSVM svmClassifier( samples, trainClasses, Mat(), Mat(), params );


    // Сохраняем обученный классификатор (можно потом его грузить в другой программе)

    svmClassifier.save("Classifier.dat");


    // Проверим как обучился

    //Mat testSample( 1, N_Descriptors, CV_32FC1 );

	Mat testSample( 1, N_Descriptors*patch_size*patch_size, CV_32FC1 );

    for( int y = patch_size/2; y < img.rows-patch_size/2; y += testStep )

    {

        for( int x = patch_size/2; x < img.cols-patch_size/2; x += testStep )

        {

            //// Берем цвет точки изображения

            //Vec3b val=img.at<Vec3b>(y,x);

            //// Сооружаем вектор признаков (feature)

            //testSample.at<float>(0) = val(0);

            //testSample.at<float>(1) = val(1);

            //testSample.at<float>(2) = val(2);


			//get_gabor_feature_for_point(Descriptors,testSample,Point(x,y));

			get_gabor_feature_for_patch(Descriptors,testSample,Point(x,y),patch_size);


            // Вычисляем прогноз

            float response = svmClassifier.predict( testSample );

            //cout << response << endl;

            if(response) //подругому маркировать?

            {

				circle( imgDst, Point(x,y), 2, cvScalar(255.0*response,0,0,0), 1);

            }

        }

    }

}

void find_decision_boundary_SVM( CvSVMParams params )

{

    img.copyTo( imgDst );


    Mat trainSamples, trainClasses;

	//представляем в виде матрицы кол-во семплов= кол-во строк

	//кол-во колонок равно размерности сэмпла

    prepare_train_data( trainSamples, trainClasses );


    // Обучаем классификатор

    CvSVM svmClassifier( trainSamples, trainClasses, Mat(), Mat(), params );

    // Сохраняем обученный классификатор (можно потом его грузить в другой программе)

    svmClassifier.save("Classifier.dat");

    // Проверим как обучился

    Mat testSample( 1, 3, CV_32FC1 );

    for( int y = 2; y < img.rows-2; y += testStep )

    {

        for( int x = 2; x < img.cols-2; x += testStep )

        {

            // Берем цвет точки изображения

            Vec3b val=img.at<Vec3b>(y,x);

            // Сооружаем вектор признаков (feature)

            testSample.at<float>(0) = val(0);

            testSample.at<float>(1) = val(1);

            testSample.at<float>(2) = val(2);

            // Вычисляем прогноз

            float response = svmClassifier.predict( testSample );

            //cout << response << endl;

            if(response)

            {

				circle( imgDst, Point(x,y), 2, cvScalar(255.0*response,0,0,0), 1 );

            }

        }

    }

}

//-----------------------------------------------------------------------------------------------------

//

//-----------------------------------------------------------------------------------------------------

//надо сначала посчитать габора для всего изображения, а потом взять для точек которых нужно

void gabor_extraction(Mat& img,vector<Mat>& Descriptors)

{

	Size img_size = Size(img.cols,img.rows);

	Mat imtmp(img.rows,img.cols,CV_32FC1);

	img.convertTo(imtmp,CV_32FC1);


	CvGabor *Gabor;


	double sigma=2*CV_PI; // По умолчанию 2*PI, но приемлемые результаты дают и PI/2


	// Цикл по масштабам

	for (int v=0;v<N_scales;v++)

	{

		// Цикл по ориентациям

		for (int u=0;u<N_orientations;u++) 

		{

			Gabor = new CvGabor(u,(double)v,sigma);

			Descriptors[v*N_orientations+u]=Mat(imtmp.rows,imtmp.cols,CV_32FC1);

			Gabor->conv_img_a(imtmp,Descriptors[v*N_orientations+u],CV_GABOR_MAG);//сам процесс?


			////for test

			//char str[100];//

			//sprintf(str,"gabor_%d_%d.png",v,u);//

			//imwrite(str,Descriptors[v*N_orientations+u]);//


			// Часто применяют сглаживание

			cv::GaussianBlur(Descriptors[v*N_orientations+u],Descriptors[v*N_orientations+u],Size(21,21),30);

			delete Gabor;

		}

	}

}


void extract_features(Mat& img,vector<Mat>& Descriptors)

{			

	Size img_size = Size(img.cols,img.rows);

	Mat gray(img_size,CV_8UC1);

	if(img.channels()==3)

	{

		cv::cvtColor(img,gray,CV_BGR2GRAY);

	}

	else

	{

		img.copyTo(gray);

	}


	gabor_extraction(gray,Descriptors);

}

int main( int argc, char** argv )

{

    int width;

    int height;


    Mat Img=imread("./data/lena.jpg");


	extract_features(Img,Descriptors);


    // Здесь можно задать любую цветовую схему

    cv::cvtColor(Img,Img,CV_BGR2HSV);


    width=Img.cols;

    height=Img.rows;

    img=Img.clone();

    imgDst.create( width, height, CV_8UC3 );

    fs.open("Samples.dat");

    imshow("Image", Img);

    setMouseCallback("Image",mouseHandler,(void*)&Img);


    while(1) 

    {

        // Ждем кнопку

        char key = waitKey(0);


        // По ESC выходим

        if (key == 27) break;


        switch(key) 

        {

        // r - запустить обучение и тестирование

        case 'r':

            if(fs.is_open()){fs.close();}

            CvSVMParams params;

            params.svm_type = CvSVM::NU_SVC;  //посмотреть параметры

            params.kernel_type = CvSVM::POLY; //

            params.degree = 0.5;//

            params.gamma = 1;//

            params.coef0 = 1;//

            params.C = 50;//

            params.nu = 0.5;//

            params.p = 0;//

            params.term_crit = cvTermCriteria(CV_TERMCRIT_ITER, 1000, 0.01);//


            //find_decision_boundary_SVM( params );

			find_decision_boundary_SVM_(params);

            namedWindow( "Результат классификации SVM", WINDOW_AUTOSIZE );

            cv::cvtColor(imgDst,imgDst,CV_HSV2BGR);

            imshow( "Результат классификации SVM", imgDst );

            break;

        }

    }

    if(fs.is_open()){fs.close();}

    return 0;

}

  • Like 1

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

Набросал программку обучающуюся нужному цвету (в принципе, можно ему любой классификатор подсунуть, я пользовался примером points_classifier.cpp и вынул оттуда SVM).

Юзать так:

Левой кнопкой мыши набираем образцы относящиеся к нашему объекту (положительные примеры),

а правой не относящиеся (отрицательные примеры).

Затем жмем 'r' и видим результат.

Что то типа этого (слева результат работы классификатора, справа картинка в пространстве HSV):

attachicon.gifSVM_SKIN.PNG

attachicon.gifSVM_FTUFF.PNG

Вот код:

#include <iostream>
#include <vector>
#include <stdio.h>
#include "opencv2/core/core.hpp"
#include "opencv2/core/gpumat.hpp"
#include "opencv2/core/opengl_interop.hpp"
#include "opencv2/gpu/gpu.hpp"
#include "opencv2/ml/ml.hpp"
#include "opencv2/highgui/highgui.hpp"
#include "opencv2/contrib/contrib.hpp"
#include "opencv2/video/tracking.hpp"
#include "opencv2/imgproc/imgproc.hpp"
#include "fstream"
#include "iostream"
using namespace std;
using namespace cv;

// 
Mat img, imgDst;

const int testStep = 5;

// Файловый поток для сохранения обучающих пар
ofstream fs;

// Сохраняем патч (20х20=400 точек)
void SavePatch(ofstream &fs, Mat &Img, bool isSkin)
{
	for( int r = 0; r < Img.rows; r++ )
	{
		for( int c = 0; c < Img.cols; c++ )
		{
			Vec3b val=Img.at<Vec3b>(r,c);
			fs << (int)val[0] << " " << (int)val[1] << " " << (int)val[2] << " " << (int)isSkin << endl;
		}
	}
}

// Заполнение матриц для обучения
void prepare_train_data( Mat& samples, Mat& classes )
{
	string line;	
	vector<Vec3b>	samples_vector;
	vector<int>		labels_vector;
	ifstream ifs("Samples.dat");
	stringstream ss;
	int N;
	Vec3b Col;
	int Label;
	
	if (ifs.is_open())
	{
		while ( ifs.good() )
		{
			getline (ifs,line);
			//cout << line << endl;
			ss = stringstream(line);
			
			ss >> N;
			Col[0]=N;
			ss >> N;
			Col[1]=N;
			ss >> N;
			Col[2]=N;
			ss >> N;
			Label=N;

			samples_vector.push_back(Col);
			labels_vector.push_back(Label);
		}
		ifs.close();
	}
	samples=Mat(samples_vector.size(),3,CV_32FC1);
	classes=Mat(labels_vector.size(),1,CV_32FC1);

	for(int i=0;i<samples_vector.size();i++)
	{
	samples.at<Vec3f>(i)[0]=samples_vector[i][0];
	samples.at<Vec3f>(i)[1]=samples_vector[i][1];
	samples.at<Vec3f>(i)[2]=samples_vector[i][2];
	classes.at<float>(i)=labels_vector[i];
	}
	samples_vector.clear();
	labels_vector.clear();
}

//-----------------------------------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------------------------------
void mouseHandler(int event, int x, int y, int flags, void *param)
{
	Mat* Img=(Mat*)param;
	Mat Img1;
	Img1=Img->clone();
	
	switch(event) 
	{
	// Левая кнопка мыши (положительные образцы)
	case CV_EVENT_LBUTTONDOWN:
		fprintf(stdout, "Left button down (%d, %d).\n", x, y);

		if( x>10 && y>10 && x<Img1.cols-10 && y<Img1.rows-10)
		{
			Mat tmp = Img1(Rect(x-10,y-10,20,20));
			SavePatch(fs,tmp,1);
		}

		break;

	// Правая кнопка мыши (отрицательные образцы)
	case CV_EVENT_RBUTTONDOWN:
		fprintf(stdout, "Right button down (%d, %d).\n", x, y);

		if( x>10 && y>10 && x<Img1.cols-10 && y<Img1.rows-10)
		{
			cv::Mat tmp = Img1(cv::Rect(x-10,y-10,20,20));
			SavePatch(fs,tmp,0);
		}

		break;

	// Перемещение мыши
	case CV_EVENT_MOUSEMOVE:
		// рисуем прямоугольник
		rectangle(Img1, Point(x-10,y-10),Point(x+10,y+10),Scalar(0, 0, 255, 0), 1, 8, 0);
		imshow("Image", Img1);
		break;
	}
}


void find_decision_boundary_SVM( CvSVMParams params )
{
	img.copyTo( imgDst );

	Mat trainSamples, trainClasses;
	prepare_train_data( trainSamples, trainClasses );

	// Обучаем классификатор
	CvSVM svmClassifier( trainSamples, trainClasses, Mat(), Mat(), params );
	// Сохраняем обученный классификатор (можно потом его грузить в другой программе)
	svmClassifier.save("Classifier.dat");
	// Проверим как обучился
	Mat testSample( 1, 3, CV_32FC1 );
	for( int y = 2; y < img.rows-2; y += testStep )
	{
		for( int x = 2; x < img.cols-2; x += testStep )
		{
			// Берем цвет точки изображения
			Vec3b val=img.at<Vec3b>(y,x);
			// Сооружаем вектор признаков (feature)
			testSample.at<float>(0) = val(0);
			testSample.at<float>(1) = val(1);
			testSample.at<float>(2) = val(2);
			// Вычисляем прогноз
			float response = svmClassifier.predict( testSample );
			//cout << response << endl;
			if(response)
			{
			circle( imgDst, Point(x,y), 2, cvScalar(255.0*response,0,0,0), 1 );
			}
		}
	}
}



//-----------------------------------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------------------------------
int main( int argc, char** argv )
{
	int width;
	int height;

	Mat Img=imread("C:\\ImagesForTest\\hand2.jpg");
	
	// Здесь можно задать любую цветовую схему
	cv::cvtColor(Img,Img,cv::COLOR_BGR2HSV);
	
	width=Img.cols;
	height=Img.rows;
	img=Img.clone();
	imgDst.create( width, height, CV_8UC3 );
	fs.open("Samples.dat");
	imshow("Image", Img);
	setMouseCallback("Image",mouseHandler,(void*)&Img);

	while(1) 
	{
		// Ждем кнопку
		char key = waitKey(0);

		// По ESC выходим
		if (key == 27) break;

		switch(key) 
		{
		// r - запустить обучение и тестирование
		case 'r':
			if(fs.is_open()){fs.close();}
			CvSVMParams params;
			params.svm_type = CvSVM::NU_SVC;
			params.kernel_type = CvSVM::POLY;
			params.degree = 0.5;
			params.gamma = 1;
			params.coef0 = 1;
			params.C = 50;
			params.nu = 0.5;
			params.p = 0;
			params.term_crit = cvTermCriteria(CV_TERMCRIT_ITER, 1000, 0.01);

			find_decision_boundary_SVM( params );
			namedWindow( "Результат классификации SVM", WINDOW_AUTOSIZE );
			cv::cvtColor(imgDst,imgDst,cv::COLOR_HSV2BGR);
			imshow( "Результат классификации SVM", imgDst );
			break;
		}
	}
	if(fs.is_open()){fs.close();}
	return 0;
}

Код немного устарел, выявлены проблемы с константами 

EVENT_LBUTTONDOWN и т.д. (используются без CV_)

Но главная проблема с отсутствием CvSVM, почему не могу его использовать? Приходится через Ptr<ml::SVM>

void find_decision_boundary_SVM( ml::SVM::Params params ) 
{  
img.copyTo( imgDst );
Mat trainSamples, trainClasses;  
prepare_train_data( trainSamples, trainClasses );

// Обучаем классификатор  
Ptr<ml::SVM> svm = ml::SVM::create(params);  
svm->train(trainSamples, 0, trainClasses);  

// Старый вызов
//CvSVM svmClassifier( trainSamples, trainClasses, Mat(), Mat(), params );


// Сохраняем обученный классификатор (можно потом его грузить в другой программе)  
svm->save("Classifier.dat");

Не пойму в чем проблема? Скрин ошибки ниже.

 

iCYaMe9.png

 

 

Кажись проблема здесь была 

classes=Mat(labels_vector.size(),1,CV_32FC1);

сменить на:

classes=Mat(labels_vector.size(),1,CV_32SC1);

не врублюсь как пользоваться,  если кликаю один раз левой мышкой и один раз правой, то норм, если например 7 раз левой и 7 раз правой то крашит, не понял пока при какой последовательности. И работает как-то странно, выбираю левой мышкой пикселы положительные, а правой отрицательные, в итоге получается хрень.

pHdbMEM.png

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

А в таких местах

classes.at<float>(i)=labels_vector[i];

заменили на int?

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

А в таких местах

classes.at<float>(i)=labels_vector[i];

заменили на int?

Попробовал, тоже самое..

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

Вот кусок обучения SVM от более нового проекта:

Mat src;// mouth image
CvSVMParams params;
params.svm_type =CvSVM::C_SVC;
params.kernel_type=CvSVM::RBF;
params.gamma=0.5;
params.C=16;
params.term_crit = cvTermCriteria(CV_TERMCRIT_ITER,100,1e-6);
SVM svm;
Mat data  =Mat(22,8,CV_32FC1);
Mat label =Mat(22,1,CV_32FC1);
int index =0;
vector<string> folders=list_folder("./Data");
for(size_t i=0;i<folders.size();++i)
{ 
cout<<i<<"..."<<endl;
vector<string> files=list_file(folders.at(i));
string folder_path=folders.at(i);
string label_foder =folder_path.substr(folder_path.length()-1);
for(size_t j =0;j<files.size();++j)
{
src=imread(files.at(j));
if(src.empty()) continue;
Mat feature=getFeatures(cc(src));
feature.copyTo(data.row(j));
label.at<float>(j)=i;
}
}
svm.train_auto (data,label,Mat(),Mat(),params);
svm.save("mouthstate.xml");
return true;

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

2Smorodov: за последний месяц в SVM::train_auto в OpenCV 3.0 было исправлено несколько ошибок (одна из них моим микро-патчем). У них там код переписн полностью и пока ещё сырой. Его правильность гарантировать трудно.

  • Like 1

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

Я пока не использую SVM в проектах, надеюсь когда понадобится, все более-менее устаканится.

А так, да, 3.0 сейчас сильно меняется. И примеры не поспевают за изменениями, это напрягает малость.

Помнится 2.0 тоже очень глючная была.

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

в 2.4.10 labels должны быть типа CV_32FC1 и еще ядро габора теперь можно получить через getGaborKernel и использовать в filter2D

https://github.com/Itseez/opencv/blob/7d4d28605087ec2d3878f9467aea313a2acdfd49/modules/imgproc/src/gabor.cpp

 

какой примерно range параметров должен быть для банка фильтров габора?

//! returns the Gabor kernel with the specified parameters
CV_EXPORTS_W Mat getGaborKernel( Size ksize, double sigma, double theta, double lambd,
                                 double gamma, double psi=CV_PI*0.5, int ktype=CV_64F );

 

 

 

визуализация самого ядра

https://gist.github.com/YusukeSuzuki/9910030

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

Лень въезжать какие буквы что здесь обозначают. На форуме были примеры.

 

Я думаю что углы от 0 до PI , сдвиг фазы 0 и PI получаются автоматом при перевороте, если надо больше, то надо варьировать еще и фазу, ну и масштаб в зависимости от целей. Обычно меняют угол и масштаб.

 

Классика 8 углов и 5 масштабов.

 

Есть еще Log-Gabor: http://www.iv.optica.csic.es/page49/page16/page16.html

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

посмотрел еще раз код из этого топика:

 

 

там с opencv'шным ядром некоторые разногласия в терминологии.

 

Но немного изменив этот код для визуализации можно посмотреть результат фильтрации, и в основном на результат влияет угол поворота ядра theta и сам размер ядра kernel_size(возможно это и есть скейл или от него зависит), а остальные параметры можно выставить так чтобы изображение было видно(хотя тут тоже вопрос надо лишь только контуры, толстые контуры или чем больше тем лучше), а вообще по виду похоже чем то на LBP.

 

UPD:

все таки я не правильно сказал, должно быть так :

http://www.juergenwiki.de/work/wiki/doku.php?id=public:gabor_filter

 

The orientation can be modified by theta and the scale by i.e. lambda.

 

а по коду в opencv, хотя там есть еще 1 scale

double cscale = CV_PI*2/lambd;

 

 

И еще тут замечание по lamda 

http://matlabserver.cs.rug.nl/edgedetectionweb/web/edgedetection_params.html#references

This is the wavelength of the cosine factor of the Gabor filter kernel and herewith the preferred wavelength of this filter. Its value is specified in pixels. Valid values are real numbers equal to or greater than 2. The value λ=2 should not be used in combination with phase offset φ=-90 or φ=90 because in these cases the Gabor function is sampled in its zero crossings. In order to prevent the occurence of undesired effects at the image borders, the wavelength value should be smaller than one fifth of the input image size.

 

а так же там дается соотношение

σ = 0.56 λ.

 

я экспериментально подбирал lamda= 7, sigma = 4 , что примерно соответствует этому уравнению 7*0.56=3.92

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

Создайте учётную запись или войдите для комментирования

Вы должны быть пользователем, чтобы оставить комментарий

Создать учётную запись

Зарегистрируйтесь для создания учётной записи. Это просто!

Зарегистрировать учётную запись

Войти

Уже зарегистрированы? Войдите здесь.

Войти сейчас


  • Сейчас на странице   0 пользователей

    Нет пользователей, просматривающих эту страницу

×