2011年12月23日金曜日

ちょっと休憩

久しぶりにラベルがProgramsな投稿
画像の回転をやってみました(画像?ここゲーム関連じゃ・・・)

問題設定?は以下のような斜めになってる棋譜画像を
正しい方向に元に戻すことにしました.
ここではPNG形式のファイルになってるんですが,
実際にはPGM形式のファイルを使用しています orz
あ、この棋譜画像は棋譜画像作成ツールを使用させて頂いて作成したものを
テスト用にわざわざ10度程度回転させたものです.




ここでは超高等テクニックであるHough変換を使って直線を検出したりはせず<-
棋譜画像で傾いているのは大抵この2つのパターンなので
場合分けして左上と右上の頂点を調べて,
そこから傾いている角度を算出して,
直したものを結果として出力画像として出力するものを作成してみました.
素晴らしく汎用性皆無のコードを最後に貼っつけて置きます・・・.
使い方はコマンドライン引数に対象とするPGM形式のファイルパスと
結果を出力するPGM形式のファイル名を指定する感じで動きます.
こんなコード書いていて大学生として恥ずかしくないわけがない<-
githubにあげられるようなコード書けるように頑張ろう・・・<-

あ,っでこのコードによる出力は以下のようになりました.
これもPNG形式の画像となっていますが,実際はPGM形式の画像です・・・ orz
う〜ん、ちゃんと正しい方向な棋譜が得られていることがわかります.ヽ(*゚д゚)ノ
意外に角を打たれていても良い感じですね<-
けど,ただ回転行列を用いているだけなので,画質が劣化してると思います.Σ(´∀`;)
あと,トリミング機能あったほうが良いかも<-

 


まとめると<-
  • Hough変換使わなくても簡単な棋譜の方向の修正はたぶんできる.
  • 単純に回転行列使ってるだけじゃ画質が劣化する.



#include <iostream>
#include <string>
#include <vector>
#include <iterator>
#include <algorithm>
#include <fstream>
#include <cstdlib>
#include <cmath>

class Image {
 public:
  Image(char *file_name, char *output_file_name) { init(file_name,
output_file_name); }
  void init(char *file_name, char *output_file_name);
  void doCorrect();
  void outputImage();
  
 private:
  std::string file_type_;
  std::string file_comment_;
  int img_height_;
  int img_width_;
  int max_value_;
  std::vector<std::vector <int> > img_data_;
  std::string output_file_name_;
};

void Image::init(char *file_name, char *output_file_name) {
  output_file_name_ = output_file_name;

  std::ifstream fin(file_name);
  if (!fin) {
    std::cerr << "file not found" << std::endl;
    exit(1);
  }
    
  fin >> file_type_;
  if (file_type_ != "P2") {
    std::cerr << "file not found" << std::endl;
    exit(1);    
  }
  
  // コメント処理 コメントが1行あると仮定
  fin.ignore();
  getline(fin, file_comment_);
   
  fin >> img_width_ >> img_height_ >> max_value_;
  img_data_.resize(img_height_);
  for (int i = 0; i < img_height_; i++) {
    img_data_[i].resize(img_width_);
    for (int j = 0; j < img_width_; j++) {
        fin >> img_data_[i][j];
    }
  }
  fin.close();
}

void Image::doCorrect() {
  int x_top_left = -1;
  int y_top_left = 0;
  int x_top_right = -1;
  int y_top_right = 0;

  // 左上の頂点を探索
  for (int i = 0; i < img_height_; i++) {
    for (int j = 0; j < img_width_; j++) {
      if (img_data_[i][j] < max_value_ / 2) {
        x_top_left = j;
        y_top_left = i;
        break;
      }
    }
    if (x_top_left >= 0) {
      break;
    }
  }
  
  // 反時計回りに傾いていると考えられる場合
  if (x_top_left > img_width_ / 2) {
    x_top_right = x_top_left;
    y_top_right = y_top_left;
    int min_distance = img_width_;
    for (int i = 0; i < img_height_ / 2; i++) {
      for (int j = 0; j < img_width_ / 2; j++) {
        if (img_data_[i][j] < max_value_ / 2 && j < min_distance) {
          min_distance = j;
          x_top_left = j;
          y_top_left =  i;
        }
      }
    }   
  }
  else {
    int max_distance = 0;
    for (int i = 0; i < img_height_; i++) {
      for (int j = 0; j < img_width_; j++) {    
        if (img_data_[i][j] < max_value_ / 2 && j > max_distance) {
          max_distance = j;
          x_top_right = j;
          y_top_right =  i;
        }
      }
    } 
  }
  
  double theta = atan2((double)(y_top_right - y_top_left),
                       (double)(x_top_right - x_top_left));

  std::vector<std::vector<int> > correct_img_data(img_height_,
 std::vector<int>(img_width_, max_value_));
  for (int i = 0; i < img_height_; i++) {
    for (int j = 0; j < img_width_; j++) {
      int x_correct = j * cos(theta) + i * sin(theta);
      int y_correct = -j * sin(theta) + i * cos(theta);
      
      if (x_correct >= 0 && x_correct < img_width_
          && y_correct >= 0 && y_correct  < img_height_) {
          correct_img_data[y_correct][x_correct] = img_data_[i][j];
      }
    }
  }
  img_data_ = correct_img_data;
}

void Image::outputImage() {
  std::ofstream fout(output_file_name_.c_str());
  fout << "P2" << std::endl;
  fout << file_comment_ << std::endl;
  fout << img_width_ << " " << img_height_ << std::endl;
  fout << max_value_ << std::endl;

  for (int i = 0; i < img_height_; i++) {
    std::copy(img_data_[i].begin(), img_data_[i].end(),
              std::ostream_iterator<int>(fout, "\n"));
  }

  fout.close();
}

int main(int argc, char **argv) {
  if (argc == 1) {
    std::cerr << "enter a pgm file on the command line" << std::endl;
    return 1;
  }
  else if (argc == 2) {
    std::cerr << "enter a output file name on the command line" << std::endl;
    return 1;    
  }
  
  Image img(argv[1], argv[2]);
  img.doCorrect();
  img.outputImage();

  return 0;
}
2011年12月25日にちょっと修正<-

0 件のコメント:

コメントを投稿