首页 文章

显示来自FLIR相机的`float [,]`热图像的图像输入

提问于
浏览
1

在过去的几天里,我一直在使用FLIR Thermovision相机,并将一个非常简单的应用程序整合在一起,这个应用程序在许多不同的地方都有一些方面(其中大多数都在stackoverflow上) .

话题

  • 在wpf应用程序中托管ActiveX组件

  • Float[,] 数组到 BitmapImage

  • 使用 MemoryStreamBitmapImage 在wpf图像控件中显示绑定位图

1. Active X control

Flir Thermovision SDK 2.6附带一个ActiveX组件DLL . AxCAMCTRLLib.dll . 在WinForms应用程序中,您只需将工具添加到工具框中,然后单击并将组件拖到窗体上即可 . 这将自动添加对项目的正确引用 . 要在wpf应用程序中使用它,这将不起作用 . 在后面看来这似乎很容易,但它没有在他们的文档中列出 .

首先,我必须手动导航到AxCAMCTRLLib.dll并将其添加到引用 . 然后为项目添加一个新窗口 . 这将是一个隐藏窗口,仅用于托管activeX组件 . 这也需要WindowsFormsIntegration引用软件ActiveX组件 .

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;


    }

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

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


        camCtrl.Close();


        base.OnClosing(e);
    }

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

2. Float[,] array to 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. Displaying the image

我创建了一个简单的帮助类:

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(object sender,RoutedEventArgs e)

我使用计时器遇到的几件事情 . 首先,应用程序时间和摄像机时间不一样,因此,我在捕获开始时停止计时器,并在结束后重新启动计时器 . 幸运的是,线程等待相机返回图像 . 这在很大程度上解决了滞后问题 . 其次, _image.Freeze() 声明是必不可少的 . 没有它,一旦启动就会出现"Must create DependencySource on same Thread as the DependencyObject."错误 . Freeze方法使图像可供其他线程使用 .

1 回答

  • 0

    这确实属于codereview.stackexchange.com

相关问题