【C++/CLI】フォーム間参照

下図のように、メインのフォームから設定値を設定するための子ウィンドウ(ダイアログボックス)を
表示し親のウィンドウに値を設定しなおす方法を紹介します。

 

 

フォームを開く

フォームをモーダルで表示する場合はShowDialogメソッドを、
モードレスで表示する場合はShowメソッドを用います。

 

(コード例) Form1.hにて

#include "Form2.h"
:
:
Form2^ frm2 = gcnew Form2;
//Form2の表示、Form2の親(Owner)をForm1(this)に設定
frm2->ShowDialog(this);

ここでのポイントはForm1からForm2を参照できるようにForm2をfrm2という
フィールド(メンバ変数)に代入していることと、Form2の親がForm1であることがわかるように
ShowDialogメソッドでthisを設定しています。
こうすることで、自分のフォームの親はどのフォームなのか?を調べる場合はフォームの
Ownerプロパティを参照することで可能となります。

 

別フォームの参照

別のフォームからテキストボックスなどのオブジェクトを参照する場合には

 

frm2->textBox1->Text

 

などとしたくなるのですが、デフォルトではエラーとなってしまいます。
そこで別のフォームからオブジェクトを参照できるようにオブジェクトのModifiersプロパティ
Publicなどに設定すると、別のフォームからオブジェクトを参照できるようになります。

 

 

循環参照(相互参照)

今回の例のように、Form1からForm2を参照、Form2からForm1を参照するので、
Form1.hにて

#include “Form2.h”

Form2.hにて

#include “Form1.h”

としたくなるのですが、お互いを参照してグルグル回ってしまうので、ヘッダの宣言の部分を
*.hファイルではなく、*.cppファイルに持って行きます。

今回のサンプルの例ではイベント処理の部分も*.cppファイルに持っていって

 

(コード例) Form2.cppにて

#include "StdAfx.h"
#include "Form2.h"

#include "Form1.h"

using namespace prjForm;

System::Void Form2::Form2_Load(System::Object^  sender, System::EventArgs^  e) {

    Form1^ ParentForm;

    if (this->Owner != nullptr){
        //親のフォームクラス
        ParentForm = static_cast<Form1^>(this->Owner);

        this->textBox1->Text = ParentForm->textBox1->Text;
        this->textBox2->Text = ParentForm->textBox2->Text;
        this->textBox3->Text = ParentForm->textBox3->Text;
    }
}

 

サンプルプログラム

このサンプルプログラムはフォーム間参照サンプルプログラムでダウンロードができます。
VisualStudio2005 Express Edition で作成しています。

 

【C++/CLI】フォーム(ウィンドウ)のサイズ固定

プログラム実行時のウィンドウのサイズをユーザに変えられないようにサイズを固定するには
フォームのFormBorderStyleプロパティFixed××に設定します。

 

 

なんとなく、FormResizeみたいなプロパティが無いか?探したくなっちゃいます...

 

ちなみに、こちら↓はVB6.0のプロパティ

VB6.0ではBorderStyleプロパティです。

 

【C++/CLI】ウィンドウの押されたボタンを取得(DialogResultプロパティ)

一般的なウィンドウでは下図のようにOKボタンCancelボタンを配置しボタンが押されるとウィンドウを閉じるようにしますが、このOKボタンCancelボタンのどちらのボタンを押されたのか?を取得する方法を紹介します。

 

 

まず、buttonコントロールOKボタンCancelボタン用の2つを配置します。
次にボタンのDialogResultプロパティの中からボタンに応じてOKCancelなどを選択します。

 

 

DialogResultプロパティを設定することで、ボタンが押されると自動的にウィンドウが閉じられるので、ボタンのイベント処理を記載する必要はありません。 (ShowDialogメソッドを使ってフォームを表示した場合)
ただし、ウィンドウが非表示になるだけなので、ウィンドウが必要なくなったら、フォームを解放しておきます。

 

以下、Form2が閉じられた時に、何のボタンを押されてウィンドウが閉じられたのか?
を取得する部分のサンプルプログラムです。

 private: System::Void button1_Click(System::Object^  sender, System::EventArgs^  e) {
     //Form2を開く
     Form2^ frm = gcnew Form2();
     frm->ShowDialog();

     //押されたボタン別の処理
     if (frm->DialogResult == System::Windows::Forms::DialogResult::OK)
          MessageBox::Show("Okボタンが押されました");
     else if (frm->DialogResult == System::Windows::Forms::DialogResult::Cancel)
          MessageBox::Show("Cancelボタンが押されました");

     //フォームの解放
     delete frm;
    }

実行例

Form2を開くボタンをクリック

 

 

OKボタンをクリックすると、Form2のウィンドウが閉じられ、

 

 

と表示されます。

ウィンドウの×ボタンでウィンドウが閉じられた場合はCancelボタンとして認識されます。

フィルタ処理の高速化アルゴリズム(縦横に処理を分ける)

前回、フィルタ処理の高速化アルゴリズム(重複した計算を行わない)で紹介した方法ではカーネルの値が全て同じでないと使えないので、今回はフィルタ処理を縦方向と横方向に分けて行う事でフィルタ処理の高速化を行う方法をガウシアンフィルタを例にとって紹介します。

 

ガウシアンフィルタのカーネルには、

 

が良く用いられますが、この処理を注目画素の周辺の輝度値をI0~I8とした場合、
ガウシアンフィルタの処理を行列で

 

と、表すこともでき、この事は縦方向に3×1のガウシアンフィルタ処理をおこなってから、
横方向に1×3のガウシアンフィルタ処理を行うことを意味しています。
(横方向に処理をしてから縦方向に処理をしても同じです。)

 

このように処理を縦と横に分けることで、カーネルのサイズm×nの場合、通常の処理では
m×n回の掛け算を行うところ、m+n回の掛け算で済む事になります。
(ただし、縦横に処理を分ける事で全画素を2回参照することになるので、カーネルのサイズが
小さいと効果はあまりありません。)

他にも、移動平均フィルタの場合

ソーベルフィルタの場合

 

となります。
ソーベルフィルタの行列を見ると、縦方向にガウシアンフィルタ処理をしてから、横方向に微分処理している事が分かりやすくなっているかと思います。

 

また、比較的処理の重いメディアンフィルタにおいても、処理を縦と横に分けることによって、
ほぼ、同様な効果を得ることができます。
厳密には同じ結果にはならないのですが、スパイクノイズを除去するという意味では
十分な結果を得る事が出来ます。

試しに何回か、メディアンフィルタ処理を縦方向に1列分の処理を行ってから、横方向に1列分の
処理を行ってみましたが、ほぼ、良好な結果を得る事ができていると思います。
(画像にするともう少し分かりやすいかと思いますが、プログラムが無いもので...)

 

 

画像処理アルゴリズムへ戻る

 

フィルタ処理の高速化アルゴリズム(重複した計算を行わない)

画像フィルタ処理の高速化のテクニックを移動平均フィルタを例にとって紹介したいと思います。

 

カーネルのサイズが5×5の移動平均フィルタの場合、注目画素の周辺の5×5の輝度値を合計し、
その輝度値の合計を画素数(5×5=25)で割る処理をラスタスキャンしながら、全画素に対して
処理を行います。

 

 

ここで、隣の画素へ処理が移った時に、輝度値の合計の計算処理は前に行った輝度値の合計の
処理とかなりかぶっている(下図の緑色の部分)事に気が付きます。

 

 

そこで、最初の輝度値の平均値を計算をした時の輝度値の合計値を保持しておき、最初の輝度値の
合計値から、最初のカーネルの左端の1列分の輝度値(上図の赤色の部分)を引き、
次のカーネルの右端の1列分の輝度値(上図の青色の部分)を足すと、次のカーネル内の
輝度値の合計値を求める事が出来ます。

 

そうすると、カーネル内の輝度値の合計の計算に25回の足し算をしていたところ、5回の引き算と
5回の足し算の計10回の計算で済ませることが分かります。
この効果はカーネルサイズが大きくなればなる程、大きくなります。

 

さらに画像の1行分の合計値を確保するメモリを確保しておくと、縦方向に関しても同様の処理が
できるので、高速化が期待できます。

 

と、今回は画像のフィルタ処理を例にとって紹介していますが、この考え方は他にもいろいろと
応用が効くので、輝度値の合計の計算に留まらず、毎回同じような処理をしているな~と思ったら、
前回行った処理の使いまわしができないか?検討してみると良いでしょう。

 

画像処理アルゴリズムへ戻る