Отображение потока изображений тепловых изображений с плавающей точкой, плавающих [,]
Я работал с камерой FLIR Thermovision последние пару дней и собрал очень простое приложение, в котором есть несколько аспектов, обнаруженных в самых разных местах (большинство из которых здесь, на stackru).
темы
- размещение компонента ActiveX в приложении wpf
Float[,]
массив дляBitmapImage
- Отображение связанного растрового изображения в элементе управления изображением wpf с помощью
MemoryStream
а такжеBitmapImage
1. Активный X-контроль
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);
}
}
}
Теперь в главном окне я могу создать, показать, а затем быстро скрыть новое окно 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. Переместить массив [,] в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()
утверждение важно. Без этого вы получите "Необходимо создать DependencySource в том же потоке, что и DependencyObject". ошибка однажды инициирована. Метод Freeze делает изображение доступным для других потоков.