Прежде всего, вы можете попробовать использовать JFeatureLib, javasift (здесь вы найдете javasift in android application).
В бинарной кластеризации вы можете посмотреть Епископ «Распознавание образов и машинное обучение» см. На страницах 461-465 главы «Смеси распределений Бернулли». У меня есть реализация (см. Код ниже), но все комментарии на русском языке (я слишком ленив, чтобы перевести его). This video показывает, как он работает для двоичных изображений рукописных цифр (бинаризованный MNIST). Он нашел центры кластеров.
#include <iostream>
#include <vector>
#include <stdio.h>
#include <opencv2/opencv.hpp>
#include <numeric>
#include "fstream"
#include "iostream"
using namespace std;
using namespace cv;
//-----------------------------------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------------------------------
inline void endian_swap(unsigned short& x)
{
x = (x>>8) |
(x<<8);
}
//-----------------------------------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------------------------------
inline void endian_swap(unsigned int& x)
{
x = (x>>24) |
((x<<8) & 0x00FF0000) |
((x>>8) & 0x0000FF00) |
(x<<24);
}
//-----------------------------------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------------------------------
inline void endian_swap(unsigned __int64& x)
{
x = (x>>56) |
((x<<40) & 0x00FF000000000000) |
((x<<24) & 0x0000FF0000000000) |
((x<<8) & 0x000000FF00000000) |
((x>>8) & 0x00000000FF000000) |
((x>>24) & 0x0000000000FF0000) |
((x>>40) & 0x000000000000FF00) |
(x<<56);
}
//-----------------------------------------------------------------------------------------------------
// Чтение меток NMIST (здесь не используется)
//-----------------------------------------------------------------------------------------------------
void read_mnist_labels(string fname,vector<unsigned char>& vec_lbl)
{
ifstream file;
file.open(fname,ifstream::in | ifstream::binary);
if (file.is_open())
{
unsigned int magic_number=0;
unsigned int number_of_labels=0;
file.read((char*)&magic_number,sizeof(magic_number)); //если перевернуть то будет 2051
endian_swap(magic_number);
file.read((char*)&number_of_labels,sizeof(number_of_labels));//если перевернуть будет 10к
endian_swap(number_of_labels);
cout << "magic_number=" << magic_number << endl;
cout << "number_of_labels=" << number_of_labels << endl;
for(int i=0;i<number_of_labels;++i)
{
unsigned char t_ch=0;
file.read((char*)&t_ch,sizeof(t_ch));
vec_lbl.push_back(t_ch);
}
}
}
//-----------------------------------------------------------------------------------------------------
// Чтение изображений MNIST, на выходе вектор матриц с изображениями цифр
//-----------------------------------------------------------------------------------------------------
void read_mnist(string fname,vector<Mat>& vec_img)
{
ifstream file;
file.open(fname,ifstream::in | ifstream::binary);
if (file.is_open())
{
unsigned int magic_number=0;
unsigned int number_of_images=0;
unsigned int n_rows=0;
unsigned int n_cols=0;
file.read((char*)&magic_number,sizeof(magic_number)); //если перевернуть то будет 2051
endian_swap(magic_number);
file.read((char*)&number_of_images,sizeof(number_of_images));//если перевернуть будет 10к
endian_swap(number_of_images);
file.read((char*)&n_rows,sizeof(n_rows));
endian_swap(n_rows);
file.read((char*)&n_cols,sizeof(n_cols));
endian_swap(n_cols);
cout << "Магическое число=" << magic_number << endl;
cout << "Высота изображений=" << n_rows << endl;
cout << "Ширина изображений=" << n_cols << endl;
cout << "Количество изображений=" << number_of_images << endl;
for(int i=0;i<number_of_images;++i)
{
Mat temp(n_rows,n_cols,CV_8UC1);
for(int r=0;r<n_rows;++r)
{
for(int c=0;c<n_cols;++c)
{
unsigned char t_ch=0;
file.read((char*)&t_ch,sizeof(t_ch));
//тут идет запись матрицы 28х28 в вектор
temp.at<unsigned char>(r,c)= t_ch; //получаем бинаризованные изображения
}
}
vec_img.push_back(temp);
}
}
}
//-----------------------------------------------------------------------------------------------------
// Считаем правдоподобие выборки данных x для распределения Бернулли с параметром mu
//-----------------------------------------------------------------------------------------------------
double Likelihood(Mat& x,Mat& mu)
{
double P=1;
int n_rows=x.rows;
int n_cols=x.cols;
for(int r=0;r<n_rows;++r)
{
for(int c=0;c<n_cols;++c)
{
// Распределение бинарное, поэтому можем позволить себе такое безобразие.
if(x.at<double>(r,c)>0.5)
{
P*=mu.at<double>(r,c); // Если выпала единица
}
else
{
P*=1.0-mu.at<double>(r,c); // если выпал ноль
}
}
}
return P;
}
//-----------------------------------------------------------------------------------------------------
// E - шаг
// Вычисляем значения скрытых переменных.
// Постериорная вероятность принадлежности X к кластеру K
//-----------------------------------------------------------------------------------------------------
RNG rng;
void getGamma(vector<Mat>& vecX, vector<Mat>& vecMu,vector<double>& vecPi,Mat& Gamma)
{
int N=vecX.size();
int K=vecPi.size();
for(int n=0;n<N;n++)
{
// Вычислим знаменатель (Маргинальная вероятность по классу)
double denom=0;
for(int j=0;j<K;j++)
{
denom+=Likelihood(vecX[n],vecMu[j]);
}
for(int k=0;k<K;k++)
{
double p=Likelihood(vecX[n],vecMu[k]);
// Некоторая моя вычислительная алхимия
double tmp;
if(fabs(denom)>(2*DBL_MIN))
{
tmp=p/denom;
}else
{
tmp=rng.gaussian(1/(K*sqrt(3.0)))+1.0/K; // пошумим чуть-чуть
}
// ------------------------------------
Gamma.at<double>(n,k)=vecPi[k]*tmp;
// Для каждого X сумма вероятностей принадлежности ко всем кластерам равна 1
// (К какому нибудь кластеру он точно принадлежит.)
normalize(Gamma.row(n),Gamma.row(n),1,0,cv::NORM_L1);
}
}
// Проконтролируем на всякий случай границы (вероятность не может иметь значения вне диапазона [0;1])
normalize(Gamma,Gamma,0,1,cv::NORM_MINMAX);
}
//-----------------------------------------------------------------------------------------------------
// M - шаг. Теперь по полному набору переменных (наблюдаемые и скрытые) находим следующее приближение параметров распределения.
//-----------------------------------------------------------------------------------------------------
void getMuAndPi(vector<Mat>& vecX,Mat& Gamma,vector<Mat>& vecMu,vector<double>& vecPi)
{
int N=vecX.size();
int K=vecPi.size();
for(int k=0;k<K;k++)
{
double Nk=0;
for(int n=0;n<N;n++)
{
Nk+=Gamma.at<double>(n,k);
}
// Эффективное количество элементов, принадлежащих K-тому кластеру
cout << "N[" << k << "]=" << Nk << endl;
vecMu[k]=0;
for(int n=0;n<N;n++)
{
vecMu[k]+=(vecX[n].mul(Gamma.at<double>(n,k)));
}
vecMu[k]/=Nk; // Находим приближение центров кластеров
vecPi[k]=Nk/(double)N; // Находим приближение коэффициентов смеси
}
// Отмасштабируем коэффициенты смеси, чтобы сумма их была равна 1
double sumPi=0;
sumPi=std::accumulate(vecPi.begin(),vecPi.end(), 0.0);
transform(vecPi.begin(), vecPi.end(), vecPi.begin(), std::bind2nd(std::divides<double>(),sumPi));
}
//-----------------------------------------------------------------------------------------------------
// Инициализация параметров
//-----------------------------------------------------------------------------------------------------
void InitParameters(int N,int K,vector<Mat>& MNIST,vector<Mat>& vecX,Mat& Gamma,vector<Mat>& vecMu,vector<double>& vecPi)
{
Gamma=Mat::zeros(N,K,CV_64FC1);
Mat tmp;
int h=MNIST[0].cols;
int w=MNIST[0].rows;
vecX.resize(N);
// Это делается чтобы можно было задать объем обрабатываемых данных (через N)
for(int i=0;i<N;i++)
{
threshold(MNIST[i],tmp,128,1,CV_8UC1);
tmp.convertTo(vecX[i],CV_64FC1);
}
// инициализация центров кластеров
for(int k=0;k<K;k++)
{
Mat r(MNIST[0].size(),CV_64FC1);
randu(r,0,1);
normalize(r,r,MNIST[0].rows*MNIST[0].cols,0,cv::NORM_L1);
r*=0.5;
r+=0.25;
// Центры кластеров (все пиксели имеют яркость в около 0.5)
vecMu.push_back(r);
// коэффициенты смеси инициализируются так, чтобы их сумма была равна единице
vecPi.push_back(1.0/K);
}
}
//-----------------------------------------------------------------------------------------------------
// Отрисовка центров кластеров в один ряд на одном изображении
//-----------------------------------------------------------------------------------------------------
void DrawMu(Mat& dst, vector<Mat>& vecMu)
{
int rows=vecMu[0].rows;
int cols=vecMu[0].cols;
dst=Mat::zeros(rows, vecMu.size()*cols, CV_64FC1);
for(int i=0;i<vecMu.size();i++)
{
vecMu[i].copyTo(dst(Rect(i*cols,0,cols,rows)));
}
cv::normalize(dst,dst,0,1,cv::NORM_MINMAX);
}
//-----------------------------------------------------------------------------------------------------
// Bishop страницы 461-465 глава "Mixtures of Bernoulli distributions"
//-----------------------------------------------------------------------------------------------------
int main(int argc, char** argv)
{
// Количество кластеров, которое хотим получить
int N_clusters=10;
// Количество итераций EM - алгоритма.
int N_iter=50;
setlocale(LC_ALL, "Russian");
vector<Mat> MNIST;
// читаем изображения
cout << "Загрузка изображений." << endl;
read_mnist("D:/MNIST/t10k-images.idx3-ubyte",MNIST);
cout << "Загрузка изображений выполнена." << endl;
// создаем окно
namedWindow("result");
// размеры одного изображения
int rows=MNIST[0].rows;
int cols=MNIST[0].cols;
vector<Mat> vecX; // Входные векторы
Mat Gamma; // Мера ответственности K-того кластера за N-ный входной вектор
vector<Mat> vecMu; // Центры кластеров
vector<double> vecPi; // Коэффициенты смеси распределений
// Создаем и заполняем необходимые переменные
cout << "Инициализация параметров." << endl;
InitParameters(10000,N_clusters,MNIST,vecX,Gamma,vecMu,vecPi);
cout << "Инициализация параметров выполнена." << endl;
// Запишем все в видеофайл
VideoWriter vw=VideoWriter::VideoWriter("output.mpeg", CV_FOURCC('P','I','M','1'), 20, Size(cols*vecMu.size(),rows));
// собственно сам EM-алгоритм
// Критерием сходимости таких алгоритмов обычно является стабилизация значений центров кластеров
// но мне лень это делать, поэтому поставлю фиксированное количество итераций.
for(int iter=0;iter<N_iter;iter++)
{
cout << "Итерация №" << iter << endl;
getGamma(vecX,vecMu,vecPi,Gamma); // E - Шаг (расчет матожиданий коэффициентов)
cout << "E - шаг выполнен." << endl;
getMuAndPi(vecX,Gamma,vecMu,vecPi); // M - шаг (уточнение параметров распределения методом максимизации правдоподобия)
cout << "M - шаг выполнен." << endl;
// Отрисовка центров кластеров, чтобы не скучно было :)
Mat MuImg;
DrawMu(MuImg,vecMu);
imshow("result",MuImg);
MuImg.convertTo(MuImg,CV_8UC1,255);
cvtColor(MuImg,MuImg,cv::COLOR_GRAY2BGR);
//resize(MuImg,MuImg,Size(MuImg.cols*4,MuImg.rows*4));
vw<<MuImg; // Запись кадра в видеофайл
waitKey(30);
MuImg.release();
}
// Освободим видеофайл
vw.release();
// Закончили, ждем нажатия клавиши
waitKey(0);
destroyAllWindows();
return 0;
}
Вы можете реализовать свой собственный алгоритм просеять (Notic: SIFT является несвободным алгоритмом) – Hadi
Вопросов просят нас, чтобы рекомендовать или найти инструмент, ** библиотеки или любимые вне сайта ресурсов являются вне темы ** для Stack Overflow, поскольку они склонны привлекать упрямые ответы и спам. Вместо этого опишите проблему и то, что было сделано до сих пор, чтобы ее решить. –