Reputation: 1675
I have been working with a FLIR Thermovision camera the last couple days and have pulled together a very simple application that has a few aspects found in many different places (most of which here on stackoverflow).
Topics
Float[,]
array to BitmapImage
MemoryStream
and BitmapImage
1. Active X control
The Flir Thermovision SDK 2.6 comes with an ActiveX component dll. AxCAMCTRLLib.dll. In a WinForms application you can simply add the tool to the tool box and click and drag the component onto the form. This will automatically add the correct references to the project. To use it in a wpf application this won't work. In hind sight this seems pretty easy but it wasn't listed in their documentation.
First I had to navigate to the AxCAMCTRLLib.dll manually and add it to the references. Then add a new window to the project. This will be a hidden window used only to host the activeX component. This also requires WindowsFormsIntegration reference to hose ActiveX components.
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);
}
}
}
Now in the MainWindow I can create, show then promptly hide a new window CameraCtrl
and have access to the public ActiveX control.
public MainWindow()
{
InitializeComponent();
camCtrl = new CameraCtrl();
camCtrl.BeginInit();
camCtrl.Show();
camCtrl.Hide();
camCtrl.ShowInTaskbar = false;
camCtrl.ShowActivated = false;
}
OnClosing
method in MainWindow
has to be modified to close the hidden window as well.
protected override void OnClosing(System.ComponentModel.CancelEventArgs e)
{
camCtrl.Close();
base.OnClosing(e);
}
With that I can now access all of the control methods contained in the activex object.
2. Float[,] array to BitmapImage
The output images from the camera can be returned in a number of different formats, but for the particular camera I'm using it returns an object
that contains a float[,]
. Since it's thermal the output pixel values represent temperatures. That means they must be normalized and then converted first to Bitmap
then stored in a MemoryStream
then added to the source of a BitmapImage
. The method I used is the following.
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
I created a simple helper class:
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");
}
}
}
And then created a static helper class object in the MainWindow (probably doesn't need to be static but I plan to use it in other classes.) BindData bind = new BindData()
and set the image1.DataContext = bind
. Then set the binding and window size to match my array:
<Image Height="240" HorizontalAlignment="Left" Margin="204,21,0,0" Name="image1" Stretch="Fill" VerticalAlignment="Top" Width="320" Source="{Binding Image}"/>
And finally captured images using a 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) { continueDisplay = false; }
Couple things I ran into with using the timer. First the application timing and the camera timing are not the same so, I stop the timer at the beginning of a capture and start it back up after the end. Luckily the thread waits for the camera to return an image. This takes care of lag for the most part. Second the _image.Freeze()
statement is essential. Without it you will get "Must create DependencySource on same Thread as the DependencyObject." error once initiated. The Freeze method makes the image available to other threads.
Upvotes: 1
Views: 2965