同僚から『外形(外枠)を検出したい』と言われた。そこで、エッジ検出を導入してみようと思う。
今回も『HDevelopのサンプルを探しながら、これかな?』と試行錯誤しながら、なんとかできたのでシンプルに分かりやすくサンプルを作成しながら紹介できればと思う。
使用する関数
今回のサンプルを作るうえでエッジ検出に必要な関数は3つとなる。
■HOperatorSet.GenMeasureRectangle2 (HTuple,HTuple,HTuple,HTuple,HTuple,HTuple,HTuple,HTuple,out HTuple) ⇒ エッジ検出に必要な長方形のエリアを抽出する ■HOperatorSet.MeasurePos (HObject,HTuple,HTuple,HTuple,HTuple,HTuple,out HTuple, out HTuple,out HTuple,out HTuple) ⇒ エッジ検出を実行する。 ■HOperatorSet.CloseMeasure(HTuple) ⇒ エッジ検出を行ったオブジェクトを開放します。
見るだけでもつらい引数。。。
各関数の引数は、下記の通りです。
■HOperatorSet.GenMeasureRectangle2()
 引数1:長方形の中心座標Y (行/Row)
 引数2:長方形の中心座標X (列/Column)
 引数3:水平に対する長方形の縦軸の角度 (ラジアン)
 引数4:長方形の半分の幅
 引数5:長方形の半分の高さ
 引数6:処理する画像の幅
 引数7:処理する画像の高さ
 引数8:使用する補間手法(nearest_neighbor/bicubic/bilinear)
 引数9:[出力]測定するオブジェクトのハンドル
■HOperatorSet.MeasurePos()
 引数1:入力画像(処理する画像)
 引数2:測定するオブジェクトのハンドル
 引数3:ガウス平滑化のシグマ(0.4≦n≦100) def:1.0
 引数4:最小エッジ振幅(閾値) (1≦n≦255) def:30
 引数5:エッジの検出方法(negative/positive/all) [明⇒暗 / 暗⇒明 / どちらも]
 引数6:エンドポイントの選択(all/first/last) [すべて / 最初 / 最後]
 引数7:[出力]検出したエッジ中心結果Y (行/Row)
 引数8:[出力]検出したエッジ中心結果X (列/Column)
 引数9:エッジのエッジ振幅 (符号付き)
 引数10:連続するエッジ間の距離
■HOperatorSet.CloseMeasure()
 引数1:測定したオブジェクトのハンドル
サンプルを作ってみる
下記、画像をエッジ検出していきたいと思う。

操作画面
下記、操作画面をデザインする。この画面は、検査が完了した時の画像となる。

配置したコントロールの説明
[Button]btnReadImg               :入力画像 選択/読込
[HWindowControl]HWindowControl1  :入力画像表示
[Button]btnEdgeInspectArea       :エッジ検出のエリアを選択
[NumericUpDown]nudX              :エッジ検出エリア開始座標X
[NumericUpDown]nudY              :エッジ検出エリア開始座標Y
[NumericUpDown]nudW              :エッジ検出エリア幅
[NumericUpDown]nudH              :エッジ検出エリア高さ
[Button]btnEdge                  :エッジ検出開始
[DataGridView]dgvResult          :エッジ検出結果表示用
今回は、btnEdgeのみを紹介する。
他の処理については、最後に補足として紹介する。
定義
NameSpaceを定義します。
using HalconDotNet;
変数を定義
HObject DispOrgImg = null;      //読み込んだ画像
const double DEF_W = 640.0;
const double DEF_H = 480.0;
処理
btnEdgeをクリックした時の処理を紹介します。
private void BtnEdge_Click(object sender, EventArgs e)
{
    if (DispOrgImg == null) return;        //画像読込されてないなら処理しない
    //Zoom方式で表示
    HTuple hwnd = hWindowControl1.HalconWindow;
    HOperatorSet.ClearWindow(hwnd);
    DispZoom(hwnd, DispOrgImg);
    //検出エリアの中心座標
    double halfW = (double)(nudW.Value / 2);
    double halfH = (double)(nudH.Value / 2);
    double centerX = (double)nudX.Value + halfW;
    double centerY = (double)nudY.Value + halfH;
    //検出エリアの表示
    HOperatorSet.SetDraw(hwnd, "margin");
    HOperatorSet.SetColor(hwnd, "yellow");
    HOperatorSet.SetLineWidth(hwnd, 1);
    HOperatorSet.DispRectangle2(hwnd, centerY, centerX, 0.0, halfW, halfH);
    //画像サイズ取得
    HOperatorSet.GetImageSize(DispOrgImg, out HTuple w, out HTuple h);
    //測定オブジェクトのハンドルを取得
    HOperatorSet.GenMeasureRectangle2(
       centerY, centerX, 0.0, halfW, halfH, w, h, "nearest_neighbor",
      out HTuple handle);
    //エッジ検出実行
    HOperatorSet.MeasurePos(
       DispOrgImg, handle, 1.0, 30, "all", "all",
       out HTuple row, out HTuple col, out HTuple ampl, out HTuple dist);
    //エッジ検出終了
    HOperatorSet.CloseMeasure(handle);
    //結果表示
    dgvResult.Rows.Clear();
    int cnt = row.Length;
    if (cnt <= 0) return;
    dgvResult.Rows.Add(cnt);
    for(int indx = 0;indx < cnt;indx++)
    {
        dgvResult.Rows[indx].Cells[0].Value = col[indx].D.ToString("F2");
        dgvResult.Rows[indx].Cells[1].Value = row[indx].D.ToString("F2");
        dgvResult.Rows[indx].Cells[2].Value = ampl[indx].D.ToString("F2");
        //クロス線を描画
        HOperatorSet.SetColor(hwnd, "red");
        HOperatorSet.SetLineWidth(hwnd, 3);
        HOperatorSet.DispCross(hwnd, row[indx], col[indx], 10, 0.0);
        if (indx + 1 < cnt)
        {
            dgvResult.Rows[indx].Cells[3].Value = dist[indx].D.ToString("F2");
        }
    }
    //描画の設定を戻す
    HOperatorSet.SetColor(hwnd, "white");
    HOperatorSet.SetLineWidth(hwnd, 1);
}NumericUpDownで設定してる[nudX/nudY/nudW/nudH]の設定から、エッジ検出のエリアの算出を行います。
次にエッジ検出をするオブジェクトハンドルを取得(HOperatorSet.GenMeasureRectangle2())します。
そして、取得したハンドルを使って、エッジ検出を実行(HOperatorSet.MeasurePos())します。
最後にオブジェクトハンドルを開放(HOperatorSet.CloseMeasure())します。
実行結果

画像の垂直方向のエッジは検出可能です。
残念なことに水平方向は検出は出来ませんでした。
まとめ
今回は、エッジ検出を出来るようにサンプルをつくってみました。
垂直方向のエッジの検出が出来るようになりました。
このままでは水平方向のエッジは取得できません。
次回、修正を加えたいと思います。
補足
ユーザーにファイルを指定してもらう関数
private DialogResult SelectImageFile(out string fp)
{
    fp = "";
    OpenFileDialog ofd = new OpenFileDialog();
    ofd.FileName = "";
    ofd.InitialDirectory = @"C:\";
    ofd.Filter = "画像ファイル(*.png;*.jpg)|*.png;*.jpg";
    ofd.Title = "開くファイルを選択してください";
    DialogResult res = ofd.ShowDialog();
    if (res == DialogResult.OK) fp = ofd.FileName;
    return res;
}画像サイズの比率を変えずに表示枠いっぱいに表示する関数
private void DispZoom(HTuple windowhandle, HObject img)
{
    //Zoom方式で表示
    HOperatorSet.GetImageSize(img, out HTuple w, out HTuple h);
    double rateR = DEF_H / h, rateC = DEF_W / w;
    double rate = (rateR < rateC) ? rateR : rateC;
    double c1 = -((DEF_W / rate) - w) / 2;
    double c2 = c1 + (DEF_W / rate);
    double r1 = -((DEF_H / rate) - h) / 2;
    double r2 = r1 + (DEF_H / rate);
    HOperatorSet.SetPart(windowhandle, r1, c1, r2, c2);
    HOperatorSet.DispObj(img, windowhandle);
}コントロールの活性制御
private void ControlEnable(bool sw)
{
    btnReadImg.Enabled = sw;
    btnEdgeInspectArea.Enabled = sw;
    grpParam.Enabled = sw;
    btnEdge.Enabled = sw;
    dgvResult.Enabled = sw;
}読込ボタンをクリックされた時の処理
private void BtnReadImg_Click(object sender, EventArgs e)
{
    //初期化
    DispOrgImg = null;
    dgvResult.Rows.Clear();
    //表示削除
    HTuple hwndOrg = hWindowControl1.HalconWindow;
    HOperatorSet.ClearWindow(hwndOrg);
    //画像を選択してもらう
    if (SelectImageFile(out string fp) == DialogResult.OK)
    {
        //画像読込
        HOperatorSet.ReadImage(out HObject img, fp);
        DispOrgImg = img;
        //Zoom方式で表示
        DispZoom(hwndOrg, img);
        //入力最大数値を変更する。
        //画像サイズ取得
        HOperatorSet.GetImageSize(DispOrgImg, out HTuple w, out HTuple h);
        nudX.Maximum = w;
        nudY.Maximum = h;  
        nudW.Maximum = w;
        nudH.Maximum = h;
    }
}エッジ検出するエリアを指定するボタンのクリック処理
hWindowControl1のマウス処理にてエリアを指定する。
左クリックをドラッグして大きさを設定し、右クリックで決定する。
private void BtnEdgeInspectArea_Click(object sender, EventArgs e)
{
    if (DispOrgImg == null) return;        //画像読込されてないなら処理しない
    ControlEnable(false);   //不要なコントロールは使用不可にする。
    hWindowControl1.Focus();  //コントロールにフォーカスを合わせる。
    //画像切り取り範囲をユーザーに求める
    hWindowControl1.HalconWindow.DrawRectangle1
        (out double top, out double left, out double bottom, out double right);
    //ユーザーが決めた範囲を入力(入力する事でイベントが発生)
    nudX.Value = (decimal)left;
    nudY.Value = (decimal)top;
    nudW.Value = (decimal)(right - left);
    nudH.Value = (decimal)(bottom - top);
    ControlEnable(true);    //コントロール有効化
}
業務でプログラミング(C#/VB/Python)を作っている。
挫折を何回も繰り返し、幾度の壁を乗り越えてきた。
乗り越えてきた事を忘れないように記録に残す。
同じ思いをしている人への情報提供になれたらと思う。
基本は初心者に向けたプログラムの情報を提供する。

 
  
  
  
  

コメント