// 2値画像をHough変換(直線)するプログラム // (2値画像は,背景(黒):MIN_GRAY_LEVEL,図形(白):MAX_GRAY_LEVELのグレイスケール画像とする。) // プログラム名:hough_conv.cpp // Hough変換(直線) // 図形画素(x, y)とするとき, // ρ = x * cosθ + y * sinθ // をθ-ρ平面上に曲線として表す。 // すべての図形画素についてHough平面(θ-ρ)上に曲線で表し,重なりを度数で計算する。 // ここで,0°≦θ<180°,|ρ|≦sqrt(2値画像のピクセル数^2 + 2値画像のライン数^2) #include "eps-header.hh" // 元画像(src)をHough変換(直線)して処理済画像に入れる関数 void hough_image( class image& src, // 元画像クラス(入力) class image& dst, // 処理済画像クラス(出力) double delta_theta, // θ°の精度 double delta_rho) // ρの精度 { if (src.p == NULL) { printf("元画像がありません。"); return; } 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("\n2値画像をHough変換(直線)します。\n"); int src_nr_lines = src.get_nr_lines(); // 元画像のライン数 (縦の画素数) int src_nr_pixels = src.get_nr_pixels(); // 元画像のピクセル数 (横の画素数) int dst_nr_lines = (int)ceil( sqrt((double)src_nr_pixels * (double)src_nr_pixels + (double)src_nr_lines * (double)src_nr_lines) * 2.0 / delta_rho); // 処理済画像のライン数 (縦の画素数) int dst_nr_pixels = (int)ceil(180.0 / delta_theta); // 処理済画像のピクセル数 (横の画素数) dst.gmake(dst_nr_lines, dst_nr_pixels); // 処理済画像の配列を確保 printf("θ°(%5d≦θ<%5d)の精度は,%5.2lfです。\n", 0, 180, delta_theta); printf("ρ (%+5d≦ρ≦%+5d)の精度は,%5.2lfです。\n", -dst_nr_lines / 2, dst_nr_lines / 2, delta_rho); printf("\nHough変換された画像の大きさは,横%dピクセル,縦%dラインになります。\n", dst_nr_pixels, dst_nr_lines); // Hough平面用の2次元配列を確保する。 int ** h = (int **)malloc(sizeof(int *) * dst_nr_lines); if (h == NULL) {printf("Hough平面用2次元配列が確保できませんでした。\n"); return;} for (int iy_dst = 0; iy_dst < dst_nr_lines; iy_dst ++) { h[iy_dst] = (int *)malloc(sizeof(int) * dst_nr_pixels); if (h[iy_dst] == NULL) {printf("Hough平面用2次元配列が確保できませんでした。\n"); return;} } // Hough平面の度数を0で初期化する。 for (int iy_dst = 0; iy_dst < dst_nr_lines; iy_dst ++) { for (int ix_dst = 0; ix_dst < dst_nr_pixels; ix_dst ++) { h[iy_dst][ix_dst] = 0; } } // Hough変換処理 for (int iy_src = 0; iy_src < src_nr_lines; iy_src ++) { for (int ix_src = 0; ix_src < src_nr_pixels; ix_src ++) { if (src.p[iy_src][ix_src] != MAX_GRAY_LEVEL) continue; // 図形画素以外は,次の画素へ // 図形画素をHough平面に表す。 for (double th = 0.0; th < (180.0 - delta_theta); th += delta_theta) { double th1 = (th ) * PI / 180.0; double th2 = (th + delta_theta) * PI / 180.0; double rho1 = ix_src * cos(th1) + iy_src * sin(th1); double rho2 = ix_src * cos(th2) + iy_src * sin(th2); // Hough平面上に度数を加える。 th1 = th; th2 = th + delta_theta; if (rho1 > rho2) { double tmp = rho1; rho1 = rho2; rho2 = tmp; tmp = th1; th1 = th2; th2 = tmp; } for (double rho = rho1; rho <= rho2; rho += delta_rho) { int iy_dst = (int)(rho / delta_rho + dst_nr_lines / 2 + 0.5); int ix_dst = (int)(((rho <= (rho1 + rho2) / 2.0) ? th1 : th2) / delta_theta + 0.5); h[iy_dst][ix_dst] ++; } } } } int max_h = 0; for (int iy_dst = 0; iy_dst < dst_nr_lines; iy_dst ++) { for (int ix_dst = 0; ix_dst < dst_nr_pixels; ix_dst ++) { if (max_h < h[iy_dst][ix_dst]) { max_h = h[iy_dst][ix_dst]; } } } printf("Hough平面上の最大度数は %dでした。\n", max_h); 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] = (unsigned char)((double)h[iy_dst][ix_dst] / (double)max_h * MAX_GRAY_LEVEL); } } // Hough平面用のメモリを開放する。 for (int iy_dst = 0; iy_dst < dst_nr_lines; iy_dst ++) { free(h[iy_dst]); } free(h); } // プログラムの開始位置 int main() { class image src; // 元画像クラスを宣言 class image dst; // 処理済画像クラスを宣言 src.gload(); // 元画像をBMPファイルから読み込む // BMPファイルを指定したい場合は, // src.gload("パス付きBMPファイル名"); // とする。 // 元画像(src)をHough変換(直線)して処理済画像に入れる。 hough_image( src, // 元画像クラス(入力) dst, // 処理済画像クラス(出力) 0.5, // θ°の精度 1.0); // ρの精度 dst.gsave(); // 処理済画像をBMPファイルに保存する // BMPファイルを指定したい場合は, // dst.gsave("パス付きBMPファイル名"); // とする。 }