2014-05-01 2 views
-1

안드로이드 용 OPENCV를 사용하여 바이너리 벡터를 클러스터링하는 방법을 찾아야합니다.Android 용 OpenCV에서 바이너리 벡터 클러스터링

저는 단어 모델 가방을 사용하고 있습니다. 지금까지는 자바를 사용하고 sift를 사용하여 PC에서 작업하고있었습니다. 문제는 안드로이드에 대한 OPENCV에 있지 않아서 ORB를 사용하려고 시도했지만, 바이너리 설명자입니다. 그래서 안드로이드에서 opencv를 사용하여 바이너리 벡터를 클러스터링하는 방법을 찾아야합니다.

+0

당신은 당신의 자신의 SIFT 알고리즘을 구현할 수는 (Notic가! SIFT는 비 자유 알고리즘) – Hadi

+0

질문 도구를 권장하거나 찾기 위해 우리를 요청, ** 라이브러리 또는 좋아하는 오프 사이트 자원 ** 주제와 관련있다 스택 오버플로는 해설 된 답변과 스팸을 끌어 들이기 쉽습니다. 대신 문제를 설명하고 지금까지 해결 된 문제를 설명하십시오. –

답변

0

먼저 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; 
} 
+0

바이너리 이미지는 바이너리 벡터이며, 바이너리 디스크립터도 바이너리 벡터입니다. 차이점은 무엇입니까? –