ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • C# RGB 출력, YUV420 -> RGB로 출력
    프로그래밍 언어/C# 2020. 9. 1. 16:17

    Raspberry Pi Zero W에 카메라 이미지를 PC에 전송하여 출력하는 것을 작업하고 있다.

     

    그런데.... Raspberry Pi 에서 큰 문제가 생겼다...

     

    자료를 찾아보니 대부분 OpenCV를 사용하여 데이터를 보내길래

     

    OpenCV를 깔아봤는데.... 'Illegal instruction'이라고 에러가 떠서 동작이 안된다.

     

    아무리 삽질을 해봐도 해결 방법을 못 찾았다ㅠㅠ;;

     

     

    그래서 다른 방법을 찾던 중, 라즈베리 기본 설치 프로그램 중에

     

    raspistill, raspivid, raspiyuv, raspividyuv를 사용해서 데이터를 보낼 방법을 찾았다.

     

     

     

    라즈베리에서 카메라 실행은

    raspividyuv -w 480 -h 320 -fps 5 -t 0 -l -o tcp://192.168.0.125:7777

    이 명령 사용

     

    YUV420 포멧으로 raw 데이터 보냄.

     

    그래서 C#에서 화면에 그려줄때

    480x320 사이즈로

    한 프레임당 230400 bytes 이다.

     

    0~153600 까지는 Y 값

    153600~192000 까지는 U 값

    192000~230400 까지 V 값

    으로 구성되어 있다.

     

    소스 ProcDrawingBuf() 함수에 38400 은 V값과 U 값 크기이다.

     

    곳곳에 마이크 녹음이랑 사운드 출력도 있다....

     

     

    --- MainWindow.xaml.cs

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;
    using System.Windows;
    using System.Windows.Controls;
    using System.Windows.Data;
    using System.Windows.Documents;
    using System.Windows.Input;
    using System.Windows.Media;
    using System.Windows.Media.Imaging;
    using System.Windows.Navigation;
    using System.Windows.Shapes;
    using System.Resources; // test.jpg
    using System.IO.Ports; // SerialPort
    using System.Windows.Threading; // DispatcherPriority
    using System.Threading; // Thread
    using NAudio.Wave;
    using System.Drawing; // Bitmap
    using System.IO;
    using System.Net.Sockets; // TcpClient
    using System.Net;
    using System.Drawing.Imaging;
    using System.Runtime.InteropServices;
    
    namespace YUVRGB_Ctrl
    {
        ///
        /// MainWindow.xaml에 대한 상호 작용 논리
        /// 
        public partial class MainWindow : System.Windows.Window
        {
            private SerialPort sp;  // COM 포트
            private bool portOpen;  // 포트 열림 여부
    
            private WaveIn sourceStream = null; // 녹음 핸들?
    
            private Thread t1; // 네트워크 스트림 thread
            private Thread t2; // 비트맵 thread
            private string destIP; // 원격지 IP
            private bool ipOpen; // 원격지 열기 닫기 여부
    
            private TcpClient tc; // 원격지 주소
            private NetworkStream stream; // 원격지 스트림
    
            private byte[] streamBuf = new byte[230400];
            private byte[] drawingBuf = new byte[230400];
    
            public MainWindow()
            {
                InitializeComponent();
    
                sp = null;
                portOpen = false;
                t1 = null;
                ipOpen = false;
    
                // 임시 이미지 그리기
                //BitmapImage drawImg = new BitmapImage(new Uri(@"C:\Users\root\Documents\Visual Studio 2017\Projects\KLabs RGControl\KLabs RGControl\test.jpg"));
                BitmapImage drawImg = new BitmapImage(new Uri("test.jpg", UriKind.Relative));
                imgTest.Source = drawImg;
                imgTest.Visibility = Visibility.Hidden;
    
                // 키보드 처리 인터럽트 추가
                this.KeyDown += new KeyEventHandler(FuncKeyDown);
            }
    
            // 키보드 처리
            private void FuncKeyDown(object sender, KeyEventArgs e)
            {
                switch(e.Key)
                {
                    case Key.Escape:
                        this.Close();
                        break;
                }
            }
    
            // 메인 윈도우창 이동
            private void Window_MouseLeftBtnDown(object sender, RoutedEventArgs e)
            {
                Cursor = Cursors.SizeAll;
                this.DragMove();
            }
    
            private void Window_MouseLeftBtnUp(object sender, RoutedEventArgs e)
            {
                Cursor = Cursors.Arrow;
            }
    
            // exit 버튼
            private void BtnExit_Click(object sender, RoutedEventArgs e)
            {
                this.Close();
            }
    
            // connect 버튼
            private void BtnConnect_Click(object sender, RoutedEventArgs e)
            {
                // 연결되어 있으면 기존 연결 끊기
                if (ipOpen == true)
                {
                    // 스트림과 TcpClient 객체 닫기
                    stream.Close();
                    tc.Close();
                    ipOpen = false;
                }
    
                Window1 infoWindown = new Window1();
                infoWindown.OnChildDataEvent += new Window1.OnChildDataHandler(GetDataFromChild);
                infoWindown.ShowDialog();
            }
    
            // 카메라, 센서 연결.
            private void GetDataFromChild(string destIP, string comPort)
            {
                // COM 포트 열기
                //OpenComport(comPort);
    
                // IP 카메라 연결
                OpenCam(destIP);
            }
    
            // COM 포트 열기
            private void OpenComport(string destCom)
            {
                Console.WriteLine(destCom);
                // COM 연결 상태 확인
                if(sp == null)
                {
                    try
                    {
                        // 1. SerialPort 클래스 객체 생성
                        sp = new SerialPort();
    
                        // 2. SerialPort 포트 셋팅 설정
                        sp.PortName = destCom;
                        sp.BaudRate = (int)115200;
                        sp.DataBits = 8;
                        sp.Parity = Parity.None;
                        sp.StopBits = StopBits.One;
                        sp.Handshake = Handshake.None;
                        sp.DataReceived += new SerialDataReceivedEventHandler(SerialPort_DataReceived);
    
                        // 3. 시리얼포트 오픈
                        sp.Open();
                        portOpen = true;
                    }
                    catch
                    {
                        MessageBox.Show("COM 포트 열기 에러");
                    }
                }
            }
    
            // COM 포트에서 읽은 데이터 처리
            public void SerialPort_DataReceived(object sender, SerialDataReceivedEventArgs e)
            {
                if ((portOpen == true) && (sp.BytesToRead >= 0))
                {
                    try
                    {
                        var stBulider = new StringBuilder();
                        stBulider.Append(sp.ReadLine());
                        //buf = sp.ReadLine();
                        if (stBulider[0] == '0')
                        {
                            stBulider.Append(sp.ReadLine());
                            stBulider.Append(sp.ReadLine());
                            stBulider.Append(sp.ReadLine());
                            stBulider.Append(sp.ReadLine());
                            /*
                            buf += sp.ReadLine();
                            buf += sp.ReadLine();
                            buf += sp.ReadLine();
                            buf += sp.ReadLine();
                            */
                            //buf = buf.Replace("\r", " ");
    
                            Dispatcher.Invoke(DispatcherPriority.Normal, new Action(delegate
                            {
                                labelSensorStatus.Content = stBulider.ToString();
                            }));
                            //this.labelSerial.Content = buf;
                        }
                    }
                    catch
                    { }
                }
            }
    
            // IP 카메라 연결
            private void OpenCam(string destIP2)
            {
                destIP = destIP2;
                Console.WriteLine(destIP2);
                imgTest.Visibility = Visibility.Visible;
                labelNetworkStatus.Visibility = Visibility.Hidden;
    
                // 카메라 데이터 스레드 처리
                /*
                if(t1 == null)
                {
                    t1.Abort();
                }
                */
                int aTmp;
    
                t1 = new Thread(new ThreadStart(ProcCamData));
                t1.Start();
            }
    
            // IP 카메라 데이터 처리
            private void ProcCamData()
            {
                byte[] outbuf = new byte[15000];
                int nbytes;
                int total;
                int i, f;
                string output;
                string sTime = System.DateTime.Now.ToString("mm:ss.ffffff");
                TcpClient tc = new TcpClient("192.168.0.125", 7777);
                NetworkStream stream = tc.GetStream();
    
                total = 0;
                i = 0;
                f = 0;
                while(true)
                {
                    nbytes = stream.Read(outbuf, 0, outbuf.Length);
                    // 한 프레임(230400) 처리
                    if (total + nbytes >= 230400)
                    {
                        Array.Copy(outbuf, 0, streamBuf, total, 230400 - total);
                        Array.Copy(streamBuf, drawingBuf, 230400);
                        Array.Copy(outbuf, 230400 - total, streamBuf, 0, nbytes - (230400 - total));
                        if(t2 == null)
                        {
                            t2 = new Thread(new ThreadStart(ProcDrawingBuf));
                            t2.Start();
                        }
                        else if ((t2.ThreadState & ThreadState.Stopped) == ThreadState.Stopped)
                        {
                            t2 = new Thread(new ThreadStart(ProcDrawingBuf));
                            t2.Start();
                        }
                        total = nbytes - (230400 - total);
                        f++;
                    }
                    else
                    {
                        Array.Copy(outbuf, 0, streamBuf, total, nbytes);
                        total += nbytes;
                    }
                    i++;
                    //output = Encoding.ASCII.GetString(outbuf, 0, nbytes);
                    //Console.WriteLine($"{i} : {nbytes} ");// bytes: {output}");
    
                    if (nbytes == 0)
                    {
                        break;
                    }
                }
                Console.WriteLine($"total : {i} {f}");
                Console.WriteLine($"Start time : {sTime}");
                Console.WriteLine($"End time : {System.DateTime.Now.ToString("mm:ss.ffffff")}");
    
                stream.Close();
                tc.Close();
            }
    
            private void ProcDrawingBuf()
            {
                Bitmap bitmap = new Bitmap(480, 320);
                BitmapData bmpData = bitmap.LockBits(new System.Drawing.Rectangle(0, 0, 480, 320), ImageLockMode.WriteOnly, System.Drawing.Imaging.PixelFormat.Format24bppRgb);
                int k, uv;
                int bY, bU, bV;
    
                k = 0;
                uv = 0;
                unsafe
                {
                    byte* ptr = (byte*)bmpData.Scan0.ToPointer();
                    for (int i = 0; i < 320; i++)
                    {
                        for (int j = 0; j < 480; j++)
                        {
                            if (uv >= 38400)
                            {
                                break;
                            }
                            bU = drawingBuf[320 * 480 + uv] - 128;
                            bV = drawingBuf[320 * 480 + 38400 + uv] - 128;
                            bY = drawingBuf[k];
    
                            // R
                            if (bV < 0)
                                ptr[bmpData.Stride * i + 3 * j + 2] = (byte)bY;
                            else
                                ptr[bmpData.Stride * i + 3 * j + 2] = (byte)(bY + (2.0790 * bV));
    
                            // G
                            if (bU < 0)
                                ptr[bmpData.Stride * i + 3 * j + 0] = (byte)bY;
                            else
                                ptr[bmpData.Stride * i + 3 * j + 0] = (byte)(bY + (1.4075 * bU));
    
                            // B
                            ptr[bmpData.Stride * i + 3 * j + 1] = (byte)(bY - (0.237633 * bV) - (0.337633 * bU));
    
                            k++;
                            if (j % 2 == 0)
                            {
                                uv++;
                            }
                        }
                        if (i % 2 == 0)
                        {
                            uv -= 240;
                        }
                    }
                    bitmap.UnlockBits(bmpData);
                }
    
                Dispatcher.Invoke(DispatcherPriority.Normal, new Action(delegate
                {
                    imgTest.Source = BitmapToImageSource(bitmap);
                }));
                bitmap.Dispose();
            }
    
            public Bitmap ScreenCapture()
            {
                // 주화면의 크기 정보 읽기
                int width = (int)SystemParameters.PrimaryScreenWidth;
                int height = (int)SystemParameters.PrimaryScreenHeight;
                Bitmap scrbmp = new Bitmap(width, height);
    
                using (Graphics g = Graphics.FromImage(scrbmp))
                {
                    g.CopyFromScreen(0, 0, 0, 0, scrbmp.Size, CopyPixelOperation.SourceCopy);
                }
    
                // Image에 캡처한 이미지를 뿌려주기 위해 Bitmap을 BitmapImage로 변환한다.
                using (MemoryStream memory = new MemoryStream())
                {
                    scrbmp.Save(memory, ImageFormat.Bmp);
                    memory.Position = 0;
                    BitmapImage bitmapimage = new BitmapImage();
                    bitmapimage.BeginInit();
                    bitmapimage.StreamSource = memory;
                    bitmapimage.CacheOption = BitmapCacheOption.OnLoad;
                    bitmapimage.EndInit();
                    imgTest.Source = bitmapimage;
                }
    
                return scrbmp;
            }
    
            BitmapImage BitmapToImageSource(Bitmap bitmap)
            {
                using (MemoryStream memory = new MemoryStream())
                {
                    bitmap.Save(memory, System.Drawing.Imaging.ImageFormat.Bmp);
                    memory.Position = 0;
                    BitmapImage bitmapimage = new BitmapImage();
                    bitmapimage.BeginInit();
                    bitmapimage.StreamSource = memory;
                    bitmapimage.CacheOption = BitmapCacheOption.OnLoad;
                    bitmapimage.EndInit();
    
                    return bitmapimage;
                }
            }
    
            // 마이크 녹음 초기화
            public void RecodeMic()
            {
                sourceStream = new NAudio.Wave.WaveIn();
                sourceStream.BufferMilliseconds = 50;
                sourceStream.DeviceNumber = 0;
                sourceStream.WaveFormat = new NAudio.Wave.WaveFormat(4800, NAudio.Wave.WaveIn.GetCapabilities(0).Channels);
                sourceStream.DataAvailable += new EventHandler(sourceStream_DataAvailable);
                sourceStream.StartRecording();
            }
    
            // 마이크 녹음 데이터 처리
            private void sourceStream_DataAvailable(object sender, NAudio.Wave.WaveInEventArgs e)
            {
                byte[] encoded = e.Buffer;
                int i;
                float multiplier = 12.0f;   // Gain
    
                for (i = 0; i < encoded.Length; i = i + 2)
                {
                    Int16 sample = BitConverter.ToInt16(encoded, i);
                    sample = (Int16)(sample * multiplier);
                    byte[] sampleBytes = BitConverter.GetBytes(sample);
                    encoded[i] = sampleBytes[0];
                    encoded[i + 1] = sampleBytes[1];
                }
    
                //encoded.CopyTo(recordingStream2, encoded.Length);
            }
        }
    }
    

     

     

    --- 실행화면

       - Raspberry pi zero w

     

       - PC

     

    '프로그래밍 언어 > C#' 카테고리의 다른 글

    C# 이미지 그리기, 출력  (0) 2020.08.21
    C# NAudio MicroPhone 볼륨 조절  (0) 2020.08.20
Designed by Tistory.