2014-05-01 49 views
-1

我需要找到一種方法來使用OPENCV爲android進行二進制向量聚類。Android中的OpenCV中的二進制向量聚類

我正在使用一包字模型,直到現在我正在使用java和使用篩選我的電腦上工作。 問題是篩選不是在android的OPENCV,所以我決定嘗試使用ORB,但它是一個二進制描述符。 所以我需要找到一種方法來在android上使用opencv對二進制向量進行聚類。

+0

您可以實現自己的篩選進算法(諾蒂奇:SIFT是一種非遊離算法) – Hadi

+0

問題,要求我們建議或找到一個工具,**庫或喜愛的異地資源是題外話爲**堆棧溢出,因爲他們傾向於吸引自以爲是的答案和垃圾郵件。相反,請描述問題以及到目前爲止解決問題所做的工作。 –

回答

0

首先您可以嘗試使用JFeatureLib,javasift(在這裏您可以找到javasift in android application)。

關於二元聚類,你可以看看 Bishop「模式識別和機器學習」見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

二進制圖像是二進制向量,二進制描述符也是二進制向量。有什麼區別? –