본문 바로가기

C#

직렬화된 배열을 비트맵 스타일로 배열하기 , 리사이징 하기

1) 직렬화된 배열을 비트맵 스타일로 배열하기 , 

2) 직렬화된 배열을 비트맵 스타일로 리사이징 하기


위의 요건이 이번 프로젝트 에 필요하게 되었다.


상황은 키넥트에서 받는 DepthFrame 을 이용하여 유니티에서 1:1 로 파티클을 매칭 한다.

이때 파티클은 1개의 매쉬 에 동적으로 그려진 버텍스 들이다. 


그리고 버택스 개수는 약 6만5천개로 제한적이다.


그리고 일정 개수 이상 넘어 가고, 이것들을 루프에 돌릴때, 메인쓰레드의 FPS 가 떨어지는 불상사가 일어난다.


그래서 품질과 퍼포먼서의 적정 수준인 160 x 120 해상도의 DepthFrame 이 필요한데, 안타깝게도 키넥트에서는 저 해상도를 지원하지 않는다.


그러나 비트맵으로 변경하여 리사이징 하면 간편하다. 하지만, 이렇게 할 경우 이미징 과정에서 생기는 노이즈와 Depth 단계를 256 단계 밖에 못 쓰므로 풍성한 양감을 얻기 힘들다. 


따라서 DepthFrame[] 을 리사이징 해야 한다. 


방법은 각 배열을 비트맵으로 생각한다음 지정된 가로 사이즈 만큼 나누어 줄을 내린다.


그다음 그 비트맵의 상하좌우 4칸을 잡은 다음 좌측 상단 픽셀만 따로 골라 별도의 배열에 담는다.




(회색부분을 전체 비트맵 이라 생각 했을때 빨간색 픽셀만 골라 낸다) 

 

그러면 계산 상 1/4 배로 사이즈가 줄어 들게 된다.


이 일련의 과정을 간단한 코드로 재현 해 보았다. 


다방면으로 이용 할 수 있는 방법인듯 하다.

using System;
using System.Collections.Generic;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Text;

namespace DepthBitmapFromKinectDepth
{
    class Program
    {
        static int bmpWidth = 320;
        static int bmpHeight = 240;
        static void Main(string[] args)
        {
            Program p = new Program();
        }

        public Program()
        {
            DateTime dt = DateTime.Now;
            //ReadDepthToImage(); - ok
            //ReadDepthFrameToImage(ParseDepthFrame()); - ok
            ResizeQuater();

            Console.WriteLine("process time (ms) > " + (DateTime.Now - dt).Milliseconds);
            Console.ReadKey();
        }
        




        // 사이즈 변경
        // 320 x 240 -> 160 x 120
        private void ResizeQuater()
        {
            var depthframes = ParseDepthFrame();
            var resizeFrmaes = new DATA.DepthFrame[depthframes.Length/4];
            int x = 0, y = 0 , c = 0;
            for (int i = 0; i < depthframes.Length; i++)
            {
                if (i % bmpWidth == 0 && i > 0)
                {
                    ++y;
                    x = 0;
                }

                if (x % 2 == 0)
                {
                    if (y % 2 == 0)
                    {
                        resizeFrmaes[c] = depthframes[i];
                        ++c;
                    }
                }
                ++x;
            }

            Console.WriteLine(x + " " + y + " " + c);
            ReadDepthFrameToImage(resizeFrmaes, new Size(160, 120));
        }





        /// <summary>
        /// 유틸 메서드들 
        /// </summary>
        private byte[] ReadDepthDatFile()
        {
            // 키넥트에서 320 x 240 사이즈의 Depth 이미지의 뎁스 와 플레이어 인덱스를 별토 파일로 저장함.
            // 즉 , 1묶음이 3바이트로 구성되어 있는데 ,[byte : 플레이어 인덱스] [[byte],[] : Short 형 Depth 값 ] 이다. 
            return File.ReadAllBytes(Environment.CurrentDirectory + "/DepthData.dat");
        }

        private DATA.DepthFrame[] ParseDepthFrame()
        {
            byte[] _org_data = ReadDepthDatFile();
            DATA.DepthFrame[] depthFrames = new DATA.DepthFrame[_org_data.Length / 3];

            Console.WriteLine("parse depthFrames : " + depthFrames.Length);

            for (int i = 0; i < _org_data.Length / 3; i++)
            {
                byte playerIndex = _org_data[(3 * i) + 0];
                short depth = BitConverter.ToInt16(_org_data, (3 * i) + 1);

                depthFrames[i] = new DATA.DepthFrame() { PlayerIndex = playerIndex, Depth = depth };
            }

            return depthFrames;
        }

        private void ReadDepthFrameToImage(DATA.DepthFrame[] depthFrames, Size bmpSize)
        {
            int x, y;
            Color[] colors = new Color[bmpSize.Width * bmpSize.Height];
            x = 0;
            y = 0;

            for (int i = 0; i < depthFrames.Length; i++)
            {
                short depth = depthFrames[i].Depth;
                byte depthToByte = (byte)depth;
                colors[i] = Color.FromArgb(depthToByte, depthToByte, depthToByte);

                if (i % bmpSize.Width == 0 && i > 0)
                {
                    ++y;
                    x = 0;
                }
                ++x;
            }

            WriteBitmap(colors, bmpSize, "save2.jpg");
        }

        private void WriteBitmap(Color[] colors, Size bmpSize, string fileName)
        {
            int x, y;
            Bitmap bmp = new Bitmap(bmpSize.Width, bmpSize.Height);
            x = 0;
            y = 0;
            for (int i = 0; i < colors.Length; i++)
            {
                if (i % bmpSize.Width == 0 && i > 0)
                {
                    ++y;
                    x = 0;
                }

                bmp.SetPixel(x, y, colors[i]);
                ++x;
            }
            bmp.Save(Environment.CurrentDirectory + "/" + fileName); // -ok
            Console.WriteLine("write complete");
        }






        /// <summary>
        /// 기본 테스트
        /// </summary>
        private void ReadDepthToImage_Pure()
        {
            int min = int.MaxValue;
            int max = int.MinValue;

            byte[] _org_data = ReadDepthDatFile();

            Color[] colors = new Color[bmpWidth * bmpHeight];
            int x, y;

            //to Depth Image
            x = 0;
            y = 0;
            for (int i = 0; i < _org_data.Length / 3; i++)
            {
                byte playerIndex = _org_data[(3 * i) + 0];
                short depth = BitConverter.ToInt16(_org_data, (3 * i) + 1);

                if (min > depth)
                {
                    min = depth;
                }

                if (max < depth)
                {
                    max = depth;
                }

                byte depthToByte = (byte)depth;
                colors[i] = Color.FromArgb(depthToByte, depthToByte, depthToByte);

                if (i % bmpWidth == 0 && i > 0)
                {
                    ++y;
                    x = 0;
                }
                ++x;
            }

            // 숫자 검증
            Console.WriteLine("min : " + min);
            Console.WriteLine("max : " + max);
            Console.WriteLine("Color len : " + colors.Length);

            Bitmap bmp = new Bitmap(320, 240);
            x = 0;
            y = 0;
            for (int i = 0; i < colors.Length; i++)
            {

                if (i % bmpWidth == 0 && i > 0)
                {
                    ++y;
                    x = 0;
                    Console.WriteLine("Reteral");
                }

                bmp.SetPixel(x, y, colors[i]);

                ++x;
            }
            bmp.Save(Environment.CurrentDirectory + "/Save.jpg"); // -ok 320 x 240
        }
    }

    public class DATA
    {
        public struct DepthFrame
        {
            public byte PlayerIndex;
            public short Depth;
        }
    }
}