OpenCVのバグ

本記事は古い情報となります。

 

OpenCVには、いくつか有名なバグがあります。

 

バグの情報に関しては、こちらのページ

https://code.ros.org/trac/opencv/report

で確認できますが、私の知っている気になったバグは以下の通り

 

  • cvErode/cvDilate
    iterationsに2以上の値を指定すると、膨張/収縮の中心位置がずれる
  • 画像の読み込み(cvLoadImage、cv::imread)(Ver2.1)
    大きな画像を開けない
  • Windows7 64bitでUSBカメラが使えない (Ver2.2)

 

などなど。

 

以前に使えていた物も、新しいバージョンではバグになるという、ソフト屋さんではやってはいけないような事もあるので、要注意ですね。

 

OpenCVへ戻る

 

多ビット(10Bit,12Bit,30Bit)画像データの表示、フォーマット

多ビット(10Bit、12Bitなど)の画像データは10Bitや12Bitの型が存在しないため、モノクロの場合はushort型の16bit中、下位10Bit、12Bitなどを使って画像データを格納します。
30Bitとは、R,G,Bの各色が10bitのデータとなるカラー画像の場合(RGB101010)で32Bit中の下位30Bitを使って画像データを格納します。

 

この××Bit中のどのビットをR,G,Bの色に割り振るかは、ビットフィールドというものを使います。
ビットフィールドを使うにはBITMAPINFOHEADERbiCompressionBI_BITFIELDSに設定します。

 

どのビットをR,G,Bの色に割り振るかはRGBQUAD型の32Bitを使って有効ビットを指定します。
bmiColors[0]R(赤)bmiColors[1]G(緑)bmiColors[2]B(青)の色のビットを設定するのに用います。

 

実際の描画にはWin32APIのSetDIBitsToDevice関数かStretchDIBits関数を使います。

 

モノクロ画像の場合(10~16Bitのとき)

(例)モノクロ10Bitの場合、biBitCount = 16 とし、16Bit中下位10Bitを使います。
表示に有効なビットを以下のように指定します。

画像データ 00000011 11111111
bmiColors[0] 00000000 00000000 00000011 11111111
bmiColors[1] 00000000 00000000 00000011 11111111
bmiColors[2] 00000000 00000000 00000011 11111111

以上より、bmiColorsを10進数で表示すると以下のようになります。

bmiColors[0].rgbReserved = 0;
bmiColors[0].rgbRed          = 0;
bmiColors[0].rgbGreen       = 3;
bmiColors[0].rgbBlue          = 255;
bmiColors[1].rgbReserved = 0;
bmiColors[1].rgbRed          = 0;
bmiColors[1].rgbGreen       = 3;
bmiColors[1].rgbBlue          = 255;
bmiColors[2].rgbReserved = 0;
bmiColors[2].rgbRed          = 0;
bmiColors[2].rgbGreen       = 3;
bmiColors[2].rgbBlue          = 255;

カラー画像の場合(R10G10B10Bitのとき)

(例)カラー10Bitの場合、biBitCount = 32 とし、32Bit中、下位30Bitを使います。
表示に有効なビットを以下のように指定します。

画像データ 00111111 11111111 11111111 11111111
bmiColors[0] 00111111 11110000 00000000 00000000
bmiColors[1] 00000000 00001111 11111100 00000000
bmiColors[2] 00000000 00000000 00000011 11111111

以上より、bmiColorsを10進数で表示すると以下のようになります。

bmiColors[0].rgbReserved = 63;
bmiColors[0].rgbRed          = 240;
bmiColors[0].rgbGreen       = 0;
bmiColors[0].rgbBlue          = 0;
bmiColors[1].rgbReserved = 0;
bmiColors[1].rgbRed          = 15;
bmiColors[1].rgbGreen       = 252;
bmiColors[1].rgbBlue          = 0;
bmiColors[2].rgbReserved = 0;
bmiColors[2].rgbRed          = 0;
bmiColors[2].rgbGreen       = 3;
bmiColors[2].rgbBlue          = 255;

サンプル画像

画像データはこちら(16BitGray.zip

【画像データプロパティ】
幅:1024
高さ:256
ビット数:16
BitField:16ビット中、下位10ビットの上位8ビットを表示設定
輝度値:0~1023のグラデーション

上記のビットマップファイルをTSXBINというバイナリエディタでヘッダ部分を表示すると以下の通りです。(値は16進数表示)

エクプスローラのプロパティや標準的なビューアソフトでは16ビットとして認識されない場合が多いのでご注意下さい。

注意事項

この記事では10Bitなどの多ビット画像データを8Bitデータにシフト処理などする事なく、画像データを表示する方法を説明しています。実際のモニタ上にはRGB各8Bitの解像度で表示されます。
モニタ上にRGB各10Bitで表示するには、別途、

■10Bit対応のモニタ
■10Bit対応のビデオカード
■10Bit対応の表示プログラム(Direct-X、OpenGL)

の3点セットが必要となります。

(参考資料)
10ビット/8ビット表示はドコが違う!?:「FlexScan SX2462W」のDisplayPort入力で“約10億色リアル表示”を体感する (1/3) – ITmedia +D PC USER
株式会社エーキューブ – ナナオ社製10bit対応モニタ「Flexscan」「ColorEdge」による動作確認
AMD(ATI)の資料
nVIDIAの資料

最近のCCDカメラでは8Bitのみならず、10Bit、12Bitの出力を持つカメラが増えてきています。多ビットの画像データは低コントラストの画像データを処理するには有功な場合が多いのですが、ほとんどの画像処理ライブラリでは多ビットの画像データに対応していない場合が多いため、画像処理部分のプログラムを全て自作する必要が出てきます。
また、8Bitが10Bitになるだけで、カメラから画像入力ボードへのデータ転送量は倍(16Bit/画素)となるため、フレームレートやスキャンレートの最速レートを出せなくなる場合があります。

 

そのため、総合的に捉えて多ビットの画像データをお取り扱い下さい。

ビットマップファイルフォーマット

ビットマップファイル(*.bmp)のファイルフォーマットです。

 

ビットマップ全体の構造

BITMAPFILEHEADER 14Byte
BITMAPINFOHEADER 40Byte
カラーテーブル(無い場合もあり) 4Byte*Index数
画像データ

 

各構造体について

■BITMAPFILEHEADER

typedef struct tagBITMAPFILEHEADER { 
	WORD bfType; 
	DWORD bfSize; 
	WORD bfReserved1; 
	WORD bfReserved2; 
	DWORD bfOffBits; 
} BITMAPFILEHEADER, *PBITMAPFILEHEADER;

 

bfType ファイルタイプ ‘BM’
bfSize ファイル全体のサイズ(バイト数)
bfReserved1 予約領域 常に0
bfReserved2 予約領域 常に0
bfOffBits ファイル先頭から画像データまでのオフセット数(バイト単位)

 

■BITMAPINFO

typedef struct tagBITMAPINFO { 
	BITMAPINFOHEADER bmiHeader; 
	RGBQUAD bmiColors[1]; 
} BITMAPINFO, *PBITMAPINFO;

●BITMAPINFOHEADER

typedef struct tagBITMAPINFOHEADER{
	DWORD  biSize;
	LONG   biWidth;
	LONG   biHeight;
	WORD   biPlanes;
	WORD   biBitCount;
	DWORD  biCompression;
	DWORD  biSizeImage;
	LONG   biXPelsPerMeter;
	LONG   biYPelsPerMeter;
	DWORD  biClrUsed;
	DWORD  biClrImportant;
} BITMAPINFOHEADER, *PBITMAPINFOHEADER;
biSize 構造体のサイズ 40
biWidth 画像の幅(ピクセル数)
biHeight 画像の高さ(ピクセル数)
値が負の場合、画像の上下が逆になる
biPlanes プレーン数 常に1
biBitCount 1画素あたりのビット数 1,4,8,16,24,32
biCompression 圧縮形式 BI_RGB, BI_RLE8, BI_RLE4
BI_BITFIELDS, BI_JPEG, BI_PNG
biSizeImage 画像データのサイズ(バイト数) BI_RGBの場合0でも可
biXPelsPerMeter 水平方向の1Mあたりの画素数 0でも可
biYPelsPerMeter 垂直方向の1Mあたりの画素数 0でも可
biClrUsed カラーテーブルの色数 0でも可
biClrImportant 表示に必要なカラーテーブルの色数 0でも可

 

●RGBQUAD(カラーテーブル)

typedef struct tagRGBQUAD {
	BYTE    rgbBlue;
	BYTE    rgbGreen;
	BYTE    rgbRed;
	BYTE    rgbReserved;
} RGBQUAD;
rgbBlue 青の輝度値 0~255
rgbGreen 緑の輝度値 0~255
rgbRed 赤の輝度値 0~255
rgbReserved 予約領域 常に0

 

biBitCountが1,4,8のとき、RGBQUAD構造体のカラーテーブルが指定されます。
biBitCountが24,32のときは存在しません。
ただし、biCompressionがBI_BITFIELDS かつ、biBitCountが16,32の場合、
多ビット(10Bit,12Bitなど)が表示可能となり、ビットフィールドが指定されます。

 

■画像データ

モノクロ画像の場合、輝度値が格納されています。

24Bitカラーの場合、B,G,R,B,G,R・・・の順で各輝度値が格納されています。
32Bitカラーの場合、B,G,R,A,B,G,R,A・・・の順で各輝度値が格納されています。

 

画像データは、画像の左から右、下から上へ向かう順番で格納されます。

 

 

また、1行あたりのメモリのサイズは4の倍数バイトになるように調整されています。
この値は以下のようにして計算しています。

 

VBの場合

((biWidth * biBitCount + 31) \ 32) * 4

Cの場合

((biWidth * biBitCount + 31) / 32) * 4

 

4の倍数バイト(32ビットの倍数)になるように調整しています。

 

ビットマップファイルを開く場合の注意点について

ビットマップファイルを開く場合、ヘッダ情報をもとに画像データ格納用のメモリを確保し、
そのメモリにデータを格納しますが、ヘッダの値はすべて正しく記載されているとは限りません。私の場合、以下の値を信じてメモリの確保などを行っています。

biWidth, biHeight, biBitCountを用いて画像データ格納用メモリの確保
biOffBitsを用いて、画像データまでのファイルのシークを行う。

biSizeやbiSizeImageなどは信じない方が良いと思います。

 

【C++/CLI】Graphicsオブジェクトの作成

.NETではピクチャボックスに画像や線、文字などを描画するには、Graphicsオブジェクトを作成し、このGraphicsオブジェクトに対して描画を行います。

 

Graphicsオブジェクトを作成する方法は3つ。
1.Imageオブジェクトから作成(オススメ!)
2.CreateGraphicsメソッドを使う方法(使わないことをオススメ!)
3.PaintイベントのPaintEventArgsから取得する方法

 

Graphiscオブジェクトを作成したら、画像の描画は

g->DrawImage(bmp, 0, 0);

線の描画は

g->DrawLine(Pens::Blue,0, 0, 100, 100);

というような具合で。

 

Imageオブジェクトから作成

(コード例)

//PictureBoxと同じ大きさのBitmapクラスを作成する。
Bitmap^ bmpPicBox = gcnew Bitmap(pictureBox1->Width, pictureBox1->Height);
//空のBitmapをPictureBoxのImageに指定する。
pictureBox1->Image = bmpPicBox;
//Graphicsオブジェクトの作成(FromImageを使う)
Graphics^ g = Graphics::FromImage(pictureBox1->Image);

 

上記コードをフォームのResizeイベントなどで処理を行い、作成したGraphicsオブジェクトを使い回せばよいかと思います。
この手法だと再描画は勝手にやってくれる上に、CreateGraphicsを使うよりも
格段に描画が速くなります。
.NET(GDI+)による描画が遅い!と思っている方は、まずはこの方法をお試し下さい。
(でも個人的には、それでも遅く感じます)

 

CreateGraphicsメソッドを使う

(コード例)

//Graphicsオブジェクトの作成(CreateGraphicsを使う)
Graphics^ g = pictureBox1->CreateGraphics();

 

この手法だと簡単ですが描画がかなり遅くなります。
しかも再描画してくれません...
説明が簡単なので、使っちゃう場合もありますが。

 

PaintイベントのPaintEventArgsから取得する

(コード例)

private: System::Void pictureBox1_Paint(System::Object^  sender, System::Windows::Forms::PaintEventArgs^  e) {
//PaintEventArgsから取得
Graphics^ g = e->Graphics;
}

この方法が一番高速ですが、ちょっと扱いにくい。

 

ImageオブジェクトからGraphicsオブジェクトを作成する方法とCreateGraphisメソッドを使った方法の処理時間の比較などを「モノクロ画像の上に線などを描画」のページで紹介しています。
もしよろしければご参照下さい。

【C++/CLI】モノクロ画像の上に線などを描画

モノクロ画像の上に線などを描画する方法をまとめました。

 

【目標】

  • モノクロ/カラー区別なく画像の上に描画できること
  • 高速に描画できること
  • 再描画すること

     

    作成したサンプルプログラムはこんな感じ↓です。

     

    (サンプルプログラムの使い方)
    各ボタンをクリックするとファイルを開くダイアログボックスが開きますので、ビットマップファイルを指定して下さい。
    (ダウンロード)MonoImageDisp2005.zip(Visual Studio 2005 Express Edition版)

     

    準備

    線の描画は条件を同じにするため、以下の関数を使い回しました。

    void DrawGraphics(Graphics^ g){
    	//ギザギザ模様を画像の上に描画
    
    	int i, j;
    
    	//ギザギザの線の描画
        for (j = 0; j < 256; j+= 3){
    	    for (i = 0; i < 25; i++){ g->DrawLine(Pens::Blue, i * 10, j, i * 10 + 5, j - 5);
    		    g->DrawLine(Pens::Blue, i * 10 + 5, j - 5, i * 10 + 10, j);
    	    }
        }
    }

    よくあるサンプルプログラム

    private: System::Void btnBitmap_Click(System::Object^  sender, System::EventArgs^  e) {
    		 //ビットマップファイルからBitmapクラスを作成し、ピクチャボックスへ渡す方法
    		 //モノクロ画像だとエラーになります。
    
    	     try{
    			//描画時間計測用
    		    Diagnostics::Stopwatch^ sw = gcnew Diagnostics::Stopwatch;
    
    			//ピクチャボックスの画像クリア
    			pictureBox1->Image = nullptr;
    
    			//画像ファイル名の取得
    			String^ FileName = GetImageFilename();
    			//Bitmapクラスの作成
    			Bitmap^ bmp = gcnew Bitmap(FileName);
    			//PictureBoxへBitmapを表示する。
    			pictureBox1->Image = bmp;
    
    			//Graphicsクラスの作成
    			//※モノクロ画像ファイルを開くと、ここでエラーになります。
    			Graphics^ g = Graphics::FromImage(bmp);
    			//画像の上にギザギザ模様の描画
    			sw->Start();
    			DrawGraphics(g);
    			sw->Stop();
    			//描画時間表示
    			MessageBox::Show("描画時間 = " + sw->ElapsedMilliseconds.ToString() + " msec");
    
    		 }catch(Exception^ err){
    			//エラーの表示
    			MessageBox::Show(err->Message);
    		 }
    	 }

    このプログラムではカラー画像では問題ないのですが、8ビットモノクロ画像ファイルを開くとFromImageメソッドでエラーとなります。

     

    CreateGraphicsを使ってGraphicsクラスを作成する方法

    private: System::Void btnCreateGraphics_Click(System::Object^  sender, System::EventArgs^  e) {
    		 //CreateGraphicsを使ってGraphicsクラスを作成し描画する。
    		 //モノクロ画像の上にも描画できますが、描画時間がかなり遅くなります。
    		 //描画したギザギザ模様は再描画されません。
    
    		 try{
    			//描画時間計測用
    		    Diagnostics::Stopwatch^ sw = gcnew Diagnostics::Stopwatch;
    
    			//ピクチャボックスの画像クリア
    			pictureBox1->Image = nullptr;
    
    			//Graphicsクラスの作成(CreateGraphicsを使う)
    			Graphics^ g = pictureBox1->CreateGraphics();
    
    			//画像ファイル名の取得
    			String^ FileName = GetImageFilename();
    			//Bitmapクラスの作成
    			Bitmap^ bmp = gcnew Bitmap(FileName);
    			//PictureBoxへBitmapを表示する。
    			pictureBox1->Image = bmp;
    			pictureBox1->Refresh();
    
    			//画像の上にギザギザ模様の描画
    			sw->Start();
    			DrawGraphics(g);
    			sw->Stop();
    			//描画時間表示
    			MessageBox::Show("描画時間 = " + sw->ElapsedMilliseconds.ToString() + " msec");
    
    		 }catch(Exception^ err){
    			//エラーの表示
    			MessageBox::Show(err->Message);
    		 }
    	 }

    このプログラムでは、モノクロ画像ファイルを開いても、モノクロ画像の上に線を描画することができます。
    ただし、遅い!!!
    再描画もされません。

    PictureBoxと同じ大きさのBitmapクラスを作成し、ピクチャボックスのImageへ。画像データはDrawImageで描画

    /private: System::Void btnDrawImage_Click(System::Object^  sender, System::EventArgs^  e) {
    		 //ピクチャボックス表示用と画像データ用と別のBitmapクラスを作成し、
    		 //ピクチャボックスにはDrawImageを使って画像を描画する。
    		 //モノクロ画像でも表示可能
    		 //再描画可能、CreateGraphicsを使うより高速
    
    		 try{
    			//描画時間計測用
    		    Diagnostics::Stopwatch^ sw = gcnew Diagnostics::Stopwatch;
    
    			//ピクチャボックスの画像クリア
    			//pictureBox1->Image = nullptr;
    
    			//画像ファイル名の取得
    			String^ FileName = GetImageFilename();
    			//Bitmapクラスの作成
    			Bitmap^ bmpFileImage = gcnew Bitmap(FileName);
    
    			//PictureBoxの大きさを画像の大きさに合わせる
    			pictureBox1->Width  = bmpFileImage->Width;
    			pictureBox1->Height = bmpFileImage->Height;
    
    			//PictureBoxと同じ大きさのBitmapクラスを作成する。
    			Bitmap^ bmpPicBox = gcnew Bitmap(pictureBox1->Width, pictureBox1->Height);
    			//空のBitmapをPictureBoxのImageに指定する。
    			pictureBox1->Image = bmpPicBox;
    			//Graphicsクラスの作成(CreateGraphicsを使う)
    			Graphics^ g = Graphics::FromImage(pictureBox1->Image);
    
    			//PictureBoxへBitmapを描画する。
    			g->DrawImage(bmpFileImage, 0, 0);
    			pictureBox1->Invalidate();
    
    			//画像の上にギザギザ模様の描画
    			sw->Start();
    			DrawGraphics(g);
    			sw->Stop();
    			//描画時間表示
    			MessageBox::Show("描画時間 = " + sw->ElapsedMilliseconds.ToString() + " msec");
    
    		 }catch(Exception^ err){
    			//エラーの表示
    			MessageBox::Show(err->Message);
    		 }
    	 }

    最初にピクチャボックスと同じ大きさのBitmapクラスを作成し、ピクチャボックスのImageクラスへ渡す。ピクチャボックスのImageからFromImageメソッドでGraphicsクラスを作成。
    このようにすると、モノクロ画像の上にも線を描画することができ、再描画も勝手にしてくれます。
    しかも、CreateGraphicsを使ってGraphicsクラスを作成した場合よりも、描画速度は10倍くらい速くなります。

    【C++/CLI】大文字/小文字、全角/半角、ひらがな/カタカナを区別せず比較する

    字列を比較する際に、文字の大文字/小文字、全角/半角、ひらがな/カタカナを区別せず比較したい場合があります。

    大文字/小文字を区別せずに比較するのは、比較的、情報も多く、

    System.StringクラスのCompareメソッド

    を使います。
    使用例はこんな感じ↓

     

    String^ strA = "Labeling";
    String^ strB = "labeling";
    
    if (String::Compare(strA, strB, true) == 0){
    MessageBox::Show("strAとstrBは同じ");
    }

     

    ただ、実際には、日本語を比較する場合は、全角/半角、ひらがな/カタカナを区別しないで比較したい場合が多く、この場合はSystem.Globalization.CompareInfoクラスCompareメソッドを使います。

     

    (使用例)

    System::Globalization::CompareInfo^ ci =
    System::Globalization::CultureInfo::CurrentCulture->CompareInfo;
    
    String^ strA = "ラベリング";
    String^ strB = "らべりんぐ";
    
    if (ci->Compare(strA, strB,
    System::Globalization::CompareOptions::IgnoreWidth |         //全角/半角を無視
    System::Globalization::CompareOptions::IgnoreCase |          //大文字/小文字を無視
    System::Globalization::CompareOptions::IgnoreKanaType    //ひらがな/カタカナを無視
    ) == 0){
    MessageBox::Show("strAとstrBは同じ");
    }

     

    詳しくはDOBON.NETの

    2つの文字列が等しいかを調べる
    大文字小文字、半角全角、ひらがなカタカナの区別をしないで文字列を比較する

    が参考になります。

     

    実は”ラベリング”と”ラベリング”の文字列を比較するのに大はまりして、
    ひらがなの”ベ” と カタカナの ”ベ” がある事に気が付くまで、とても苦労してしまいました...

    【C++/CLI】VB.NET固有の関数を使用する方法

    .NET Framework ではVisual Basic.NETであっても、ただのVisualBasicという名前空間であるだけなので、C#やC++/CLIからVB.NET固有の関数を使うことが可能です。

     

    以下、VB固有の関数の代表格のInputBox関数の使用方法を紹介します。

     

    まず、VisualBasicの名前空間を参照できるように参照設定を行います。

     

    プロジェクトの名前の部分で右ボタンをクリックし、参照を選択します。

     

     

    次に新しい参照の追加ボタンをクリックします。

     

     

    すると、使用可能な.NETの名前空間を表示されるので、この中からMicrosoft.VisualBaicを選択しOKボタンをクリックします。

     

     

    すると、現在参照設定している名前空間の一覧にMicrosoft.VisualBasicの名前か追加されます。

     

     

    これで準備は完了です。

     

    実際にInputBoxを呼び出すのは、こんな感じ↓です。

     

    String^ str = Microsoft::VisualBasic::Interaction::InputBox(“入力文字?”, “タイトル”, “初期値”, -1, -1);

     

    とすると、このよう↓に表示されます。

     

     

    C++からVBの関数が呼べてしまうのは不思議な感じもしますが、他にも使いたいVBの関数があったので、今回わざわざ記事にしてみました。

    【C++/CLI】大文字/小文字、全角/半角、ひらがな/カタカナ変換

    大文字/小文字の変換をするには System.String クラスToLowerToUpper メソッドを用います。

     

    【コード例】

    String^ str;
    
    //大文字→小文字変換
    str = "ImagingSolution";    // → imagingsolution
    str = str->ToLower();
    //小文字→大文字変換
    str = "Imagingsolution";    // → IMAGINGSOLUTION
    str = str->ToUpper();

    さらにVBの関数を用いると全角/半角、ひらがな/カタカナの変換も行う事が出来ます。
    VBの関数をC++/CLIから使う方法は VB.NET固有の関数を使用する方法 を参照願います。

     

    【コード例】

    using namespace Microsoft::VisualBasic;
    
    String^ str;
    
    //大文字→小文字変換
    str = "ImagingSolution";    //→imagingsolution
    str = Strings::StrConv(str, VbStrConv::Lowercase, 0);
    //小文字→大文字変換
    str = "Imagingsolution";    //→IMAGINGSOLUTION
    str = Strings::StrConv(str, VbStrConv::Uppercase, 0);
    
    //全角→半角変換
    str = "画像処理ソリューション";    //→画像処理ソリューション
    str = Strings::StrConv(str, VbStrConv::Narrow, 0);
    //半角→全角変換
    str = "画像処理ソリューション";            //→画像処理ソリューション
    str = Strings::StrConv(str, VbStrConv::Wide, 0);
    
    //ひらがな→カタカナ
    str = "画像処理そりゅーしょん";    //→画像処理ソリューション
    str = Strings::StrConv(str, VbStrConv::Katakana, 0);
    //カタカナ→ひらがな
    str = "画像処理ソリューション";    //→画像処理そりゅーしょん
    str = Strings::StrConv(str, VbStrConv::Hiragana, 0);
    

    (参考)
    http://msdn.microsoft.com/ja-jp/library/7wtc81z6%28v=VS.80%29.aspx
    http://msdn.microsoft.com/ja-jp/library/microsoft.visualbasic.vbstrconv.aspx

    【C++/CLI】ファイルパス(フルパス)からファイル名、拡張子、ディレクトリの取得

    ファイルを開くダイアログボックスなどから取得したファイル名(フルパス)からファイル名や拡張子、
    ディレクトリなどを取得する場合には

    System::IOクラスのGet×××メソッド

    を用います。

     

    以下、サンプルプログラムです。

    //ファイル名(フルパス)
    String^ FullPath = "c:\\Dir1\\Dir2\\Bitmap.bmp";
    String ^FileName, ^Extension, ^Directory;
    
    //ファイル名の取得(拡張子を含む)        → "Bitmap.bmp"
    FileName = IO::Path::GetFileName(FullPath);
    //ファイル名の取得(拡張子を含まない) → "Bitmap"
    FileName = IO::Path::GetFileNameWithoutExtension(FullPath);
    //拡張子の取得                         → ".bmp"  ※ピリオド"."を含みます。
    Extension = IO::Path::GetExtension(FullPath);
    //ディレクトリの取得                    → "c:\Dir1\Dir2"
    Directory = IO::Path::GetDirectoryName (FullPath);
    //相対パスから絶対パスを取得            → "c:\Dir1\Dir2\Bitmap.bmp"
    FullPath = IO::Path::GetFullPath("Bitmap.bmp");

     

    相対パスから絶対パスを取得する場合、指定したファイル名がカレントディレクトリに無い場合は
    カレントディレクトリにファイル名が追加された文字列が返されます。

    【C++/CLI】数値の書式(フォーマット)指定文字列

    数値の表示桁数などの設定にはFormatメソッドかToStringメソッドなどで可能ですが、ここではToStringメソッドによる設定方法を紹介します。

     

    【コード例】
    int Val = 123;
    String^ Txt = Val.ToString(“D5”);

     

    とすると
    Txt = “00123”
    となります。

     

    以下、主なフォーマットの設定例です。

     

    標準書式指定文字列

    書式 書式設定文字列 データ型 出力
    10進数 D Int32 123 123
    10進数(桁数設定) D5 Int32 123 00123
    固定小数点 F Double 123.456 123.46
    固定小数点(桁数設定) F6 Double 123.456 123.456000
    パーセント P Double 0.12345 12.35%
    パーセント(桁数設定) P3 Double 0.123456 12.346%
    16進数(小文字) x Int32 123456 1e240
    16進数(大文字) X Int32 123456 1E240
    通貨 C Double 1234567 \1,234,567
    数字 N Double 123456 123,456.00
    数字(桁数設定) N1 Double 12345678 123,456.8

    詳細は下記ページを参照願います。

    http://msdn.microsoft.com/ja-jp/library/241ad66z(VS.80).aspx

     

    カスタム数値書式指定文字列

    書式 書式設定文字列 データ型 出力
    ゼロプレースホルダ 00000 Double 123 00123
    000.000 Double 12.3456 012.346
    000.000 Double 123.45 123.450
    桁プレースホルダ ##### Double 123 123
    #.# Double 1.2 1.2
    #.# Double 1 1
    #.# Double 123.456 123.5
    #,# Double 123456789 123,456,789
    (00)####-#### Double 312345678 (03)1234-5678
    正、負、0別表示 +#;-#;±0 Double 123 +123
    +#;-#;±0 Double -123 -123
    +#;-#;±0 Double 0 ±0
    #;(#) Double 123 123
    #;(#) Double -123 (123)

    詳細は下記ページを参照願います

    http://msdn.microsoft.com/ja-jp/library/7x5bacwt(VS.80).aspx

     

    【右寄せ】
    int Val = 123;
    String^ Txt = Val.ToString()->PadLeft(5);
    とすると
    Txt = ”  123″
    となります。

     

    【左寄せ】
    int Val = 123;
    String^ Txt = Val.ToString()->PadRight(5);
    とすると
    Txt = “123  ”
    となります。

     

    右寄せ/左寄せは、少し紛らわしいですが、左側を穴埋めして右寄せ(PadLeft)、右側を穴埋めして左寄せ(PadRight)となります。
    カッコ内の数値が文字数になります。

     

    【C++/CLI】文字列の分割(System.String.Splitメソッド)

    文字列を特定の文字で分割し、Stringの配列に格納するには

    System.String.Splitメソッド

    を用います。
    CSVファイルを読み込む時などはカンマ(,)で区切るので、このSplitメソッドを使います。

     

    このメソッドの定義は

    array^ Split (
        array<String^>^ separator,
        StringSplitOptions options
    )

     

    パラメータ

    separator

    分割する文字列をString配列で指定します。

    options

    StringSplitOptions::Nodeを指定すると分割した文字列が空の場合でもそのまま返します。
    StringSplitOptions::RemoveEmptyEntriesを指定すると分割した文字列が空の場合、除去されます

     

    【サンプルプログラム】

    String^ SrcText = "A,B;C,,E,F";
    
    //分割する文字列の配列
    array<String^>^ SepString ={",", ";"};
    
    //文字列の分割
    array<String^>^ TextArr1 = SrcText->Split(SepString, StringSplitOptions::RemoveEmptyEntries);
    array<String^>^ TextArr2 = SrcText->Split(SepString, StringSplitOptions::None);

    このコードを実行し、TestArr1とTestArr2の中身をウォッチで確認すると、以下の通りとなります。

     

     

    区切り文字が1つの場合は

    array<String^>^ TextArr3 = SrcText->Split(‘,’);

    のように区切ることもできます。
    ただし、この場合はシングルクォーテーション( )なので注意して下さい。

    【C++/CLI】改行文字

    メッセージボックスなどの文字列を改行させる時には

    System.Environment.NewLineプロパティ

    を用います。

    以下、サンプルと実行例です。

     

    【サンプルプログラム】

    private: System::Void button1_Click(System::Object^  sender, System::EventArgs^  e) {
             button1->Text = "1行目の文字列" + Environment::NewLine +
                                     "2行目の文字列";
             label1->Text  = "1行目の文字列" + Environment::NewLine +
                                    "2行目の文字列";
    
             MessageBox::Show("ボタンとラベルの文字列が" + Environment::NewLine +
                                           "変更されました。");
         }

     

    【実行例】

     

    ボタンやラベルに関しては、プロパティウィンドウで文字列を入力すると改行も繁栄されます。

    また、System.Environment.NewLineプロパティを使わずとも、\n\rを使って

     

    MessageBox::Show(“ボタンとラベルの文字列が\r\n変更されました。”);

     

    のように書くことも可能です。

     

    【C++/CLI】テキストボックスの文字列を変数へ代入

    テキストボックスの文字列はtextbox1->Textのようにすると取得できますが、この文字列を数値へ変換するには各型に用意されているParseメソッドを使います。

     

    (例)
    int Val= int::Parse(textbox1->Text);

     

    文字列変換する型は必要に応じて変えてください。
    ただし、この場合、テキストボックスに数値ではない文字列が入力された場合、エラーとなるので、エラー処理を行う場合はTryParseメソッドを用います。

    (例)
    int Val;
    bool result = int::TryParse(textbox1->Text, Val );

     

    テキストボックスの文字列がint 型として判断された場合、変数(上の例ではVal)に値が代入され、戻り値(result)にtrueが戻されます。
    失敗した場合は戻り値がfalseとなります。

     

    このエラー処理を用いると、数値としてByte型の値(0~255)が欲しいときなど便利です。

     

    (例)
    Byte Val ;
    bool result = Byte::TryParse(textbox1->Text, Val );

     

    詳細はmsdn2の「Int32.TryParse メソッド 」に記載されています。

    【C++/CLI】 nullチェック

    今回は、あまり教える程理解できていないので、メモ的な意味で...

     

    C言語ではメモリを確保したか?を確認するのにポインタがNULLかどうか確認しますが、C++では『^』で表されるハンドル型というC言語のポインタにも似た型が登場し、このハンドル型で示されるオブジェクトが確保されたのか?(gcnewされたのか?)を確認するのにはnullptrを用います。

     

    (コード例)

    System::Drawing::Graphics^ g;
    if (g == nullptr) MessageBox::Show("gは未定義です");	//MessageBoxが表示される
    g = CreateGraphics();
    if (g == nullptr) MessageBox::Show("gは未定義です");	//MessageBoxが表示されない

    しかし、System::Drawing::Colorクラスでは

    System::Drawing::Color^ c;

    とも

    System::Drawing::Color c;

    とも書けるのです。

     

    このとき

    System::Drawing::Color c;
    if (c == nullptr) MessageBox::Show("cは未定義です");

    と書くと

     

    error C2446: ‘==’ : ‘nullptr’ 型から ‘System::Drawing::Color’ 型への変換ができません。
    この変換を実行可能なユーザー定義変換演算子がないか、または演算子を呼び出せません。
    error C2088: ‘==’ : class に対して正しくありません。

     

    というエラーメッセージが表示され怒られてしまします。

     

    正解は

    System::Drawing::Color c;
    if (c == System::Drawing::Color::Empty) MessageBox::Show("cは未定義です");

    のように書きます。

     

    そんなnullチェックをするのに、nullptrで比較してしまいそうなクラスをまとめておこうと思います。

    System::Drawing::Colorクラス System::Drawing::Color::Empty
    System::IntPtrクラス System::IntPtr::Zero

     

    上記表はおいおい追記しようと思いますが、こんなのもあるよ!と思った方はこのページ下部よりコメント頂けると助かります。

    よろしくお願い致します。

    【C++/CLI】アンカープロパティによるフォームのリサイズ処理

    フォームのリサイズ時にボタンなどの位置をフォームの大きさに合わせて調整する場合、位置を調整するコントロールのAnchorプロパティを設定すると簡単に位置調整が可能となります。

     

     

    Anchorプロパティは指定した方向がリサイズ時の位置の基準となり、指定した方向の距離が一定に保たれるようにコントロールの位置が調整されます。

     

    何はともあれ、以下の動きを見て頂くと分かりやすいと思います。

     

    【button1~button4:Top,Leftの場合】※Top,Leftはデフォルト設定値です。


     

    【button4:Bottom,Rightの場合】

     

    【button2,button4:Top,Left,Rightの場合】

     

    【button3,button4:Bottomの場合】

     

    【button4:Top,Bottom,Left,Rightの場合】

     


    VB6.0の時はフォームのリサイズイベントで座標を計算しながら位置を調整していましたが、.NETからは、このAnchorプロパティを使う事でやたらと簡単になりました。

    【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 で作成しています。