WPF 16Bit Gray Image 출력

카메라로부터 16비트 이미지 버퍼를 받아서 WPF의 ImageSource로 이미지를 출력하는 중입니다.

이런 글을 쓰는게 익숙하지 않아서… 작성이 이쁘지 못한점 죄송합니다.

속도 때문에 해당 링크와 같게 작성했습니다.

rtsp 카메라 영상 이미지로 출력 - :desktop_computer: UI/UX 프로그래밍 Q&A / WPF Q&A - 닷넷데브 (dotnetdev.kr)

DisplayImage(ushort[] frame, ushort width, ushort height)
{
            System.Windows.Application.Current.Dispatcher.BeginInvoke(new Action(() => AllocImgBuf(width, height)), DispatcherPriority.Normal);

            WriteableBitmap buffer = null;
            ushort pixelValue = 0;
            lock (_bufferLock)
            {
                if (_lastWritten == _bufferPtr) // 이전 버퍼가 아직 사용되지 않았다면 스킵
                {
                    return;
                }
                //고정된 배열로부터 포인터 얻기
                GCHandle handle = GCHandle.Alloc(frame, GCHandleType.Pinned);
                IntPtr pPixelData = handle.AddrOfPinnedObject();

                CopyMemory(_bufferPtr, pPixelData, (uint)((uint)width * (uint)height * 2));

                _lastWritten = _bufferPtr;
                buffer = _freeBuffer;
                handle.Free();
            }
            System.Windows.Application.Current.Dispatcher.BeginInvoke(new Action(() => SwitchBuffer(buffer)), DispatcherPriority.Render);
}
        private void AllocImgBuf(int width, int height)
        {
            if (_buffer1 == null || ((width * height) != (_buffer1.Width * _buffer1.Height)))
            {
                _buffer1 = new WriteableBitmap(width, height, 96, 96, PixelFormats.Gray16, null);
                _buffer2 = new WriteableBitmap(width, height, 96, 96, PixelFormats.Gray16, null);
                _freeBuffer = _buffer2;
                _freeBuffer.Lock();
                _bufferPtr = _freeBuffer.BackBuffer;
            }
        }
        private void SwitchBuffer(WriteableBitmap buffer)
        {
            // CountFPS();
            ushort pixelValue = 0;
            lock (_bufferLock)
            {
                _freeBuffer = buffer == _buffer2 ? _buffer1 : _buffer2;
                _freeBuffer.Lock();
                _bufferPtr = _freeBuffer.BackBuffer;
            }

            buffer.AddDirtyRect(new System.Windows.Int32Rect(0, 0, buffer.PixelWidth, buffer.PixelHeight));
            buffer.Unlock();

            Image = buffer;
        }

Image는 ObservableProperty로 Binding 되어있습니다.

위의 코드로 프로그램을 실행할 경우, 아래의 이미지와 같이 Stride나 이미지 형식이 잘못된 것처럼 출력됩니다.

//            System.Windows.Application.Current.Dispatcher.BeginInvoke(new Action(() => SwitchBuffer(buffer)), DispatcherPriority.Render);
//위의 코드 대신에
            System.Windows.Application.Current.Dispatcher.BeginInvoke((Action)(() =>
            {
                if (frame is not null && frame.Length != 0)
                    Image = BitmapSource.Create(width, height, 96, 96, PixelFormats.Gray16, null, frame, width * 2);
                else
                    Image = null;
            }));

BackBuffer를 Copy하는 방식이 아닌 BitmapSource.Create 방식으로 할 경우, 아래의 이미지와 같이 정상적으로 나옵니다.

위의 CopyMemory 방식 이후, SwitchBuffer 방식이 BitmapSource.Create 방식과 다르게 이미지가 이상하게 나오는 이유를 모르겠습니다… ㅠㅠ…

이것 저것 확인해본다고, 메모리 주소에 있는 값도 전부 긁어와서 비교해봤는데 똑같은 것을 확인했습니다.

속도가 중요해서 그런데 SwitchBuffer 방식을 사용했을 때 이미지가 정상적으로 출력될 수 있는 방법을 알수있을까요…

입력되는 이미지의 가로 세로 크기가 어떻게 되나요?

말씀하신 Stride의 문제로 보이며 Width * 2 한 값이 4의 배수가 아니라서 발생하는 문제가 아닌지 추측해봅니다.

frame 변수로 넘어오는 데이터를 바이너리로 저장해서 첨부해 주시면 도움이 될 것 같네요.

2개의 좋아요

Width가 홀수였는데, WriteableBitmap은 자동으로 변환이 안되나봅니다!
WriteableBitmap 사이즈에 패딩 줘서 짝수로 만들고 CopyMemory 해주니 잘 나옵니다!
감사합니다.

객체 멤버 변수상으로 Stride는 정상적으로 나와서 문제가 없는줄 알았는데 속았습니다 ㅠㅠ…

2개의 좋아요