显示FLIR相机的“float[,]”热图像的图像馈送



在过去的几天里,我一直在使用FLIR Thermovision相机,并开发了一个非常简单的应用程序,该应用程序在许多不同的地方都有一些方面(其中大部分在stackoverflow上)。

主题

  1. 在wpf应用程序中托管ActiveX组件
  2. Float[,]阵列到BitmapImage
  3. 使用MemoryStreamBitmapImage在wpf图像控件中显示绑定位图

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);
    }
}
}

现在,在主窗口中,我可以创建、显示然后立即隐藏一个新窗口CameraCtrl,并可以访问公共ActiveX控件。

public MainWindow()
    {

        InitializeComponent();
        camCtrl = new CameraCtrl();
        camCtrl.BeginInit();
        camCtrl.Show();
        camCtrl.Hide();

        camCtrl.ShowInTaskbar = false;
        camCtrl.ShowActivated = false;

    }

MainWindow中的OnClosing方法也必须修改以关闭隐藏窗口。

    protected override void OnClosing(System.ComponentModel.CancelEventArgs e)
    {

        camCtrl.Close();

        base.OnClosing(e);
    }

有了它,我现在可以访问activex对象中包含的所有控制方法。

2.浮动[,]数组到BitmapImage

相机的输出图像可以以多种不同的格式返回,但对于我使用的特定相机,它会返回包含float[,]object。由于它是热的,所以输出像素值表示温度。这意味着它们必须被归一化,然后首先转换为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方法使图像可用于其他线程。

这确实属于codereview.stackeexchange.com

最新更新