첨부 소스 코드는 나눔고딕코딩 폰트를 사용합니다.
728x90
반응형
728x170

TestProject.zip
다운로드
TestProject.z01
다운로드
TestProject.z02
다운로드
TestProject.z03
다운로드
TestProject.z04
다운로드
TestProject.z05
다운로드
TestProject.z06
다운로드

▶ MainForm.cs

using System;
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;
using System.Windows.Forms;

using AForge.Video.DirectShow;

using TensorFlow;

namespace TestProject
{
    /// <summary>
    /// 메인 폼
    /// </summary>
    public partial class MainForm : Form
    {
        //////////////////////////////////////////////////////////////////////////////////////////////////// Field
        ////////////////////////////////////////////////////////////////////////////////////////// Private

        #region Field

        /// <summary>
        /// 타이머
        /// </summary>
        private Timer timer = null;

        /// <summary>
        /// 필터 정보 컬렉션
        /// </summary>
        private FilterInfoCollection filterInfoCollection = null;


        /// <summary>
        /// 모델 배열
        /// </summary>
        private byte[] modelArray;

        /// <summary>
        /// 레이블 배열
        /// </summary>
        private string[] labelArray = null;

        /// <summary>
        /// 텐서플로우 세션
        /// </summary>
        private TFSession session = null;

        /// <summary>
        /// 텐서플로우 그래프
        /// </summary>
        private TFGraph graph = null;

        #endregion

        //////////////////////////////////////////////////////////////////////////////////////////////////// Constructor
        ////////////////////////////////////////////////////////////////////////////////////////// Public

        #region 생성자 - MainForm()

        /// <summary>
        /// 생성자
        /// </summary>
        public MainForm()
        {
            InitializeComponent();

            #region 타이머를 설정한다.

            this.timer = new Timer();

            this.timer.Interval = 1000;

            #endregion
            #region 이벤트를 설정한다.

            Load += Form_Load;
            FormClosing            += Form_FormClosing;
            this.startButton.Click += startButton_Click;
            this.timer.Tick        += timer_Tick;

            #endregion
        }

        #endregion

        //////////////////////////////////////////////////////////////////////////////////////////////////// Method
        ////////////////////////////////////////////////////////////////////////////////////////// Private
        //////////////////////////////////////////////////////////////////////////////// Event

        #region 폼 로드시 처리하기 - Form_Load(sender, e)

        /// <summary>
        /// 폼 로드시 처리하기
        /// </summary>
        /// <param name="sender">이벤트 발생자</param>
        /// <param name="e">이벤트 인자</param>
        private void Form_Load(object sender, EventArgs e)
        {
            #region 카메라 장치 콤보 박스를 설정한다.

            this.filterInfoCollection = new FilterInfoCollection(FilterCategory.VideoInputDevice);

            for(int i = 0; i < this.filterInfoCollection.Count; i++)
            {
                this.cameraDeviceComboBox.Items.Add(this.filterInfoCollection[i].Name);
            }

            if(this.cameraDeviceComboBox.Items.Count > 0)
            {
                this.cameraDeviceComboBox.SelectedIndex = 0;
            }

            #endregion
            #region 시작 버튼을 설정한다.

            this.startButton.Enabled = (this.cameraDeviceComboBox.Items.Count > 0);

            #endregion

            #region 모델 배열을 설정한다.

            this.modelArray = File.ReadAllBytes("DATA\\tensorflow_inception_graph.pb");

            #endregion
            #region 레이블 배열을 설정한다.

            this.labelArray = File.ReadAllLines("DATA\\imagenet_comp_graph_label_strings.txt");

            #endregion


            this.graph   = new TFGraph();
            this.session = new TFSession(graph);

            this.graph.Import(this.modelArray, "");
        }

        #endregion
        #region 폼 닫을 경우 처리하기 - Form_FormClosing(sender, e)

        /// <summary>
        /// 폼 닫을 경우 처리하기
        /// </summary>
        /// <param name="sender">이벤트 발생자</param>
        /// <param name="e">이벤트 인자</param>
        private void Form_FormClosing(object sender, FormClosingEventArgs e)
        {
            this.timer.Stop();

            StopCameraCaoture();
        }

        #endregion
        #region 시작 버튼 클릭시 처리하기 - startButton_Click(sender, e)

        /// <summary>
        /// 시작 버튼 클릭시 처리하기
        /// </summary>
        /// <param name="sender">이벤트 발생자</param>
        /// <param name="e">이벤트 인자</param>
        private void startButton_Click(object sender, EventArgs e)
        {
            if(this.startButton.Text == "시작")
            {
                this.startButton.Text = "중지";

                StartCameraCapture();

                this.timer.Start();
            }
            else
            {
                this.timer.Stop();

                StopCameraCaoture();

                this.startButton.Text = "시작";
            }
        }

        #endregion
        #region 타이머 틱 처리하기 - timer_Tick(sender, e)

        /// <summary>
        /// 타이머 틱 처리하기
        /// </summary>
        /// <param name="sender">이벤트 발생자</param>
        /// <param name="e">이벤트 인자</param>
        private void timer_Tick(object sender, EventArgs e)
        {
            Bitmap bitmap = this.videoSourcePlayer.GetCurrentVideoFrame();

            if(bitmap == null)
            {
                return;
            }

            MemoryStream memoryStream = new MemoryStream();

            bitmap.Save(memoryStream, ImageFormat.Jpeg);

            TFTensor tensor = GetTensor(memoryStream.GetBuffer());

            TFSession.Runner runner = this.session.GetRunner();

            runner.AddInput(graph["input"][0], tensor).Fetch(graph["output"][0]);

            TFTensor[] outputTensorArray = runner.Run();

            TFTensor outputTensor = outputTensorArray[0];

            long[] shapeArray = outputTensor.Shape;

            if(outputTensor.NumDims != 2 || shapeArray[0] != 1)
            {
                string shapeString = string.Empty;

                foreach(long shape in shapeArray)
                {
                    shapeString += $"{shape} ";
                }

                shapeString = shapeString.Trim();

                Console.WriteLine($"에러 : [1 N] 차원 텐서의 생성을 기대했는데(여기서 N은 라벨의 개수), [{shapeString}] 차원 텐서를 생성했습니다.");

                Environment.Exit(1);
            }

            bool  jagged    = true;
            int   bestIndex = 0;
            float best      = 0;

            if(jagged)
            {
                float[] probabilityArray = ((float[][])outputTensor.GetValue(jagged : true))[0];

                for(int i = 0; i < probabilityArray.Length; i++)
                {
                    if(probabilityArray[i] > best)
                    {
                        bestIndex = i;

                        best = probabilityArray[i];
                    }
                }
            }
            else
            {
                float[,] valueArray = (float[,])outputTensor.GetValue(jagged : false);

                for(int i = 0; i < valueArray.GetLength(1); i++)
                {
                    if(valueArray[0, i] > best)
                    {
                        bestIndex = i;

                        best = valueArray[0, i];
                    }
                }
            }

            this.informationLabel.Text = $"판정 : [{bestIndex}] {best * 100.0}% {this.labelArray[bestIndex]}";

            Console.WriteLine($"판정 : [{bestIndex}] {best * 100.0}% {this.labelArray[bestIndex]}");
        }

        #endregion

        //////////////////////////////////////////////////////////////////////////////// Function

        #region 카메라 캡처 시작하기 - StartCameraCapture()

        /// <summary>
        /// 카메라 캡처 시작하기
        /// </summary>
        private void StartCameraCapture()
        {
            FilterInfo filterInfo;

            filterInfo = this.filterInfoCollection[this.cameraDeviceComboBox.SelectedIndex];

            VideoCaptureDevice videoCaptureDevice = new VideoCaptureDevice(filterInfo.MonikerString);

            this.videoSourcePlayer.VideoSource = videoCaptureDevice;

            this.videoSourcePlayer.AutoSizeControl = true;

            this.videoSourcePlayer.Start();
        }

        #endregion
        #region 카메라 캡처 중단하기 - StopCameraCaoture()

        /// <summary>
        /// 카메라 캡처 중단하기
        /// </summary>
        private void StopCameraCaoture()
        {
            this.videoSourcePlayer.SignalToStop();

            this.videoSourcePlayer.WaitForStop();
        }

        #endregion

        #region 값 설정하기 - SetValue(graph, input, output)

        /// <summary>
        /// 값 설정하기
        /// </summary>
        /// <param name="graph">그래프</param>
        /// <param name="input">입력</param>
        /// <param name="output">출력</param>
        private void SetValue(out TFGraph graph, out TFOutput input, out TFOutput output)
        {
            const int   IMAGE_WIDTH  = 224;
            const int   IMAGE_HEIGHT = 224;
            const float MEAN         = 117;
            const float SCALE        = 1;

            graph = new TFGraph();
            input = graph.Placeholder(TFDataType.String);

            output = graph.Div
            (
                x : graph.Sub
                (
                    x : graph.ResizeBilinear
                    (
                        images : graph.ExpandDims
                        (
                            input : graph.Cast
                            (
                                graph.DecodeJpeg(contents : input, channels : 3),
                                DstT : TFDataType.Float
                            ),
                            dim : graph.Const(0, "make_batch")
                        ),
                        size : graph.Const(new int[] { IMAGE_WIDTH, IMAGE_HEIGHT }, "size")),
                    y : graph.Const(MEAN, "mean")
                ),
                y : graph.Const(SCALE, "scale")
            );
        }

        #endregion
        #region 텐서 구하기 - GetTensor(sourceArray)

        /// <summary>
        /// 텐서 구하기
        /// </summary>
        /// <param name="sourceArray">소스 배열</param>
        /// <returns>텐서</returns>
        private TFTensor GetTensor(byte[] sourceArray)
        {
            TFTensor tensor = TFTensor.CreateString(sourceArray);
            TFGraph  graph;
            TFOutput input;
            TFOutput output;

            SetValue(out graph, out input, out output);

            using(TFSession session = new TFSession(graph))
            {
                TFTensor[] normalizedTensorArray = session.Run
                (
                    inputs      : new[] { input  },
                    inputValues : new[] { tensor },
                    outputs     : new[] { output }
                );

                return normalizedTensorArray[0];
            }
        }

        #endregion
    }
}

※ 닷넷 프레임워크 4.7.1 버전을 사용해야 한다.

※ 프로젝트 속성의 빌드 탭에서 "32비트 기본 사용"을 체크 해제해야 한다.

728x90
반응형
그리드형(광고전용)
Posted by icodebroker

댓글을 달아 주세요