[SDL] SDL OpenCV

C, C++ 2024. 1. 27. 01:00 |
반응형

SDL에서 OpenCV를 사용해 보자.

 

아래와 같이 비주얼 스튜디오에서 SDL과 OpenCV를 사용할 수 있도록 적절히 세팅한다.

 

 

 

 

 

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
#include <iostream>
#include "SDL.h"
#include "opencv2/opencv.hpp"
 
int main(int argc, char* argv[]) {
    SDL_Init(SDL_INIT_VIDEO);
    SDL_Window* window = SDL_CreateWindow("SDL Test", SDL_WINDOWPOS_UNDEFINED,
        SDL_WINDOWPOS_UNDEFINED, 640480, SDL_WINDOW_RESIZABLE);
    SDL_Renderer* renderer = SDL_CreateRenderer(window, -10);
 
    //////////////////// cv::Mat to SDL_Texture ////////////////////
    cv::Mat image = cv::imread("image.png", cv::IMREAD_COLOR);
    SDL_Rect destRect = { 00, image.cols, image.rows };
    SDL_Texture* texture = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_BGR24,
        SDL_TEXTUREACCESS_STREAMING, image.cols, image.rows);
    SDL_UpdateTexture(texture, NULL, (void*)image.data, image.step1());
    // SDL_UpdateTexture() 대신 아래 주석 처리된 코드를 사용할 수도 있다.
    // #include <opencv2/core/types_c.h> 필요.
 
    /*
    IplImage iplImage = cvIplImage(image);
    unsigned char* texture_data = NULL;
    int texture_pitch = 0;
    SDL_LockTexture(texture, 0, (void**)&texture_data, &texture_pitch);
    memcpy(texture_data, (void*)iplImage.imageData,
        iplImage.width * iplImage.height * iplImage.nChannels);
    SDL_UnlockTexture(texture);
    */
    ////////////////////////////////////////////////////////////////
 
    SDL_Event event;
    bool quit = false;
 
    while (!quit) {
        while (SDL_PollEvent(&event)) {
            switch (event.type) {
            case SDL_QUIT:
                quit = true;
                break;
            case SDL_KEYDOWN:
                printf("Key pressed: %s\n", SDL_GetKeyName(event.key.keysym.sym));
                if (event.key.keysym.sym == SDLK_ESCAPE)
                    quit = true;
                break;
            default:
                break;
            }
        }
 
        SDL_SetRenderDrawColor(renderer, 255255255, SDL_ALPHA_OPAQUE);
        SDL_RenderClear(renderer);
        SDL_RenderCopy(renderer, texture, NULL&destRect);
        SDL_RenderPresent(renderer);
    }
 
    SDL_DestroyTexture(texture);
    SDL_DestroyRenderer(renderer);
    SDL_DestroyWindow(window);
    SDL_Quit();
 
    return 0;
}
 

 

코드를 작성하고 빌드한다.

 

실행하면 이미지가 출력된다.

 

반응형
Posted by J-sean
:
반응형

OpenPose 소스를 컴파일하고 실행해 보자.

 

CMake를 다운로드하고 설치한다.

 

CUDA Toolkit 11.1.1을 다운로드하고 설치한다.

 

권장옵션으로 설치한다.

 

cuDNN 8.1.0을 다운로드한다. (NVIDIA 로그인이 필요하다)

 

 

다운로드한 cuDNN 압축을 풀고 cuda 폴더를 위 경로에 복사한다.

 

적당한 디렉토리에 OpenPose를 클론한다.

 

git clone https://github.com/CMU-Perceptual-Computing-Lab/openpose

cd openpose

git submodule update --init --recursive --remote

 

build 폴더를 만든다.

 

CMake를 실행하고 경로를 설정한다. 그리고 Configure를 클릭한다.

 

 

위와 같이 설정하고 Finish를 클릭한다.

 

Configuring이 끝나면 Generate를 클릭한다.

Python이나 Unity에서 사용한다면 옵션을 선택한다.

(Configure에서 x64를 선택했다면 64비트 Python이 필요하다)

 

OpenPose.sln을 실행한다.

 

Solution Configurations를 Release로 바꾸고 빌드한다.

빌드가 완료되면 실행(Ctrl+F5)한다. OpenPoseDemo가 실행된다.

 

 

Visual Studio 외부에서 OpenPoseDemo.exe나 다른 예제를 실행하려면 아래 파일과 폴더를 같은 폴더에 복사해야 한다.

 

bin 폴더의 모든 파일을 실행파일이 있는 폴더에 복사한다.

 

models 폴더를 실행파일이 있는 폴더에 복사한다.

 

예제에 따라 examples 폴더가 필요할 수도 있다.

 

 

생각보다 메모리가 많이 필요하다.

 

※ 참고

OpenPose Installation

반응형

'OpenCV' 카테고리의 다른 글

GDI+ and OpenCV - Bitmap to Mat & Mat to Bitmap Conversion  (0) 2022.01.02
OpenCV with C# and Camera  (0) 2021.12.29
OpenCvSharp for Network  (0) 2021.12.28
OpenCV with C#  (0) 2021.11.20
OpenCV with Qt and MSVC in Windows  (0) 2021.09.26
Posted by J-sean
:

OpenCvSharp Simple Camera Example

C# 2022. 1. 14. 18:07 |
반응형

C#과 OpenCvSharp를 이용한 간단한 카메라 응용 프로그램 예.

 

폼에 Button, RadioButton, PictureBox등을 적당히 배치한다.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
 
using System.Threading;
using OpenCvSharp;
using OpenCvSharp.Extensions;
 
namespace OpenCV
{
    delegate void dele(Mat m);
 
    public partial class Form1 : Form
    {
        bool isCameraOn;
 
        dele filter;    // 카메라에 적용할 필터(효과) 델리게이트
        Thread thread;
        Mat mat;
        VideoCapture videoCapture;
 
        public Form1()
        {
            InitializeComponent();
 
            this.FormBorderStyle = FormBorderStyle.FixedSingle;
            pictureBox1.SizeMode = PictureBoxSizeMode.StretchImage;
            button1.Text = "Camera Start";
            isCameraOn = false;
            filter = null;
            radioButton1.Checked = true;
        }
 
        private void CameraCallback()
        {
            mat = new Mat();
            videoCapture = new VideoCapture(0);
 
            if (!videoCapture.IsOpened())
            {
                Text = "Camera open failed!";
                MessageBox.Show("카메라를 열 수 없습니다. 연결 상태를 확인 해 주세요.");
 
                return;
            }
 
            while (true)
            {
                videoCapture.Read(mat);
 
                if (!mat.Empty() && filter != null)
                {
                    filter(mat);    // 선택된 라디오 버튼에 따른 필터 적용.                    
                }
 
                if (!mat.Empty())
                {
                    // 로고를 디스플레이하기 위해 그레이 이미지(1채널)는 컬러 포맷(3채널)으로 변환
                    if (mat.Channels() == 1)
                    {
                        Cv2.CvtColor(mat, mat, ColorConversionCodes.GRAY2BGR);
                    }
                    Cv2.PutText(mat, "SEAN"new OpenCvSharp.Point(550470), HersheyFonts.HersheyDuplex, 1new Scalar(00255), 2);
 
                    // 이 전 프레임에서 PictureBox에 로드된 비트맵 이미지를 Dispose하지 않으면 메모리 사용량이 크게 증가한다.
                    if (pictureBox1.Image != null)
                    {
                        pictureBox1.Image.Dispose();
                    }
                    pictureBox1.Image = BitmapConverter.ToBitmap(mat);
                }
            }
        }
 
        private void button1_Click(object sender, EventArgs e)
        {
            if (isCameraOn == false)
            {
                thread = new Thread(new ThreadStart(CameraCallback));
 
                thread.Start();
                isCameraOn = true;
                button1.Text = "Camera Stop";
            }
            else
            {
                if (videoCapture.IsOpened())
                {
                    thread.Abort();
                    if (pictureBox1.Image != null)
                    {
                        pictureBox1.Image.Dispose();
                    }
                    videoCapture.Release();
                    mat.Release();
                }
                isCameraOn = false;
                button1.Text = "Camera Start";
            }
        }
 
        private void button2_Click(object sender, EventArgs e)
        {
            System.Diagnostics.Process.Start("https://s-engineer.tistory.com/");
        }
 
        private void Form1_FormClosing(object sender, FormClosingEventArgs e)
        {
            if (thread != null && thread.IsAlive && videoCapture.IsOpened())
            {
                thread.Abort();
                if (pictureBox1.Image != null)
                {
                    pictureBox1.Image.Dispose();
                }
                videoCapture.Release();
                mat.Release();
            }
        }
 
        // 필터 함수들
        private void ToGray(Mat mat)
        {
            Cv2.CvtColor(mat, mat, ColorConversionCodes.BGR2GRAY);
        }
 
        private void ToEmboss(Mat mat)
        {
            float[] data = { -1.0f, -1.0f, 0.0f, -1.0f, 0f, 1.0f, 0.0f, 1.0f, 1.0f };
            Mat emboss = new Mat(33, MatType.CV_32FC1, data);
 
            Cv2.CvtColor(mat, mat, ColorConversionCodes.BGR2GRAY);
            Cv2.Filter2D(mat, mat, -1, emboss, new OpenCvSharp.Point(-1-1), 128);
 
            emboss.Release();
        }
 
        private void ToBlur(Mat mat)
        {
            Cv2.GaussianBlur(mat, mat, new OpenCvSharp.Size(), (double)3);
        }
 
        private void ToSharpen(Mat mat)
        {
            Mat blurred = new Mat();
            Cv2.GaussianBlur(mat, blurred, new OpenCvSharp.Size(), (double)3);
 
            // 아래 연산이 반복되면 메모리 사용량이 크게 증가한다.
            float alpha = 2.0f;
            ((1 + alpha) * mat - alpha * blurred).ToMat().CopyTo(mat);
            //mat = (1 + alpha) * mat - alpha * blurred;
 
            blurred.Release();
        }
 
        private void ToEdge(Mat mat)
        {
            Cv2.CvtColor(mat, mat, ColorConversionCodes.BGR2GRAY);
            Cv2.Canny(mat, mat, 5070);
        }
 
        // 라디오 버튼 이벤트 핸들러들
        private void radioButton1_CheckedChanged(object sender, EventArgs e)
        {
            if (((RadioButton)sender).Checked)
            {
                filter = null;
            }
        }
 
        private void radioButton2_CheckedChanged(object sender, EventArgs e)
        {
            if (((RadioButton)sender).Checked)
            {
                filter = ToGray;
            }
        }
 
        private void radioButton3_CheckedChanged(object sender, EventArgs e)
        {
            if (((RadioButton)sender).Checked)
            {
                filter = ToEmboss;
            }
        }
 
        private void radioButton4_CheckedChanged(object sender, EventArgs e)
        {
            if (((RadioButton)sender).Checked)
            {
                filter = ToBlur;
            }
        }
 
        private void radioButton5_CheckedChanged(object sender, EventArgs e)
        {
            if (((RadioButton)sender).Checked)
            {
                filter = ToSharpen;
            }
        }
 
        private void radioButton6_CheckedChanged(object sender, EventArgs e)
        {
            if (((RadioButton)sender).Checked)
            {
                filter = ToEdge;
            }
        }
    }
}
 

 

소스를 입력하고 빌드한다.

 

프로그램을 실행하고 Camera Start 버튼을 클릭한다.

 

다른 필터를 선택하면 그에 맞는 화면이 출력된다.

 

※ 소스에서 ToSharpen() 의 주석 부분은 제대로 실행되지 않는다. 관련 내용은 아래 링크를 참고하자.

2022.01.14 - [C#] - OpenCvSharp Simple Example and MatExpr

 

※ ToSharpen() 의 반복 실행으로 인한 메모리 사용량 증가는 OpenCV의 메모리 할당을 파악하지 못하는 .NET Garbage Collector의 문제다. 아래와 같이 Garbage Collector 호출 코드 추가로 해결은 가능하다.

1
2
3
4
5
6
7
8
9
10
11
12
private void ToSharpen(Mat mat)
{
    Mat blurred = new Mat();
    Cv2.GaussianBlur(mat, blurred, new OpenCvSharp.Size(), (double)3);
 
    float alpha = 2.0f;
    ((1 + alpha) * mat - alpha * blurred).ToMat().CopyTo(mat);
 
    GC.Collect();
 
    blurred.Release();
}
 

 

https://github.com/shimat/opencvsharp/issues/391

 

반응형
Posted by J-sean
:
반응형

C#과 OpenCvSharp를 이용한 간단한 이미지 변환 예.

 

폼에 Button과 PictureBox를 적당히 배치한다.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
 
using OpenCvSharp;
 
namespace OpenCV
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }
 
        private void button1_Click(object sender, EventArgs e)
        {
            try
            {
                OpenFileDialog dlg = new OpenFileDialog();
                if (dlg.ShowDialog() == DialogResult.OK)
                {
                    Mat mat = Cv2.ImRead(dlg.FileName);
 
                    if (pictureBox1.Image != null)
                    {
                        pictureBox1.Image.Dispose();
                    }
 
                    // Canny Edge Detection(컬러 이미지를 그레이 이미지로 변환 후 Canny Edge Detection)
                    //Cv2.CvtColor(mat, mat, ColorConversionCodes.BGR2GRAY);
                    //Cv2.Canny(mat, mat, 50, 100);
 
                    // 이미지 샤프닝(가우시안블러 후 샤프닝)
                    Mat blurred = new Mat();
                    Cv2.GaussianBlur(mat, blurred, new OpenCvSharp.Size(), (double)3);
 
                    float alpha = 2.0f;
                    mat = (1 + alpha) * mat - alpha * blurred;
                    //((1 + alpha) * mat - alpha * blurred).ToMat().CopyTo(mat);
 
                    // PictureBox에 이미지 디스플레이(Mat to Bitmap)
                    System.IO.MemoryStream memoryStream = new System.IO.MemoryStream(mat.ToBytes());
                    pictureBox1.Image = new Bitmap(memoryStream);
                }
            }
            catch (Exception exc)
            {
                MessageBox.Show(exc.Message);
            }
        }
    }
}
 

 

이미지 샤프닝 예제를 입력하고 빌드한다.

 

프로그램을 실행하고 이미지를 불러오면 샤프닝 처리가 되어 표시된다.

 

원본 이미지

 

1
2
3
4
5
6
7
// 이미지 샤프닝(가우시안블러 후 샤프닝)
Mat blurred = new Mat();
Cv2.GaussianBlur(mat, blurred, new OpenCvSharp.Size(), (double)3);
 
float alpha = 2.0f;
mat = (1 + alpha) * mat - alpha * blurred;
//((1 + alpha) * mat - alpha * blurred).ToMat().CopyTo(mat);
 

 

이미지 샤프닝 코드 부분을 보면 위와같이 Mat 클래스에 +, -, * 등의 연산을 직접한다. 이 때 Mat 클래스는 효율을 높이기 위해 MatExpr 클래스로 변환 되어 연산이 진행된다.

 

위 예제에서는 별 문제 없지만 Mat 클래스 인스턴스의 레퍼런스(포인터)가 함수의 파라미터로 넘어 오고 그 함수에서 계산해서 다시 리턴하는 등의 작업이 진행될 때는 이렇게 계산 결과를 대입하는 경우 계산된 데이터가 제대로 전달 되지 않는다. C++에서는 같은 방식으로 해도 문제가 없다. 내가 모르는 C#의 특성이 있는거 같다.

 

이럴때는 주석 부분과 같이 MatExpr 클래스로 변환 되는 부분을 괄호로 감싸고 MatExpr.ToMat()로 Mat 클래스로 변환해서 다시 Mat.CopyTo()로 복사한다. 아니면 함수의 파라미터 선언을 (ref Mat mat) 처럼 바꿔서 레퍼런스를 주고 받도록 바꾸면 된다. 아래 링크의 경우 델리게이트 선언도 레퍼런스를 주고 받도록 바꿔야 한다.

2022.01.14 - [C#] - OpenCvSharp Simple Camera Example

 

반응형
Posted by J-sean
:
반응형

GDI Plus Bitmap을 OpenCV Mat으로, 다시 OpenCV Mat을 GDI Plus Bitmap으로 변환해 보자.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
#include <windows.h>
#include <gdiplus.h>
#include <opencv2/opencv.hpp>
 
#pragma comment(lib, "gdiplus")
 
using namespace cv;
using namespace Gdiplus;
 
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
HINSTANCE g_hInst;
LPCSTR lpszClass = "Mat and Bitmap";
 
Mat originalImage;
Mat bitmapToMat;
 
int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpszCmdParam, int nCmdShow)
{
    HWND hWnd;
    MSG Message;
    WNDCLASS WndClass;
    g_hInst = hInstance;
 
    ULONG_PTR gpToken;
    GdiplusStartupInput gpsi;
    if (GdiplusStartup(&gpToken, &gpsi, NULL!= Ok) {
        MessageBox(NULL, TEXT("GDI+ start-up error."), TEXT("GDI+ Error"), MB_OK);
 
        return 0;
    }
 
    WndClass.cbClsExtra = 0;
    WndClass.cbWndExtra = 0;
    WndClass.hbrBackground = (HBRUSH)(COLOR_BTNFACE + 1); //(HBRUSH)GetStockObject(WHITE_BRUSH);
    WndClass.hCursor = LoadCursor(NULL, IDC_ARROW);
    WndClass.hIcon = LoadIcon(NULL, IDI_APPLICATION);
    WndClass.hInstance = hInstance;
    WndClass.lpfnWndProc = (WNDPROC)WndProc;
    WndClass.lpszClassName = lpszClass;
    WndClass.lpszMenuName = NULL;
    WndClass.style = CS_HREDRAW | CS_VREDRAW;
    RegisterClass(&WndClass);
 
    hWnd = CreateWindow(lpszClass, lpszClass, WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT,
        630960NULL, (HMENU)NULL, hInstance, NULL);
    ShowWindow(hWnd, nCmdShow);
 
    while (GetMessage(&Message, 000)) {
        TranslateMessage(&Message);
        DispatchMessage(&Message);
    }
 
    GdiplusShutdown(gpToken);
 
    return (int)Message.wParam;
}
 
Mat BitmapToMat(Bitmap* bitmap)
{
    PixelFormat pixelFormat = bitmap->GetPixelFormat();
    if (pixelFormat != PixelFormat32bppARGB) // PixelFormat24bppRGB
        return Mat();
 
    int width = bitmap->GetWidth();
    int height = bitmap->GetHeight();
    Gdiplus::Rect rectLock(00, width, height);
    Gdiplus::BitmapData bitmapData;
 
    if (bitmap->LockBits(&rectLock, Gdiplus::ImageLockModeRead, pixelFormat, &bitmapData) != Gdiplus::Ok)
        return Mat();
 
    Mat mat = Mat(height, width, CV_8UC4, // CV_8UC3
        static_cast<unsigned char*>(bitmapData.Scan0), bitmapData.Stride).clone();
 
    bitmap->UnlockBits(&bitmapData);
 
    return mat;
}
 
void OnPaint(HDC hdc)
{
    Graphics G(hdc);
 
    // Mat to Bitmap
    cvtColor(originalImage, originalImage, COLOR_BGR2BGRA);
    Bitmap bitmap((INT)originalImage.size().width, (INT)originalImage.size().height, (INT)originalImage.step,
        PixelFormat32bppARGB, originalImage.data);
    G.DrawImage(&bitmap, 00, bitmap.GetWidth(), bitmap.GetHeight());
 
    // Bitmap to Mat
    bitmapToMat = BitmapToMat(&bitmap);
 
    imshow("BitmapToMat", bitmapToMat);
}
 
LRESULT CALLBACK WndProc(HWND hWnd, UINT iMessage, WPARAM wParam, LPARAM lParam)
{
    HDC hdc;
    PAINTSTRUCT ps;
 
    switch (iMessage) {
    case WM_CREATE:
        originalImage = imread("Barbara.jpg");
 
        return 0;
 
    case WM_PAINT:
        hdc = BeginPaint(hWnd, &ps);
        OnPaint(hdc);
        EndPaint(hWnd, &ps);
 
        return 0;
 
    case WM_DESTROY:
        PostQuitMessage(0);
 
        return 0;
    }
 
    return(DefWindowProc(hWnd, iMessage, wParam, lParam));
}
 

 

소스를 입력하고 빌드한다.

 

실행하면 Bitmap과 Mat 포맷의 이미지가 출력된다.

 

Mat to Bitmap 주석 부분이 Mat에서 Bitmap으로 변환의 핵심이다.

 

위 함수가 Bitmap에서 Mat으로 변환의 핵심이다.

 

반응형

'OpenCV' 카테고리의 다른 글

Compiling and Running OpenPose from Source  (2) 2022.05.15
OpenCV with C# and Camera  (0) 2021.12.29
OpenCvSharp for Network  (0) 2021.12.28
OpenCV with C#  (0) 2021.11.20
OpenCV with Qt and MSVC in Windows  (0) 2021.09.26
Posted by J-sean
:
반응형

 

2021.12.30 - [Unity] - Unity3D - 유니티 3D with OpenCV 1 에서 만든 라이브러리(OpenCVDll.dll)와 

2021.12.29 - [Unity] - Unity3D - 유니티 3D WebCamTexture 라이브 비디오 텍스쳐 의 WebCamTexture를 이용해 라이브 비디오 데이터를 프로세싱하고 오브젝트의 텍스쳐로 사용해 보자.

 

이미지 프로세싱에 사용할 OpenCVDll 라이브러리를 세팅하고 Cube를 생성한다.

 

Cube에 스크립트를 추가한다.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
 
using System.Runtime.InteropServices;
 
public class UnityWithOpenCV : MonoBehaviour
{
    [DllImport("OpenCVDll")]
    private static extern void FlipImage(ref Color32[] rawImage, int width, int height);
 
    Renderer renderer;
    WebCamTexture webCamTexture;
    Color32[] image;
 
    // Start is called before the first frame update
    void Start()
    {
        renderer = GetComponent<Renderer>();
        webCamTexture = new WebCamTexture(64048060);
        webCamTexture.Play();
 
        image = new Color32[webCamTexture.width * webCamTexture.height];
    }
 
    // Update is called once per frame
    void Update()
    {
        webCamTexture.GetPixels32(image);
 
        // Image processing
        FlipImage(ref image, webCamTexture.widthwebCamTexture.height);
 
        Texture2D texture2D = new Texture2D(webCamTexture.width, webCamTexture.height);
        texture2D.SetPixels32(image);
        renderer.material.mainTexture = texture2D as Texture;
        (renderer.material.mainTexture as Texture2D).Apply();
    }
}
 

 

Cube에 추가한 스크립트에 코드를 작성하고 저장한다. 모든 프레임에 텍스쳐가 이미지 프로세서(FlipImage())에 의해 상하좌우 반전된다.

 

유니티로 돌아오면 스크립트가 컴파일 된다. 실행 버튼을 클릭하면 매 프레임마다 Cube의 텍스쳐가 상하좌우 반전 처리된다.

 

 

스크립트의 FlipImage()를 주석처리하고 실행하면 원래 이미지의 텍스쳐가 표시된다.

 

프로그램을 실행하는 동안 처리된 이미지가 Inpector창의 Material Preview에도 실시간 적용된다.

 

반응형
Posted by J-sean
:
반응형

유니티에서 OpenCV를 사용할 수 있도록 라이브러리(DLL)를 만들어 보자.

 

Visual Studio에서 C++ - Windows - Empty Project를 선택한다.

 

적당한 이름과 폴더를 지정한다.

 

프로젝트가 생성되었으면 C++ File을 추가한다. (Add New Item)

 

Solution Platforms는 x64로 변경한다.

 

 

프로젝트 Property Pages - General - Configuration Type - Dynamic Library (.dll)로 변경한다.

 

Advanced - Target File Extension - .dll로 변경한다.

 

C/C++ - General - Additional Include Directories에 OpenCV Include 폴더를 추가한다.

 

Linker - General - Additional Library Directories에 OpenCV Library 폴더를 추가한다.

 

 

Linker - Input - Additional Dependencies에 OpenCV 라이브러리(opencv_worldXXXd.lib)를 추가한다.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#include <opencv2/opencv.hpp>
 
struct Color32
{
    uchar red;
    uchar green;
    uchar blue;
    uchar alpha;
};
 
extern "C" __declspec(dllexportvoid FlipImage(Color32 **rawImage, int width, int height)
{
    using namespace cv;
    Mat image(height, width, CV_8UC4, *rawImage);
    flip(image, image, -1);
}
 

 

프로젝트 세팅이 끝나면 이미지의 상하좌우를 반전하는 소스를 입력하고 빌드한다.

 

라이브러리 파일(OpenCVDll.dll)이 생성된다.

 

유니티3D 프로젝트를 생성한다.

 

 

Assets에 Plugin 폴더를 만들고 OpenCV 라이브러리 파일(opencv_worldXXX.dll)과 위에서 만든 라이브러리 파일(OpenCVDll.dll)을 복사한다.

 

원하는 이미지 파일을 Assets 폴더에 복사한다.

 

아래와 같이 텍스쳐 세팅을 변경한다.

Texture Type - Sprite (2D and UI)

Advanced - Read/Write Enabled - Check

Default - Format - RGBA 32 bit

Apply 클릭

 

Image UI를 생성한다.

 

 

Source Image에 위에서 추가한 이미지를 선택한다.

 

스크립트 컴포넌트를 추가한다.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
 
using System.Runtime.InteropServices;
using UnityEngine.UI;
 
public class OpenCVTest : MonoBehaviour
{
    [DllImport("OpenCVDll")]
    private static extern void FlipImage(ref Color32[] rawImage, int width, int height);
 
    // Start is called before the first frame update
    void Start()
    {
        Color32[] image = GetComponent<Image>().sprite.texture.GetPixels32();
        FlipImage(ref image, 608912);
        GetComponent<Image>().sprite.texture.SetPixels32(image);
        GetComponent<Image>().sprite.texture.Apply();
    }
 
    // Update is called once per frame
    void Update()
    {
 
    }
}
 

 

OpenCVDll.dll을 사용하는 스크립트를 작성하고 저장한다.

 

유니티로 돌아오면 스크립트가 컴파일 된다. 실행 버튼을 클릭한다.

 

 

이미지의 상하좌우가 반전된다.

 

3D 오브젝트에 적용해 보자. Image UI를 삭제하고 Cube를 생성한다.

 

위에서 사용했던 이미지는 삭제하고 다시 복사한다. 그리고 아래와 같이 세팅한다.

Advanced - Read/Write Enabled - Check

Default - Format - RGBA 32 bit

Apply 클릭

 

Material을 생성하고 위에서 세팅한 이미지를 적용한다. (Albedo 옆 동그라미 클릭)

 

 

Cube에 새로 생성한 Material을 적용하고 OpenCVTest 스크립트를 추가한다.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
 
using System.Runtime.InteropServices;
 
public class OpenCVTest : MonoBehaviour
{
    [DllImport("OpenCVDll")]
    private static extern void FlipImage(ref Color32[] rawImage, int width, int height);
 
    // Start is called before the first frame update
    void Start()
    {
        Color32[] image = (GetComponent<Renderer>().material.mainTexture as Texture2D).GetPixels32();
        FlipImage(ref image, 608912);
        (GetComponent<Renderer>().material.mainTexture as Texture2D).SetPixels32(image);
        (GetComponent<Renderer>().material.mainTexture as Texture2D).Apply();
 
        // Assets - Resources 폴더에 이미지를 저장하고 로드해서 텍스쳐로 활용하는 예
        //Texture2D texture2D = Resources.Load("Barbara") as Texture2D;
        //Color32[] image = texture2D.GetPixels32();
        //FlipImage(ref image, 608, 912);
        //texture2D.SetPixels32(image);
        //texture2D.Apply();
        //GetComponent<Renderer>().material.mainTexture = texture2D;
    }
 
    // Update is called once per frame
    void Update()
    {
 
    }
}
 

 

OpenCVTest 스크립트는 위와 같이 수정하고 저장한다.

 

유니티로 돌아오면 스크립트가 컴파일된다. 실행 버튼을 클릭한다.

 

상하좌우가 반전된다.

 

반응형
Posted by J-sean
:

OpenCV with C# and Camera

OpenCV 2021. 12. 29. 17:32 |
반응형

C#으로 OpenCV와 카메라를 사용해 보자.

 

C#에서 OpenCV를 사용하기 위한 준비는 아래 링크를 참고 한다.

2021.11.20 - [OpenCV] - OpenCV with C#

 

하지만 링크와 같이 OpenCvSharp4.Windows만 설치하면 Extensions가 설치 되지 않으므로 마찬가지로 NuGet Package Manager에서 검색하고 설치한다. (BitmapConverter.ToBitmap()을 사용하기 위해)

 

OpenCvSharp4.Extensions를 설치한다.

 

폼에 PictureBox와 Button을 적당히 배치한다.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
 
using System.Threading;
using OpenCvSharp;
using OpenCvSharp.Extensions;
 
namespace OpenCV
{
    public partial class Form1 : Form
    {
        bool isCameraOn;
 
        Thread thread;
        Mat mat;
        VideoCapture videoCapture;
 
        public Form1()
        {
            InitializeComponent();
 
            pictureBox1.SizeMode = PictureBoxSizeMode.StretchImage;
            button1.Text = "Start";
            isCameraOn = false;
        }
 
        private void CameraCallback()
        {
            mat = new Mat();
            videoCapture = new VideoCapture(0);
 
            if (!videoCapture.IsOpened())
            {
                Text = "Camera open failed!";
                return;
            }
 
            while (true)
            {
                videoCapture.Read(mat);
 
                if (!mat.Empty())
                {
                    pictureBox1.Image = BitmapConverter.ToBitmap(mat);
 
                    //System.IO.MemoryStream memoryStream = new System.IO.MemoryStream(mat.ToBytes());
                    //pictureBox1.Image = new Bitmap(memoryStream);
                }
            }
        }
 
        private void button1_Click(object sender, EventArgs e)
        {
            if (isCameraOn == false)
            {
                thread = new Thread(new ThreadStart(CameraCallback));
 
                thread.Start();
                isCameraOn = true;
                button1.Text = "Stop";
            }
            else
            {
                if (videoCapture.IsOpened())
                {
                    thread.Abort();
                    videoCapture.Release();
                    mat.Release();
                }
                isCameraOn = false;
                button1.Text = "Start";
            }
        }
 
        private void Form1_FormClosing(object sender, FormClosingEventArgs e)
        {
            if (thread != null && thread.IsAlive && videoCapture.IsOpened())
            {
                thread.Abort();
                videoCapture.Release();
                mat.Release();
            }
        }
    }
}
 

 

소스를 입력하고 빌드한다. (주석에 있는 내용을 사용하면 BitmapConverter.ToBitmap()을 사용하지 않아도 되므로 OpenCvSharp.Extensions도 설치할 필요가 없다)

 

 

실행하면 Start 버튼이 보인다.

 

Start 버튼을 클릭하면 Stop으로 바뀌고 카메라 영상이 재생된다.

 

반응형

'OpenCV' 카테고리의 다른 글

Compiling and Running OpenPose from Source  (2) 2022.05.15
GDI+ and OpenCV - Bitmap to Mat & Mat to Bitmap Conversion  (0) 2022.01.02
OpenCvSharp for Network  (0) 2021.12.28
OpenCV with C#  (0) 2021.11.20
OpenCV with Qt and MSVC in Windows  (0) 2021.09.26
Posted by J-sean
: