-
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