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๋‚˜ ์ด๋ฏธ์ง€ ํ˜•์‹์ด ์ž˜๋ชป๋œ ๊ฒƒ์ฒ˜๋Ÿผ ์ถœ๋ ฅ๋ฉ๋‹ˆ๋‹ค.
image

//            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 ๋ฐฉ์‹์œผ๋กœ ํ•  ๊ฒฝ์šฐ, ์•„๋ž˜์˜ ์ด๋ฏธ์ง€์™€ ๊ฐ™์ด ์ •์ƒ์ ์œผ๋กœ ๋‚˜์˜ต๋‹ˆ๋‹ค.
image

์œ„์˜ CopyMemory ๋ฐฉ์‹ ์ดํ›„, SwitchBuffer ๋ฐฉ์‹์ด BitmapSource.Create ๋ฐฉ์‹๊ณผ ๋‹ค๋ฅด๊ฒŒ ์ด๋ฏธ์ง€๊ฐ€ ์ด์ƒํ•˜๊ฒŒ ๋‚˜์˜ค๋Š” ์ด์œ ๋ฅผ ๋ชจ๋ฅด๊ฒ ์Šต๋‹ˆ๋‹คโ€ฆ ใ… ใ… โ€ฆ

์ด๊ฒƒ ์ €๊ฒƒ ํ™•์ธํ•ด๋ณธ๋‹ค๊ณ , ๋ฉ”๋ชจ๋ฆฌ ์ฃผ์†Œ์— ์žˆ๋Š” ๊ฐ’๋„ ์ „๋ถ€ ๊ธ์–ด์™€์„œ ๋น„๊ตํ•ด๋ดค๋Š”๋ฐ ๋˜‘๊ฐ™์€ ๊ฒƒ์„ ํ™•์ธํ–ˆ์Šต๋‹ˆ๋‹ค.

์†๋„๊ฐ€ ์ค‘์š”ํ•ด์„œ ๊ทธ๋Ÿฐ๋ฐ SwitchBuffer ๋ฐฉ์‹์„ ์‚ฌ์šฉํ–ˆ์„ ๋•Œ ์ด๋ฏธ์ง€๊ฐ€ ์ •์ƒ์ ์œผ๋กœ ์ถœ๋ ฅ๋  ์ˆ˜ ์žˆ๋Š” ๋ฐฉ๋ฒ•์„ ์•Œ์ˆ˜์žˆ์„๊นŒ์š”โ€ฆ

์ž…๋ ฅ๋˜๋Š” ์ด๋ฏธ์ง€์˜ ๊ฐ€๋กœ ์„ธ๋กœ ํฌ๊ธฐ๊ฐ€ ์–ด๋–ป๊ฒŒ ๋˜๋‚˜์š”?

๋ง์”€ํ•˜์‹  Stride์˜ ๋ฌธ์ œ๋กœ ๋ณด์ด๋ฉฐ Width * 2 ํ•œ ๊ฐ’์ด 4์˜ ๋ฐฐ์ˆ˜๊ฐ€ ์•„๋‹ˆ๋ผ์„œ ๋ฐœ์ƒํ•˜๋Š” ๋ฌธ์ œ๊ฐ€ ์•„๋‹Œ์ง€ ์ถ”์ธกํ•ด๋ด…๋‹ˆ๋‹ค.

frame ๋ณ€์ˆ˜๋กœ ๋„˜์–ด์˜ค๋Š” ๋ฐ์ดํ„ฐ๋ฅผ ๋ฐ”์ด๋„ˆ๋ฆฌ๋กœ ์ €์žฅํ•ด์„œ ์ฒจ๋ถ€ํ•ด ์ฃผ์‹œ๋ฉด ๋„์›€์ด ๋  ๊ฒƒ ๊ฐ™๋„ค์š”.

2๊ฐœ์˜ ์ข‹์•„์š”

Width๊ฐ€ ํ™€์ˆ˜์˜€๋Š”๋ฐ, WriteableBitmap์€ ์ž๋™์œผ๋กœ ๋ณ€ํ™˜์ด ์•ˆ๋˜๋‚˜๋ด…๋‹ˆ๋‹ค!
WriteableBitmap ์‚ฌ์ด์ฆˆ์— ํŒจ๋”ฉ ์ค˜์„œ ์ง์ˆ˜๋กœ ๋งŒ๋“ค๊ณ  CopyMemory ํ•ด์ฃผ๋‹ˆ ์ž˜ ๋‚˜์˜ต๋‹ˆ๋‹ค!
๊ฐ์‚ฌํ•ฉ๋‹ˆ๋‹ค.

๊ฐ์ฒด ๋ฉค๋ฒ„ ๋ณ€์ˆ˜์ƒ์œผ๋กœ Stride๋Š” ์ •์ƒ์ ์œผ๋กœ ๋‚˜์™€์„œ ๋ฌธ์ œ๊ฐ€ ์—†๋Š”์ค„ ์•Œ์•˜๋Š”๋ฐ ์†์•˜์Šต๋‹ˆ๋‹ค ใ… ใ… โ€ฆ

2๊ฐœ์˜ ์ข‹์•„์š”