ライブ中のオーバーレイの移動
概要
このプログラムはICImagingControl上に十字線をオーバーレイで描画し、画面の縮小・拡大にかかわらず常に同じ位置・太さで表示します。さらに、フォームのサイズに応じて、ICImagingControlの表示倍率を自動調整し、アスペクト比を維持しつつ枠内にぴったり収まるように表示しています。
サンプルプログラム
利用した開発環境 | Visual Studio™ 2022 |
---|---|
SDK | IC Imaging Control 3.5(Python, C#, VB.NET) |
デバイスドライバ | Cam33U_setup,gigecam_setup,usbcam,AFU420_setup,usb2pro_drv |
デバイス | TISカメラ全般(MIPI CSI-2&FPD-Link IIIカメラを除く) |
サンプル(C#) | full-size-display-and-overlay.zip |
サンプル(VB.NET) | ー |
exeファイル アプリケーション |
ー |
別途ファイル | ー |
関連参照URL | ー |
サンプルツールの外観
解説
Form読み込み時の処理
private Form _sinkDisplay;
private FrameQueueSink _sink;
int intsldZoom = 1;
private void Form1_Load( object sender, EventArgs e )
{
icImagingControl1.MouseWheel += new System.Windows.Forms.MouseEventHandler(this.icImagingControl1_MouseWheel);
//画像ストリームにおけるOverlayBitmapオブジェクトの位置をコントロール
icImagingControl1.OverlayBitmapPosition = TIS.Imaging.PathPositions.Display;
//入力されたフレームを取得する無名関数を使ったRGB32のみ受け入れるFrameQueueSink
_sink = new TIS.Imaging.FrameQueueSink( (img) => { return ShowImage( img ); }, new FrameType( MediaSubtypes.RGB32 ), 5 );
//sinkに受け渡す
icImagingControl1.Sink = _sink;
icImagingControl1.LiveDisplayDefault = false;
TIS.Imaging.OverlayBitmap ob = icImagingControl1.OverlayBitmapAtPath[PathPositions.Display];
ob.Enable = true;
}
ここでは、フォームが読み込まれるタイミングでライブ表示の制御、デジタルズーム操作、オーバーレイ描画機能の有効化を行っています。
まず、MouseWheelイベントに後述のicImagingControl1_MouseWheel関数を登録することで、Ctrlキーを押しながらマウスホイールを操作することでズーム倍率の調整ができるようになります。
次に、オーバーレイ描画の対象パスをDisplayに指定しています。これにより、表示パス上で十字線を描画するための設定が整います。 そして、カメラからの画像データを処理するために、RGB32形式に限定したFrameQueueSinkを定義しています。このSinkは後述のShowImageのコールバック関数を通じて各フレームごとに処理(画像表示など)を実行します。そのSinkを icImagingControl1.Sink に設定することで、取得された映像がこのパイプラインを通して処理されるようになります。 最後に、OverlayBitmapAtPath[Display] を有効にすることで、ライブ映像上にオーバーレイを描画する準備が整います。
Ctrl+マウスホイールで画面の拡大縮小
private void icImagingControl1_MouseWheel(object sender, MouseEventArgs e)
{
//Ctrl キーが押された状態であるか確認
if ((Control.ModifierKeys & Keys.Control) == Keys.Control)
{
if (icImagingControl1.LiveDisplayDefault == false)
{
int value = (e.Delta * SystemInformation.MouseWheelScrollLines / 360) + intsldZoom;
if (80 >= value && 1 <= value)
{
intsldZoom = value;
icImagingControl1.LiveDisplayZoomFactor = (float)intsldZoom / 10.0f;
}
}
}
}
ここではCtrlキーを押しながらマウスホイールを回転させることで、IC Imaging Control上のライブ映像のズーム倍率を変更することを可能にしています。まず、Control.ModifierKeysを用いてCtrlキーが押されているかを確認します。ホイールの回転量は e.Deltaから取得し、移動量を調整してから現在の倍率intsldZoom に加算します。最後に倍率は0.1単位で設定することで、拡大縮小が滑らかに行えるようにしています。
FrameQueueSinkのコールバック関数
private TIS.Imaging.FrameQueuedResult ShowImage( IFrameQueueBuffer buffer )
{
if( _sinkDisplay != null && !_sinkDisplay.IsDisposed )
{
// ビットマップのコピーを作成する
_sinkDisplay.BackgroundImage = buffer.CreateBitmapCopy();
}
return FrameQueuedResult.ReQueue;
}
この関数は、取得したカメラ映像を別ウィンドウにリアルタイムで表示する処理です。本サンプルでは PathPositions.Display を使用しているため、_sinkDisplay は無効となっていますが、PathPositions.Sink を使用し、かつ _sinkDisplay が有効な場合には、buffer.CreateBitmapCopy() を使って画像を Bitmap に変換し、背景画像として表示します。最後に ReQueue を返すことで、次のフレームも引き続き処理されるように設定されています。
オーバーレイ描画のアップデート
//初期のオーバーレイ表示
private int centerX = 100;
private int centerY = 100;
private void icImagingControl1_OverlayUpdate( object sender, TIS.Imaging.ICImagingControl.OverlayUpdateEventArgs e )
{
var ob = e.overlay;
var sz = GetVideoDimension();
// 表示倍率の取得
float zoom = Math.Min((float)icImagingControl1.LiveDisplayWidth / icImagingControl1.LiveDisplayOutputWidth, (float)icImagingControl1.LiveDisplayHeight / icImagingControl1.LiveDisplayOutputHeight);
// 画面上で見せたい太さ(ピクセル)。例: 2px
int desiredScreenPx = 2;
// デバイス座標での線幅:縮小後に desiredScreenPx に見えるよう逆数倍で太文字にする
int wDev = Math.Max(1, (int)Math.Ceiling(desiredScreenPx / zoom));
// サブピクセル落ち回避:表示側で整数ピクセルに乗るよう 1/zoom の倍数に丸める
float step = 1f / zoom;
float snappedX = (float)Math.Round(centerX / step) * step;
float snappedY = (float)Math.Round(centerY / step) * step;
// 透明色でクリア
ob.DropOutColor = Color.Magenta;
ob.Fill(ob.DropOutColor);
using (Graphics g = ob.GetGraphics())
{
// 横線・縦線を矩形で描く(中心に wDev/2 ずらし)
int y = (int)Math.Round(snappedY - wDev / 2f);
int x = (int)Math.Round(snappedX - wDev / 2f);
// 画面外クランプ
y = Math.Max(0, Math.Min(y, sz.Height - 1));
x = Math.Max(0, Math.Min(x, sz.Width - 1));
int h = Math.Min(wDev, sz.Height - y);
int w = Math.Min(wDev, sz.Width - x);
using (var brush = new SolidBrush(Color.FromArgb(255, 0, 0)))
{
// 横バー(全幅)
g.FillRectangle(brush, 0, y, sz.Width, h);
// 縦バー(全高)
g.FillRectangle(brush, x, 0, w, sz.Height);
}
}
}
ここでは、ICImagingControl上に十字線を表示するためのオーバーレイ描画処理を行っています。centerXおよびcenterYに設定された座標を中心に、縦横のラインを重ねて描画します。まず、現在のズーム倍率を取得し、最小値0.1以下にならないように制限します。次に、ズーム状態に関わらず見た目上一定の太さ(例:2ピクセル)を維持するため、表示倍率に応じてデバイス空間上の線の太さwDevを計算します。(表示が極端に縮小された場合でも線が細くなりすぎないようにするため)
さらに、拡大縮小時に線がピクセルとピクセルの中間に落ちて描画が消えてしまうのを防ぐため、1/zoom の倍数に丸める処理を行い、位置を整数ピクセルにスナップさせています。これにより、描画時にピクセルの境界で線が途切れるのを回避しています。その後、Graphicsオブジェクトを取得し、背景を初期化してから、太さに応じた矩形 (FillRectangle) を用いて横線・縦線をそれぞれ描画します。
従来はDrawLineを使用していたため、倍率やピクセルの間に線が引かれてしまい表示が不安定になる問題がありましたが、矩形による描画に変更したことで、拡大縮小や位置調整を行っても線が見えるようになっています。
Fit to FormSizeボタンをクリック
private void btnScreenSizeFit_Click(object sender, EventArgs e)
{
icImagingControl1.LiveDisplayDefault = false;
// ソースサイズと表示枠サイズを取得
int srcW = icImagingControl1.LiveDisplayOutputWidth;
int srcH = icImagingControl1.LiveDisplayOutputHeight;
var box = icImagingControl1.Size;
if (srcW <= 0 || srcH <= 0 || box.Width <= 0 || box.Height <= 0) return;
// 枠内に収まる最大スケール(長辺フィット)
double scale = Math.Min((double)box.Width / srcW, (double)box.Height / srcH);
icImagingControl1.LiveDisplayZoomFactor = (float)scale;
// 再描画
icImagingControl1.Invalidate();
}
ボタンクリックすると、カメラのライブ表示サイズをICImagingControlのウィンドウサイズに合わせて自動的に調整するプログラムです。まず、カメラ映像の出力サイズ(解像度)とICImagingControlの表示領域サイズを取得し、幅と高さの比率を比較することで、アスペクト比を維持したままウィンドウ内に映像を最大限収めるための最適なズーム倍率を計算します。この倍率はLiveDisplayZoomFactorに設定する必要があるため、0.1刻みに丸めたうえで、倍率の範囲を1(0.1倍)~100(10倍)に制限しています。最後にInvalidate()を呼び出し、ズーム変更に伴う再描画を呼び出して、画面を更新します。
リサイズのときの処理
private void icImagingControl1_Resize(object sender, EventArgs e)
{
if (chkKeepAspectRatio.Checked)
{
// ソースサイズと表示枠サイズを取得
int srcW = icImagingControl1.LiveDisplayOutputWidth;
int srcH = icImagingControl1.LiveDisplayOutputHeight;
var box = icImagingControl1.Size;
double scale = Math.Min((double)box.Width / srcW, (double)box.Height / srcH);
icImagingControl1.LiveDisplayZoomFactor = (float)scale;
}
else
{
icImagingControl1.LiveDisplayWidth = icImagingControl1.Width;
icImagingControl1.LiveDisplayHeight = icImagingControl1.Height;
}
}
「Keep Aspect Ratio」ボタンを押したときにアスペクト比を維持したままリサイズを行うかどうか条件分岐をしています。
この関数は、icImagingControl1のサイズが変更された際に実行され、アスペクト比を維持するかどうかを chkKeepAspectRatioチェックボックスの状態に応じて分岐しています。チェックされている場合は、カメラの出力解像度(ソースサイズ)と表示枠のサイズをもとに、ウィンドウ内に収まる最大倍率(長辺フィット)を計算し、LiveDisplayZoomFactorを調整してアスペクト比を保ったまま、拡大・縮小表示します。チェックされていない場合は、アスペクト比を無視して表示サイズをそのままコントロールの幅と高さに合わせて強制的にリサイズします。