카메라 에서 받은 프레임을 이미지 컨트롤에 넣으려는데...

Gige 카메라를 사용해 이미지 프레임을 받았습니다.
Winform 데모 프로그램에서는 정상적으로 프레임을 받아
뷰어에 표시 합니다.

그런데 wpf 이미지 컨트롤에 넣는데 나타나질 않네요

아래는 윈폼 데모 소스 코드
image

private void mCam1_OnFrameReceived(Image image)
{
if (InvokeRequired == true)
{
BeginInvoke(new ImageReceivedHandler(this.mCam1_OnFrameReceived), image);
return;
}

        m_PictureBox.Image = image;
        m_Frames_Cam1++;
        labelFramesCam1.Text = m_Frames_Cam1.ToString();

}

이 소스 형식을 그대로 wpf 로 옮겨 만들고 있습니다.
wpf 코드
image

private void Cam_OnFrameReceived(Image image)
{
Dispatcher.CurrentDispatcher.BeginInvoke(new Action(() =>
{
Dispatcher.BeginInvoke(new ImageReceivedHandler(this.Cam_OnFrameReceived), image);
return;
}));

        ImgCamView_Before[0].Source = ToImageSource(image, ImageFormat.Bmp);

}

System.Drawing.Image 를 System.Windows.Media.ImageSource 로 변환하는 함수
image

public static ImageSource ToImageSource(Image image, ImageFormat imageFormat)
{
using MemoryStream stream = new();

        // Save to the stream
        image.Save(stream, imageFormat);

        // Rewind the stream
        stream.Seek(0, SeekOrigin.Begin);

        // Tell the WPF BitmapImage to use this streamtreeze
        var BitmapImg = new BitmapImage();
        BitmapImg.BeginInit();
        BitmapImg.CacheOption = BitmapCacheOption.OnLoad;
        BitmapImg.StreamSource = stream;
        BitmapImg.EndInit();

        return BitmapImg;
    }

카메라에서 넘겨온 이미지도 정상이고
image

변환 함수에서 리턴도 정상같아요
image

그런데 왜 나타나질 않는건지 모르겠어요
image

private void Cam_OnFrameReceived(Image image)
{
    Dispatcher.CurrentDispatcher.BeginInvoke(new Action(() =>
    {
        Dispatcher.BeginInvoke(new ImageReceivedHandler(this.Cam_OnFrameReceived), image);
        return;
    }));

    ImgCamView_Before[0].Source = ToImageSource(image, ImageFormat.Bmp);
}

위 코드는 메인 스레드에서 이미지를 할당하는 것을 의도하신 것 같은데 첫 번째 BeginInvoke 부분에서 실수를 하신 것 같네요. 그래서 마지막 줄의 ImgCamView_Before[0].Source를 Set하는 코드가 메인 스레드와 카메라 프레임 수신 스레드에서 교차로 호출되고, 또 무한 반복되고 있는 듯 합니다.

함수의 첫 줄을 아래와 같이 수정해보세요.

private void Cam_OnFrameReceived(Image image)
{
    if (!Dispatcher.CheckAccess()) 
    {
        Dispatcher.BeginInvoke(new ImageReceivedHandler(this.Cam_OnFrameReceived), image);
        return;
    }

    ImgCamView_Before[0].Source = ToImageSource(image, ImageFormat.Bmp);
}

그리고 Cam_OnFrameReceived 함수로 넘어오는 Image 객체에 대해서 Dispose()를 호출하지 않아도 되는지 확인이 필요할 것 같습니다. 이전 코드에서 BeginInvoke로 호출해서 잘 동작했다면 Cam 라이브러리가 Bitmap 리소스를 Release 하지 않을 가능성이 큽니다. 메모리 누수가 없는지 확인해보시고 메모리가 계속 증가한다면 ToImageSource함수에서 image.Save 후 image 객체를 Dispose하세요.

4 Likes

바로 해결되었습니다! 감사합니다.
스레딩에 대한 이해와 활용이 부족한게 원인이였네요.

언급해주신 객체 디스포즈를 했음에도 cpu, 메모리 부하가 크고
대략 30초 이내에 이벤트가 죽어버리네요,
이 부분은 데모 코드를 잘 파악해서 수정해 수정해나아가야 겠습니다.

많은 도움과 배움이 되었습니다.
감사합니다!

MemoryStream을 이용한 BitmapSource 변환은 성능 이슈와 메모리 누수 이슈가 있다고 합니다. (메모리 누수는 솔루션이 있지만 테스트 해보지 않았습니다.)

https://www.google.com/search?q=bitmapimage+streamsource+memory+leak

관련 내용은 참고하시고 아래 코드를 사용해 보세요. 메모리 누수 없이 안정적으로 동작했던 코드입니다.

[DllImport("gdi32")]
private static extern int DeleteObject(IntPtr o);

public static BitmapSource ToBitmapSource(Bitmap bitmap)
{            
    var ptr = bitmap.GetHbitmap(); //obtain the Hbitmap
    try 
    {
        return System.Windows.Interop.Imaging.CreateBitmapSourceFromHBitmap(
            ptr,
            IntPtr.Zero,
            Int32Rect.Empty,
            BitmapSizeOptions.FromEmptyOptions());
    }
    finally
    {
        DeleteObject(ptr); //release the HBitmap

        bitmap.Dispose();
    }
}
3 Likes

추가 도움까지 아낌없이 주시네요!
바로 적용해서 비교해봤습니다.
아래와 같은 결과가 나왔는데요.
image

알려주신 함수형식이 메모리 부하를 낮춰주었으나
Cpu 부하가 올라가는 현상이 나타났습니다.

이는 완벽한 결과가 아니기에 카메라 라이브러리의 데모 코드를 손 보면
더욱 효과가 뚜렷해질 것 같습니다.

코드를 개선 해서 추가 결과로 보답해드리겠습니다
도움에 감사드립니다
즐거운 하루 되세요!

2 Likes

댓글에 많은 지식 쌓고 갑니다.

1 Like

BitmapImg.EndInit() 다음에 아래 함수 한번 추가해 보세요.

///////////////////////////////////////////////
BitmapImg.EndInit();
BitmapImg.Freeze(); //Important to freeze it, otherwise it will still have minor leaks
///////////////////////////////////////////////

2 Likes

도움 감사합니다! 적용해보고 계속 결과로 남겨둘게요! 즐거운 금요일 되세요!

1 Like