Allen's 데이터 맛집
C#과 Basler Pylon을 사용한 라이브 뷰 애플리케니션의 메인 윈도우 MainForm 본문
Programming/Pylon .Net API
C#과 Basler Pylon을 사용한 라이브 뷰 애플리케니션의 메인 윈도우 MainForm
Allen93 2024. 8. 28. 17:02C#과 Basler Pylon .NET API를 사용하여 라이브 뷰 애플리케이션의 메인 윈도우 역할을 하는 MainForm 클래스에 대해 설명하겠습니다. 이 클래스는 카메라와의 연결을 관리하고, 이미지를 캡처하며, 다양한 카메라 설정을 조정할 수 있는 기능을 제공합니다.
클래스 개요
MainForm 클래스는 Basler Pylon 카메라의 다양한 기능을 제어할 수 있는 메인 윈도우를 구현합니다. 이 클래스는 카메라 연결, 이미지 캡처, 설정 조정, 그리고 사용자 인터페이스 업데이트 등의 주요 기능을 담당합니다.
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Drawing.Imaging;
using System.Text;
using System.Windows.Forms;
using System.Diagnostics;
using PylonLiveView;
using Basler.Pylon;
namespace PylonLiveView
{
// The main window.
public partial class MainForm : Form
{
private Camera camera = null;
private PixelDataConverter converter = new PixelDataConverter();
private Stopwatch stopWatch = new Stopwatch();
// Set up the controls and events to be used and update the device list.
public MainForm()
{
InitializeComponent();
// Set the default names for the controls.
testImageControl.DefaultName = "Test Image Selector";
pixelFormatControl.DefaultName = "Pixel Format";
widthSliderControl.DefaultName = "Width";
heightSliderControl.DefaultName = "Height";
gainSliderControl.DefaultName = "Gain";
exposureTimeSliderControl.DefaultName = "Exposure Time";
// Update the list of available camera devices in the upper left area.
UpdateDeviceList();
// Disable all buttons.
EnableButtons(false, false);
}
// Occurs when the single frame acquisition button is clicked.
private void toolStripButtonOneShot_Click(object sender, EventArgs e)
{
OneShot(); // Start the grabbing of one image.
}
// Occurs when the continuous frame acquisition button is clicked.
private void toolStripButtonContinuousShot_Click(object sender, EventArgs e)
{
ContinuousShot(); // Start the grabbing of images until grabbing is stopped.
}
// Occurs when the stop frame acquisition button is clicked.
private void toolStripButtonStop_Click(object sender, EventArgs e)
{
Stop(); // Stop the grabbing of images.
}
// Occurs when a device with an opened connection is removed.
private void OnConnectionLost(Object sender, EventArgs e)
{
if (InvokeRequired)
{
BeginInvoke(new EventHandler<EventArgs>(OnConnectionLost), sender, e);
return;
}
DestroyCamera();
UpdateDeviceList();
}
// Occurs when the connection to a camera device is opened.
private void OnCameraOpened(Object sender, EventArgs e)
{
if (InvokeRequired)
{
BeginInvoke(new EventHandler<EventArgs>(OnCameraOpened), sender, e);
return;
}
EnableButtons(true, false);
}
// Occurs when the connection to a camera device is closed.
private void OnCameraClosed(Object sender, EventArgs e)
{
if (InvokeRequired)
{
BeginInvoke(new EventHandler<EventArgs>(OnCameraClosed), sender, e);
return;
}
EnableButtons(false, false);
}
// Occurs when a camera starts grabbing.
private void OnGrabStarted(Object sender, EventArgs e)
{
if (InvokeRequired)
{
BeginInvoke(new EventHandler<EventArgs>(OnGrabStarted), sender, e);
return;
}
stopWatch.Reset();
updateDeviceListTimer.Stop();
EnableButtons(false, true);
}
// Occurs when an image has been acquired and is ready to be processed.
private void OnImageGrabbed(Object sender, ImageGrabbedEventArgs e)
{
if (InvokeRequired)
{
BeginInvoke(new EventHandler<ImageGrabbedEventArgs>(OnImageGrabbed), sender, e.Clone());
return;
}
try
{
IGrabResult grabResult = e.GrabResult;
if (grabResult.IsValid)
{
if (!stopWatch.IsRunning || stopWatch.ElapsedMilliseconds > 33)
{
stopWatch.Restart();
Bitmap bitmap = new Bitmap(grabResult.Width, grabResult.Height, PixelFormat.Format32bppRgb);
BitmapData bmpData = bitmap.LockBits(new Rectangle(0, 0, bitmap.Width, bitmap.Height), ImageLockMode.ReadWrite, bitmap.PixelFormat);
IntPtr ptrBmp = bmpData.Scan0;
converter.OutputPixelFormat = PixelType.BGRA8packed;
converter.Convert(ptrBmp, bmpData.Stride * bitmap.Height, grabResult);
bitmap.UnlockBits(bmpData);
Bitmap bitmapOld = pictureBox.Image as Bitmap;
pictureBox.Image = bitmap;
if (bitmapOld != null)
{
bitmapOld.Dispose();
}
}
}
}
catch (Exception exception)
{
ShowException(exception);
}
finally
{
e.DisposeGrabResultIfClone();
}
}
// Occurs when a camera has stopped grabbing.
private void OnGrabStopped(Object sender, GrabStopEventArgs e)
{
if (InvokeRequired)
{
BeginInvoke(new EventHandler<GrabStopEventArgs>(OnGrabStopped), sender, e);
return;
}
stopWatch.Reset();
updateDeviceListTimer.Start();
EnableButtons(true, false);
if(e.Reason != GrabStopReason.UserRequest)
{
MessageBox.Show("A grab error occured:\n" + e.ErrorMessage, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
// Helps to set the states of all buttons.
private void EnableButtons(bool canGrab, bool canStop)
{
toolStripButtonContinuousShot.Enabled = canGrab;
toolStripButtonOneShot.Enabled = canGrab;
toolStripButtonStop.Enabled = canStop;
}
// Stops the grabbing of images and handles exceptions.
private void Stop()
{
try
{
camera.StreamGrabber.Stop();
}
catch (Exception exception)
{
ShowException(exception);
}
}
// Closes the camera object and handles exceptions.
private void DestroyCamera()
{
try
{
if (camera != null)
{
testImageControl.Parameter = null;
pixelFormatControl.Parameter = null;
widthSliderControl.Parameter = null;
heightSliderControl.Parameter = null;
gainSliderControl.Parameter = null;
exposureTimeSliderControl.Parameter = null;
}
}
catch (Exception exception)
{
ShowException(exception);
}
try
{
if (camera != null)
{
camera.Close();
camera.Dispose();
camera = null;
}
}
catch (Exception exception)
{
ShowException(exception);
}
}
// Starts the grabbing of a single image and handles exceptions.
private void OneShot()
{
try
{
camera.Parameters[PLCamera.AcquisitionMode].SetValue(PLCamera.AcquisitionMode.SingleFrame);
camera.StreamGrabber.Start(1, GrabStrategy.OneByOne, GrabLoop.ProvidedByStreamGrabber);
}
catch (Exception exception)
{
ShowException(exception);
}
}
// Starts the continuous grabbing of images and handles exceptions.
private void ContinuousShot()
{
try
{
camera.Parameters[PLCamera.AcquisitionMode].SetValue(PLCamera.AcquisitionMode.Continuous);
camera.StreamGrabber.Start(GrabStrategy.OneByOne, GrabLoop.ProvidedByStreamGrabber);
}
catch (Exception exception)
{
ShowException(exception);
}
}
// Updates the list of available camera devices.
private void UpdateDeviceList()
{
try
{
List<ICameraInfo> allCameras = CameraFinder.Enumerate();
ListView.ListViewItemCollection items = deviceListView.Items;
foreach (ICameraInfo cameraInfo in allCameras)
{
bool newitem = true;
foreach (ListViewItem item in items)
{
ICameraInfo tag = item.Tag as ICameraInfo;
if ( tag[CameraInfoKey.FullName] == cameraInfo[CameraInfoKey.FullName])
{
tag = cameraInfo;
newitem = false;
break;
}
}
if (newitem)
{
ListViewItem item = new ListViewItem(cameraInfo[CameraInfoKey.FriendlyName]);
string toolTipText = "";
foreach( KeyValuePair<string, string> kvp in cameraInfo)
{
toolTipText += kvp.Key + ": " + kvp.Value + "\n";
}
item.ToolTipText = toolTipText;
item.Tag = cameraInfo;
deviceListView.Items.Add(item);
}
}
foreach (ListViewItem item in items)
{
bool exists = false;
foreach (ICameraInfo cameraInfo in allCameras)
{
if (((ICameraInfo)item.Tag)[CameraInfoKey.FullName] == cameraInfo[CameraInfoKey.FullName])
{
exists = true;
break;
}
}
if (!exists)
{
deviceListView.Items.Remove(item);
}
}
}
catch (Exception exception)
{
ShowException(exception);
}
}
// Shows exceptions in a message box.
private void ShowException(Exception exception)
{
MessageBox.Show("Exception caught:\n" + exception.Message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
// Closes the camera object when the window is closed.
private void MainForm_FormClosing(object sender, FormClosingEventArgs ev)
{
DestroyCamera();
}
// Occurs when a new camera has been selected in the list. Destroys the object of the currently opened camera device and
// creates a new object for the selected camera device. After that, the connection to the selected camera device is opened.
private void deviceListView_SelectedIndexChanged(object sender, EventArgs ev)
{
if (camera != null)
{
DestroyCamera();
}
if (deviceListView.SelectedItems.Count > 0)
{
ListViewItem item = deviceListView.SelectedItems[0];
ICameraInfo selectedCamera = item.Tag as ICameraInfo;
try
{
camera = new Camera (selectedCamera);
camera.CameraOpened += Configuration.AcquireContinuous;
camera.ConnectionLost += OnConnectionLost;
camera.CameraOpened += OnCameraOpened;
camera.CameraClosed += OnCameraClosed;
camera.StreamGrabber.GrabStarted += OnGrabStarted;
camera.StreamGrabber.ImageGrabbed += OnImageGrabbed;
camera.StreamGrabber.GrabStopped += OnGrabStopped;
camera.Open();
testImageControl.Parameter = camera.Parameters[PLCamera.TestImageSelector];
pixelFormatControl.Parameter = camera.Parameters[PLCamera.PixelFormat];
widthSliderControl.Parameter = camera.Parameters[PLCamera.Width];
heightSliderControl.Parameter = camera.Parameters[PLCamera.Height];
if (camera.Parameters.Contains(PLCamera.GainAbs))
{
gainSliderControl.Parameter = camera.Parameters[PLCamera.GainAbs];
}
else
{
gainSliderControl.Parameter = camera.Parameters[PLCamera.Gain];
}
if (camera.Parameters.Contains(PLCamera.ExposureTimeAbs))
{
exposureTimeSliderControl.Parameter = camera.Parameters[PLCamera.ExposureTimeAbs];
}
else
{
exposureTimeSliderControl.Parameter = camera.Parameters[PLCamera.ExposureTime];
}
}
catch (Exception exception)
{
ShowException(exception);
}
}
}
// If the F5 key has been pressed, update the list of devices.
private void deviceListView_KeyDown(object sender, KeyEventArgs ev)
{
if (ev.KeyCode == Keys.F5)
{
ev.Handled = true;
UpdateDeviceList();
}
}
// Timer callback used to periodically check whether displayed camera devices are still attached to the PC.
private void updateDeviceListTimer_Tick(object sender, EventArgs e)
{
UpdateDeviceList();
}
}
}
2. 주요 구성 요소 및 기능
a. 초기화 및 설정
public MainForm()
{
InitializeComponent();
// Set the default names for the controls.
testImageControl.DefaultName = "Test Image Selector";
pixelFormatControl.DefaultName = "Pixel Format";
widthSliderControl.DefaultName = "Width";
heightSliderControl.DefaultName = "Height";
gainSliderControl.DefaultName = "Gain";
exposureTimeSliderControl.DefaultName = "Exposure Time";
// Update the list of available camera devices in the upper left area.
UpdateDeviceList();
// Disable all buttons.
EnableButtons(false, false);
}
- 초기화: 생성자에서 컨트롤을 초기화하고 기본 이름을 설정합니다.
- 디바이스 리스트 업데이트: 사용 가능한 카메라 디바이스 목록을 갱신합니다.
- 버튼 비활성화: 모든 버튼을 비활성화합니다.
b. 이미지 캡처 버튼 처리
private void toolStripButtonOneShot_Click(object sender, EventArgs e)
{
OneShot(); // Start the grabbing of one image.
}
private void toolStripButtonContinuousShot_Click(object sender, EventArgs e)
{
ContinuousShot(); // Start the grabbing of images until grabbing is stopped.
}
private void toolStripButtonStop_Click(object sender, EventArgs e)
{
Stop(); // Stop the grabbing of images.
}
- 단일 이미지 캡처: 한 장의 이미지를 캡처합니다.
- 연속 이미지 캡처: 이미지를 연속으로 캡처합니다.
- 이미지 캡처 중지: 이미지 캡처를 중지합니다.
c. 연결 상태 처리
private void OnConnectionLost(Object sender, EventArgs e)
{
if (InvokeRequired)
{
BeginInvoke(new EventHandler<EventArgs>(OnConnectionLost), sender, e);
return;
}
DestroyCamera();
UpdateDeviceList();
}
private void OnCameraOpened(Object sender, EventArgs e)
{
if (InvokeRequired)
{
BeginInvoke(new EventHandler<EventArgs>(OnCameraOpened), sender, e);
return;
}
EnableButtons(true, false);
}
private void OnCameraClosed(Object sender, EventArgs e)
{
if (InvokeRequired)
{
BeginInvoke(new EventHandler<EventArgs>(OnCameraClosed), sender, e);
return;
}
EnableButtons(false, false);
}
- 연결 끊김 처리: 카메라 연결이 끊어졌을 때 카메라를 파괴하고 디바이스 리스트를 업데이트합니다.
- 카메라 열림 처리: 카메라 연결이 열렸을 때 버튼을 활성화합니다.
- 카메라 닫힘 처리: 카메라 연결이 닫혔을 때 버튼을 비활성화합니다.
d. 이미지 캡처 처리
private void OnGrabStarted(Object sender, EventArgs e)
{
if (InvokeRequired)
{
BeginInvoke(new EventHandler<EventArgs>(OnGrabStarted), sender, e);
return;
}
stopWatch.Reset();
updateDeviceListTimer.Stop();
EnableButtons(false, true);
}
private void OnImageGrabbed(Object sender, ImageGrabbedEventArgs e)
{
if (InvokeRequired)
{
BeginInvoke(new EventHandler<ImageGrabbedEventArgs>(OnImageGrabbed), sender, e.Clone());
return;
}
try
{
IGrabResult grabResult = e.GrabResult;
if (grabResult.IsValid)
{
if (!stopWatch.IsRunning || stopWatch.ElapsedMilliseconds > 33)
{
stopWatch.Restart();
Bitmap bitmap = new Bitmap(grabResult.Width, grabResult.Height, PixelFormat.Format32bppRgb);
BitmapData bmpData = bitmap.LockBits(new Rectangle(0, 0, bitmap.Width, bitmap.Height), ImageLockMode.ReadWrite, bitmap.PixelFormat);
IntPtr ptrBmp = bmpData.Scan0;
converter.OutputPixelFormat = PixelType.BGRA8packed;
converter.Convert(ptrBmp, bmpData.Stride * bitmap.Height, grabResult);
bitmap.UnlockBits(bmpData);
Bitmap bitmapOld = pictureBox.Image as Bitmap;
pictureBox.Image = bitmap;
if (bitmapOld != null)
{
bitmapOld.Dispose();
}
}
}
}
catch (Exception exception)
{
ShowException(exception);
}
finally
{
e.DisposeGrabResultIfClone();
}
}
private void OnGrabStopped(Object sender, GrabStopEventArgs e)
{
if (InvokeRequired)
{
BeginInvoke(new EventHandler<GrabStopEventArgs>(OnGrabStopped), sender, e);
return;
}
stopWatch.Reset();
updateDeviceListTimer.Start();
EnableButtons(true, false);
if(e.Reason != GrabStopReason.UserRequest)
{
MessageBox.Show("A grab error occured:\n" + e.ErrorMessage, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
- 이미지 캡처 시작: 이미지 캡처가 시작될 때 타이머를 리셋하고 디바이스 리스트 업데이트를 중지합니다.
- 이미지 캡처 완료: 이미지가 캡처되었을 때 이미지를 처리하고, 최신 이미지만 표시합니다.
- 이미지 캡처 중지: 이미지 캡처가 중지될 때 타이머를 리셋하고 디바이스 리스트 업데이트를 다시 시작합니다.
e. 버튼 활성화/비활성화
private void EnableButtons(bool canGrab, bool canStop)
{
toolStripButtonContinuousShot.Enabled = canGrab;
toolStripButtonOneShot.Enabled = canGrab;
toolStripButtonStop.Enabled = canStop;
}
- 버튼 활성화/비활성화: 이미지 캡처 상태에 따라 버튼을 활성화하거나 비활성화합니다.
f. 카메라 파괴 및 예외 처리
private void DestroyCamera()
{
try
{
if (camera != null)
{
testImageControl.Parameter = null;
pixelFormatControl.Parameter = null;
widthSliderControl.Parameter = null;
heightSliderControl.Parameter = null;
gainSliderControl.Parameter = null;
exposureTimeSliderControl.Parameter = null;
}
}
catch (Exception exception)
{
ShowException(exception);
}
try
{
if (camera != null)
{
camera.Close();
camera.Dispose();
camera = null;
}
}
catch (Exception exception)
{
ShowException(exception);
}
}
- 카메라 파괴: 카메라 객체를 안전하게 파괴하고 예외를 처리합니다.
g. 단일 이미지 및 연속 이미지 캡처
private void OneShot()
{
try
{
camera.Parameters[PLCamera.AcquisitionMode].SetValue(PLCamera.AcquisitionMode.SingleFrame);
camera.StreamGrabber.Start(1, GrabStrategy.OneByOne, GrabLoop.ProvidedByStreamGrabber);
}
catch (Exception exception)
{
ShowException(exception);
}
}
private void ContinuousShot()
{
try
{
camera.Parameters[PLCamera.AcquisitionMode].SetValue(PLCamera.AcquisitionMode.Continuous);
camera.StreamGrabber.Start(GrabStrategy.OneByOne, GrabLoop.ProvidedByStreamGrabber);
}
catch (Exception exception)
{
ShowException(exception);
}
}
- 단일 이미지 캡처: 한 장의 이미지를 캡처합니다.
- 연속 이미지 캡처: 이미지를 연속으로 캡처합니다.
h. 디바이스 리스트 업데이트
private void UpdateDeviceList()
{
try
{
List<ICameraInfo> allCameras = CameraFinder.Enumerate();
ListView.ListViewItemCollection items = deviceListView.Items;
foreach (ICameraInfo cameraInfo in allCameras)
{
bool newitem = true;
foreach (ListViewItem item in items)
{
ICameraInfo tag = item.Tag as ICameraInfo;
if ( tag[CameraInfoKey.FullName] == cameraInfo[CameraInfoKey.FullName])
{
tag = cameraInfo;
newitem = false;
break;
}
}
if (newitem)
{
ListViewItem item = new ListViewItem(cameraInfo[CameraInfoKey.FriendlyName]);
string toolTipText = "";
foreach( KeyValuePair<string, string> kvp in cameraInfo)
{
toolTipText += kvp.Key + ": " + kvp.Value + "\n";
}
item.ToolTipText = toolTipText;
item.Tag = cameraInfo;
deviceListView.Items.Add(item);
}
}
foreach (ListViewItem item in items)
{
bool exists = false;
foreach (ICameraInfo cameraInfo in allCameras)
{
if (((ICameraInfo)item.Tag)[CameraInfoKey.FullName] == cameraInfo[CameraInfoKey.FullName])
{
exists = true;
break;
}
}
if (!exists)
{
deviceListView.Items.Remove(item);
}
}
}
catch (Exception exception)
{
ShowException(exception);
}
}
- 디바이스 리스트 업데이트: 사용 가능한 카메라 디바이스 목록을 갱신하고, 제거된 디바이스는 리스트에서 삭제합니다.
i. 예외 처리
private void ShowException(Exception exception)
{
MessageBox.Show("Exception caught:\n" + exception.Message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
- 예외 처리: 예외가 발생했을 때 메시지 박스를 통해 사용자에게 알립니다.
j. 폼 닫기 및 디바이스 선택 처리
private void MainForm_FormClosing(object sender, FormClosingEventArgs ev)
{
DestroyCamera();
}
private void deviceListView_SelectedIndexChanged(object sender, EventArgs ev)
{
if (camera != null)
{
DestroyCamera();
}
if (deviceListView.SelectedItems.Count > 0)
{
ListViewItem item = deviceListView.SelectedItems[0];
ICameraInfo selectedCamera = item.Tag as ICameraInfo;
try
{
camera = new Camera (selectedCamera);
camera.CameraOpened += Configuration.AcquireContinuous;
camera.ConnectionLost += OnConnectionLost;
camera.CameraOpened += OnCameraOpened;
camera.CameraClosed += OnCameraClosed;
camera.StreamGrabber.GrabStarted += OnGrabStarted;
camera.StreamGrabber.ImageGrabbed += OnImageGrabbed;
camera.StreamGrabber.GrabStopped += OnGrabStopped;
camera.Open();
testImageControl.Parameter = camera.Parameters[PLCamera.TestImageSelector];
pixelFormatControl.Parameter = camera.Parameters[PLCamera.PixelFormat];
widthSliderControl.Parameter = camera.Parameters[PLCamera.Width];
heightSliderControl.Parameter = camera.Parameters[PLCamera.Height];
if (camera.Parameters.Contains(PLCamera.GainAbs))
{
gainSliderControl.Parameter = camera.Parameters[PLCamera.GainAbs];
}
else
{
gainSliderControl.Parameter = camera.Parameters[PLCamera.Gain];
}
if (camera.Parameters.Contains(PLCamera.ExposureTimeAbs))
{
exposureTimeSliderControl.Parameter = camera.Parameters[PLCamera.ExposureTimeAbs];
}
else
{
exposureTimeSliderControl.Parameter = camera.Parameters[PLCamera.ExposureTime];
}
}
catch (Exception exception)
{
ShowException(exception);
}
}
}
- 폼 닫기: 폼이 닫힐 때 카메라 객체를 파괴합니다.
- 디바이스 선택: 새로운 카메라 디바이스가 선택되었을 때 현재 카메라 객체를 파괴하고 선택된 카메라와 연결을 엽니다.
k. 키 입력 및 타이머 콜백 처리
private void deviceListView_KeyDown(object sender, KeyEventArgs ev)
{
if (ev.KeyCode == Keys.F5)
{
ev.Handled = true;
UpdateDeviceList();
}
}
private void updateDeviceListTimer_Tick(object sender, EventArgs e)
{
UpdateDeviceList();
}
- 키 입력 처리: F5 키가 눌렸을 때 디바이스 리스트를 업데이트합니다.
- 타이머 콜백: 일정 시간마다 디바이스 리스트를 업데이트합니다.
MainForm 클래스는 Basler Pylon 카메라의 다양한 기능을 제어할 수 있는 메인 윈도우를 구현합니다. 이 클래스는 카메라 연결, 이미지 캡처, 설정 조정, 그리고 사용자 인터페이스 업데이트 등의 주요 기능을 담당합니다.
해시태그
#CSharp #BaslerPylon #윈도우폼 #비전코딩 #프로그래밍 #오픈소스 #GitHub #코딩팁 #개발자
728x90