ここ数日、FLIR Thermovision カメラを使用して作業を行っており、非常に単純なアプリケーションを作成しました。このアプリケーションには、多くの異なる場所 (そのほとんどはスタック オーバーフローにあります) に見られるいくつかの側面があります。
トピック
- wpf アプリケーションで ActiveX コンポーネントをホストする
Float[,]
への配列BitmapImage
MemoryStream
とを使用して wpf イメージ コントロールにバインドされたビットマップを表示するBitmapImage
1. アクティブ X コントロール
Flir Thermovision SDK 2.6 には、ActiveX コンポーネント dll が付属しています。AxCAMCTRLLib.dll。WinForms アプリケーションでは、ツールをツール ボックスに追加し、コンポーネントをクリックしてフォームにドラッグするだけです。これにより、正しい参照がプロジェクトに自動的に追加されます。wpf アプリケーションで使用するには、これは機能しません。後から考えると、これはかなり簡単に思えますが、ドキュメントには記載されていませんでした。
まず、手動で AxCAMCTRLLib.dll に移動し、参照に追加する必要がありました。次に、プロジェクトに新しいウィンドウを追加します。これは、activeX コンポーネントをホストするためだけに使用される非表示のウィンドウになります。これには、ホース ActiveX コンポーネントへの WindowsFormsIntegration 参照も必要です。
using CAMCTRLLib;
using AxCAMCTRLLib;
namespace camView
{
public partial class CameraCtrl : Window
{
public AxCAMCTRLLib.AxLVCam camera;
private System.Windows.Forms.Integration.WindowsFormsHost host;
public CameraCtrl()
{
InitializeComponent();
host = new System.Windows.Forms.Integration.WindowsFormsHost();
camera = new AxCAMCTRLLib.AxLVCam();
host.Child = camera;
this.grid1.Children.Add(host);
}
}
}
これで、MainWindow で新しいウィンドウを作成して表示し、すぐに非表示にCameraCtrl
して、パブリック ActiveX コントロールにアクセスできるようになりました。
public MainWindow()
{
InitializeComponent();
camCtrl = new CameraCtrl();
camCtrl.BeginInit();
camCtrl.Show();
camCtrl.Hide();
camCtrl.ShowInTaskbar = false;
camCtrl.ShowActivated = false;
}
OnClosing
MainWindow
非表示のウィンドウも閉じるようにメソッドを変更する必要があります。
protected override void OnClosing(System.ComponentModel.CancelEventArgs e)
{
camCtrl.Close();
base.OnClosing(e);
}
これで、activex オブジェクトに含まれるすべてのコントロール メソッドにアクセスできるようになりました。
2. Float[,] 配列へBitmapImage
カメラからの出力画像はさまざまな形式で返すことができますが、私が使用している特定のカメラobject
では、float[,]
. 熱であるため、出力ピクセル値は温度を表します。つまり、最初に正規化してから に変換しBitmap
、次に に格納してMemoryStream
から のソースに追加する必要がありBitmapImage
ます。私が使用した方法は次のとおりです。
private BitmapImage setDisplayImage(float[,] image)
{
float[,] tempStoreImage = new float[240 , 320];
float max = -10000.0f, min = 10000.0f;
BitmapImage localBitmap;
for (int j = 0; j < 320; j++)
{
for (int i = 0; i < 240; i++)
{
tempStoreImage[i,j] = image[j,i];//have to transpose the image from cam
if (tempStoreImage[i,j] > max)
{
max = tempStoreImage[i,j];
}
if (tempStoreImage[i,j] < min)
{
min = tempStoreImage[i,j];
}
}
}
if(max != min)//can't divide by zero
{
System.Drawing.Bitmap newBitmap = new System.Drawing.Bitmap(320, 240);
for (int i = 0; i < 240; i++)
{
for (int j = 0; j < 320; j++)
{
tempStoreImage[i,j] = (float)Math.Round((double)(tempStoreImage[i,j] - min) * 255 / (max - min));//Normalize and set between 0 - 255
System.Drawing.Color newColor = System.Drawing.Color.FromArgb((int)tempStoreImage[i, j],0, 0, 0);//Gray scale color using alpha channel
newBitmap.SetPixel(j, i, newColor);
}
}
System.Drawing.Image img = (System.Drawing.Image)newBitmap;
MemoryStream stream = new MemoryStream();
img.Save(stream, System.Drawing.Imaging.ImageFormat.Png);//add Bitmap to memory stream
stream.Position = 0;
localBitmap = new BitmapImage();
localBitmap.BeginInit();
localBitmap.StreamSource = stream; //
localBitmap.EndInit();
}
else localBitmap = new BitmapImage();//dark image
return localBitmap;
}
3. 画像の表示
簡単なヘルパー クラスを作成しました。
class BindData : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged(string PropertyName)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(PropertyName));
}
BitmapImage _image;
public BitmapImage Image
{
get { return _image; }
set
{
_image = value;
_image.Freeze();
OnPropertyChanged("Image");
}
}
}
次に、MainWindow に静的ヘルパー クラス オブジェクトを作成し (おそらく静的である必要はありませんが、他のクラスで使用する予定です) BindData bind = new BindData()
、. image1.DataContext = bind
次に、配列と一致するようにバインディングとウィンドウ サイズを設定します。
<Image Height="240" HorizontalAlignment="Left" Margin="204,21,0,0" Name="image1" Stretch="Fill" VerticalAlignment="Top" Width="320" Source="{Binding Image}"/>
System.Timers.Timer
最後に、次を使用して画像をキャプチャしました。
private void cameraCap()
{
if (continueDisplay)
{
captureTimer.Stop();
lock (camCtrl)
{
object yo = camCtrl.camera.GetImage(3);
bind.Image = setDisplayImage(yo as float[,]);
}
captureTimer.Start();
}
else
captureTimer.Stop();
}
private void capture_Click(object sender, RoutedEventArgs e)
{
continueDisplay = true;
captureTimer.Start();
}
private void kill_Click(オブジェクト送信者, RoutedEventArgs e) { continueDisplay = false; }
タイマーを使用して遭遇したことがいくつかあります。まずアプリのタイミングとカメラのタイミングが違うので、撮影開始時にタイマーをストップさせ、終了後にタイマーをスタートさせています。幸いなことに、スレッドはカメラが画像を返すのを待ちます。これにより、ほとんどの場合、ラグが処理されます。第二に、_image.Freeze()
ステートメントは不可欠です。それがないと、「DependencyObject と同じスレッドに DependencySource を作成する必要があります」というメッセージが表示されます。エラーが発生すると。Freeze メソッドは、イメージを他のスレッドで使用できるようにします。