ひでみのアイデア帳

くだらないことなんだけど、忘れないために・・・

HOG(2)

HOG(1)ではOpenCVだったので手抜きです。 次はスクラッチしてみたものです。

#include <iostream>
#include <string>
#include <fstream>
#include <vector>
#include <opencv/cv.h>
#include <opencv2/core.hpp>
#include <opencv2/imgcodecs.hpp>
#include <opencv2/imgproc.hpp>
#include <opencv2/highgui.hpp>

#define N_BIN 9             // ヒストグラムのビン数
#define THETA (180 / N_BIN) // 分解能

#define CELL_SIZE   20  //セルサイズ (pixel)
#define BLOCK_SIZE  3   //ブロックサイズ (セル)

#define PI          3.14

/*
 * HOG 特徴量の書き出し
 */
int OutputHOGFeature(int nFeatureNum, double *pHOGFeatures, char* FileName)
{
  char   buf[256];
  FILE *fp;

  sscanf(FileName, "%s.png", buf);
  strcat(buf, ".hog");

  if(NULL == (fp=fopen(buf, "w"))){
    printf("Can not open the outfile.\n");
    return(1);
  }

  for(int i=0; i<nFeatureNum; i++){
    fprintf(fp, "%lf\n", pHOGFeatures[i]);
  }

  fclose(fp);

  return(0);
}

/*
 * 勾配方向ヒストグラムの算出
 */
int CompHistogram(cv::Mat Image, double cell_hist[])
{
  int num1, num2;      //配列の要素数の計算

  int xDelta,yDelta;          //各方向の輝度の差分
  double magnitude;           //勾配強度
  double angle = 180.0/(double)N_BIN;   //量子化する時の角度
  double gradient;

  unsigned char* imgSource = (unsigned char*)&Image.data[0];

  // パッチ内の移動
  for(int y=0; y<Image.rows; y++){
    if(Image.rows/CELL_SIZE <= y/CELL_SIZE) continue;
    for(int x=0; x<Image.cols; x++){
      if(Image.cols/CELL_SIZE <= x/CELL_SIZE) continue;

      // 横方向の差分
      if(x == 0){
        num1 = y*Image.cols+(x+0);
        num2 = y*Image.cols+(x+1);
      }else if(x == Image.cols-1){
        num1 = y*Image.cols+(x-1);
        num2 = y*Image.cols+(x+0);
      }else{
        num1 = y*Image.cols+(x-1);
        num2 = y*Image.cols+(x+1);
      }
      xDelta = imgSource[num1]-imgSource[num2];

      // 縦方向の差分
      if(y == 0){
        num1 = (y+0)*Image.cols+x;
        num2 = (y+1)*Image.cols+x;
      }else if(y == Image.rows-1){
        num1 = (y-1)*Image.cols+x;
        num2 = (y+0)*Image.cols+x;
      }else{
        num1 = (y-1)*Image.cols+x;
        num2 = (y+1)*Image.cols+x;
      }
      yDelta = imgSource[num1]-imgSource[num2];

      // 勾配強度の算出
      magnitude = sqrt((double)xDelta*(double)xDelta+(double)yDelta*(double)yDelta);
      gradient = atan2((double)yDelta, (double)xDelta); // 勾配方向の算出

      gradient = (gradient*180.0)/PI;          // ラジアンから角度へ変換
      if(gradient < 0.0)    gradient += 360.0; // 符号が負である場合は反転
      if(gradient > 180.0)  gradient -= 180.0; // 0~360度から 0~180度に変換
      gradient = gradient/angle;               // 20度ずつ,9分割

      // ヒストグラムに蓄積
      num1 = (int)((y/CELL_SIZE)*(Image.cols/CELL_SIZE)*N_BIN+(x/CELL_SIZE)*N_BIN+(int)gradient);
      cell_hist[num1] += magnitude;

    }
  }

  return(0);
}

/*
 * HOG 特徴量の算出と正規化
 */
int CompHOG(double *pHOGFeatures, double cell_hist[], int Cell_Y, int Cell_X)
{
  int x,y,i,j,k;
  int num;
  double sum_magnitude;

  // 特徴量の算出と正規化
  for(y=0; y<Cell_Y; y++){
    for(x=0; x<Cell_X; x++){
      // 勾配方向¸
      sum_magnitude = 0.0;
      for(k=0; k<N_BIN; k++){
        // 特徴量量を正規化する時に使用する特徴量の二乗和を正規化
        for(j=-((int)(BLOCK_SIZE/2)); j<=(int)(BLOCK_SIZE/2); j++){
          if(((y+j) < 0) || ((y+j) >= Cell_Y)) continue;
          for(i=-((int)(BLOCK_SIZE/2)); i<=(int)(BLOCK_SIZE/2); i++){
            if(((x+i) < 0) || ((x+i) >= Cell_X)) continue;
            // 正規化のためヒストグラムの総和の二乗を算出
            num = (int)((y+j)*Cell_X*N_BIN+(x+i)*N_BIN+k);
            sum_magnitude += cell_hist[num] * cell_hist[num];
          }
        }
      }
      sum_magnitude = sqrt(sum_magnitude);

      for(k=0; k<N_BIN; k++){
        // 特徴量の正規化
        num = (int)(y*Cell_X*N_BIN+x*N_BIN+k);
        if(sum_magnitude == 0){
          pHOGFeatures[num] = 0;
        }else{
          pHOGFeatures[num] = cell_hist[num] / sum_magnitude;
        }
      }
    }
  }
  return 0;
}

/*
 * 勾配強度と勾配方向を算出して HOG 特徴量を抽出
 */
int HOG(cv::Mat Image, double *pHOGFeatures, int Cell_Y, int Cell_X)
{
  int num = Cell_Y*Cell_X*N_BIN;
  double *cell_hist = new double[num]();    // 0初期化が必要

  CompHistogram(Image, cell_hist);  // 勾配方向と勾配強度から勾配方向ヒストグラムを算出
  CompHOG(pHOGFeatures, cell_hist, Cell_Y, Cell_X); //HOG 特徴量の算出

  delete []cell_hist;

  return 0;
}

int main(int argc, char** argv)
{
  // 画像の読み込み
  cv::Mat Image = cv::imread(argv[1], CV_LOAD_IMAGE_GRAYSCALE);

  int Cell_Y = Image.rows / CELL_SIZE;
  int Cell_X = Image.cols / CELL_SIZE;

  int num = (int)Cell_Y*Cell_X*BLOCK_SIZE*BLOCK_SIZE*N_BIN;
  double *HogVal = new double[num];

  // HOG 特徴抽出
  HOG(Image, HogVal, Cell_Y, Cell_X);

  // 特徴量の書き出し
  OutputHOGFeature(num, HogVal, argv[1]);

  // 勾配画像の表示
  Image *= 0.5; // 描画用のイメージを作成(輝度は半分にする)
  for(int y=0; y<Cell_Y; y++){
    for(int x=0; x<Cell_X; x++){
      // 角度ごとに線を描画
      cv::Point center(x*CELL_SIZE+CELL_SIZE/2, y*CELL_SIZE+CELL_SIZE/2);
      for (int i = 0; i < N_BIN; i++) {
        int num = y*Cell_X*N_BIN+x*N_BIN+i;
        double theta = (i * THETA + 90.0 ) * PI / 180.0;
        cv::Point rd(CELL_SIZE*0.5*cos(theta), CELL_SIZE*0.5*sin(theta));
        cv::Point rp = center -   rd;
        cv::Point lp = center -  -rd;
        cv::line(Image, rp, lp, cv::Scalar(255*HogVal[num], 255, 255));
      }
    }
  }
  cv::imshow("Hog Image", Image);
  cv::waitKey(0);

  delete []HogVal;

  return 0;
}

まぁ、これで結果がこんな感じ。

OpenCVの結果より色が薄く表示されているが、勾配の傾向は同じなのでこのまま使えそうな気配。 ちゃんと、特徴を抽出出来ていると思う。