// Hough変換(直線)画像から抽出した直線と,元の2値画像を合成したグレイスケール画像を作成するプログラム // (2値画像は,背景(黒):MIN_GRAY_LEVEL,図形(白):MAX_GRAY_LEVELのグレイスケール画像とする。) // プログラム名:hough_inv.cpp // Hough変換(直線) // 図形画素(x, y)とするとき, // ρ = x * cosθ + y * sinθ (1) // をθ-ρ平面上に曲線として表す。 // すべての図形画素についてHough平面(θ-ρ)上に曲線で表し,重なりを度数で計算する。 // ここで,0°≦θ<180°,|ρ|≦sqrt(2値画像のピクセル数^2 + 2値画像のライン数^2) // // Hough逆変換(直線) // θ-ρ平面上の点(θ,ρ)に対する直線は,(1)より // x = (ρ - y * sinθ) / cosθ // y = (ρ - x * cosθ) / sinθ // となる。 #include "eps-header.hh" // Hough変換(直線)された画像から直線を抽出し,処理済画像に入れる関数 void hough_inv_lines( class image& src, // 元画像クラス(入力) class image& hgh, // Hough変換(直線)されたHough画像クラス(入力) class image& dst, // 処理済画像クラス(出力) unsigned char th_line, // Hough画像で直線とみなす閾値 double reso_theta, // 抽出する直線に対するθの解像度[°] double reso_rho) // 抽出する直線に対するρの解像度[画素] { if (src.p == NULL) { printf("Hough変換(直線)された画像がありません。"); return; } printf("\nHough変換(直線)された画像から直線を抽出します。\n\n"); int src_nr_lines = src.get_nr_lines(); // 元画像のライン数 (縦の画素数) int src_nr_pixels = src.get_nr_pixels(); // 元画像のピクセル数 (横の画素数) int hgh_nr_lines = hgh.get_nr_lines(); // Hough画像のライン数 (縦の画素数) int hgh_nr_pixels = hgh.get_nr_pixels(); // Hough画像のピクセル数 (横の画素数) double delta_theta = 180.0 / hgh_nr_pixels; // θ°の精度 double delta_rho = hgh_nr_lines / (2.0 * sqrt((double)src_nr_pixels * (double)src_nr_pixels + (double)src_nr_lines * (double)src_nr_lines)); // ρ[画素]の精度 int hgh_reso_theta = (int)(reso_theta / delta_theta + 0.5); int hgh_reso_rho = (int)(reso_rho / delta_rho + 0.5); if (delta_theta <= 0.1 || 10.0 <= delta_theta) { printf("θ°の精度 %lfは,0.1以上10.0以下としてください。\n", delta_theta); return; } if (delta_rho <= 0.1 || 10.0 <= delta_rho) { printf("ρの精度 %lfは,0.1以上10.0以下としてください。\n", delta_rho); return; } printf("θ°(%5d≦θ<%5d)の精度は,%5.2lfです。\n", 0, 180, delta_theta); printf("ρ (%+5d≦ρ≦%+5d)の精度は,%5.2lfです。\n", -hgh_nr_lines / 2, hgh_nr_lines / 2, delta_rho); printf("Hough画像で,%d以上の点を直線の候補とします。\n", th_line); printf("抽出する直線に対するθの解像度は,%5.2lf°です。\n", reso_theta); printf(" ρの解像度は,%5.2lf画素です。\n", reso_rho); printf("\n"); int dst_nr_lines = src_nr_lines; // 処理済画像のライン数 (縦の画素数) int dst_nr_pixels = src_nr_pixels; // 処理済画像のピクセル数 (横の画素数) dst.gmake(dst_nr_lines, dst_nr_pixels); // 処理済画像の配列を確保 // 元の2値画像のうち図形(白)を灰色にして処理済画像にコピーする。 for (int iy_dst = 0; iy_dst < dst_nr_lines; iy_dst ++) { for (int ix_dst = 0; ix_dst < dst_nr_pixels; ix_dst ++) { dst.p[iy_dst][ix_dst] = MIN_GRAY_LEVEL; if (src.p[iy_dst][ix_dst] == MAX_GRAY_LEVEL) { dst.p[iy_dst][ix_dst] = (MIN_GRAY_LEVEL + MAX_GRAY_LEVEL) / 2; } } } // Hough画像から直線を抽出する。 int line_count = 0; while(true) { // 現時点のHough画像の最大値を求める。 int max_hgh = hgh.p[0][0]; int iy_hgh_max = 0; // ρ方向 int ix_hgh_max = 0; // θ方向 for (int iy_hgh = 0; iy_hgh < hgh_nr_lines; iy_hgh ++) { for (int ix_hgh = 0; ix_hgh < hgh_nr_pixels; ix_hgh ++) { if (max_hgh < hgh.p[iy_hgh][ix_hgh]) { max_hgh = hgh.p[iy_hgh][ix_hgh]; iy_hgh_max = iy_hgh; ix_hgh_max = ix_hgh; } } } if (max_hgh < th_line) { printf("\nHough画像からの直線の抽出を終わります。\n"); break; } printf("第%4d番目の直線を抽出しました。\n", line_count + 1); line_count ++; double theta = ix_hgh_max * delta_theta; double rho = (iy_hgh_max - (hgh_nr_lines / 2)) * delta_rho; printf("Hough平面上のθ=%8.2lf, ρ=%8.2lfで,最大値 %4d が抽出されました。\n", theta, rho, max_hgh); // 最大値周辺を指定された解像度に従って,その周囲領域をゼロにする。 for (int iy_hgh = iy_hgh_max - (hgh_reso_rho / 2); iy_hgh < iy_hgh_max + (hgh_reso_rho / 2); iy_hgh ++) { for (int ix_hgh = ix_hgh_max - (hgh_reso_theta / 2); ix_hgh < ix_hgh_max + (hgh_reso_theta / 2); ix_hgh ++) { if (0 <= iy_hgh && iy_hgh < hgh_nr_lines && 0 <= ix_hgh && ix_hgh < hgh_nr_pixels) { hgh.p[iy_hgh][ix_hgh] = 0; } } } // 処理済画像上に抽出された直線を描画する。 double sin_val = sin(theta * PI / 180.0); double cos_val = cos(theta * PI / 180.0); if (45.0 <= theta && theta <= 135.0) { for (int ix_dst = 0; ix_dst < dst_nr_pixels; ix_dst ++) { int iy_dst = (int)((rho - ix_dst * cos_val) / sin_val + 0.5); if (0 <= iy_dst && iy_dst < dst_nr_lines) { dst.p[iy_dst][ix_dst] = MAX_GRAY_LEVEL; } } } else { for (int iy_dst = 0; iy_dst < dst_nr_lines; iy_dst ++) { int ix_dst = (int)((rho - iy_dst * sin_val) / cos_val + 0.5); if (0 <= ix_dst && ix_dst < dst_nr_pixels) { dst.p[iy_dst][ix_dst] = MAX_GRAY_LEVEL; } } } } } // プログラムの開始位置 int main() { class image src; // 元画像クラスを宣言 class image hgh; // Hough画像クラスを宣言 class image dst; // 処理済画像クラスを宣言 printf("\n元の2値画像を入力してください。\n"); src.gload(); // 元画像をBMPファイルから読み込む // BMPファイルを指定したい場合は, // src.gload("パス付きBMPファイル名"); // とする。 printf("\nHough変換(直線)した画像を入力してください。\n"); hgh.gload(); // Hough変換(直線)した画像をBMPファイルから読み込む // BMPファイルを指定したい場合は, // hgh.gload("パス付きBMPファイル名"); // とする。 // Hough変換(直線)された画像から直線を抽出し,処理済画像に入れる hough_inv_lines( src, // 元画像クラス(入力) hgh, // Hough変換(直線)されたHough画像クラス(入力) dst, // 処理済画像クラス(出力) 100, // Hough画像で直線とみなす閾値 10.0, // 抽出する直線に対するθの解像度[°] 20.0); // 抽出する直線に対するρの解像度[画素] printf("\n元の2値画像に抽出した直線を重ねた画像を出力します。\n"); dst.gsave(); // 処理済画像をBMPファイルに保存する // BMPファイルを指定したい場合は, // dst.gsave("パス付きBMPファイル名"); // とする。 }