반응형

SDL3에 Dear ImGui를 사용해 보자. 아래 링크를 참고해 SDL3를 준비한다.

2025.03.30 - [C, C++] - [SDL3] Simple DirectMedia Layer 3 Setup and Getting Started - SDL3 설정 및 초기화

 

프로젝트 폴더에 위와 같이 ImGui 관련 파일을 복사한다. (lib, dll 파일은 없다)

 

프로젝트에 ImGui 필터를 추가하고 파일을 추가한다.

 

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
#define SDL_MAIN_USE_CALLBACKS 1
#include <SDL3/SDL.h>
#include <SDL3/SDL_main.h>
#include <stdio.h>
#include <Windows.h>
 
#include "imgui.h"
#include "imgui_impl_sdl3.h"
#include "imgui_impl_sdlrenderer3.h"
 
static SDL_Window* window = NULL;
static SDL_Renderer* renderer = NULL;
ImGuiIO* pio = NULL;
 
SDL_AppResult SDL_AppInit(void** appstate, int argc, char* argv[])
{
    SDL_SetAppMetadata("Example Renderer Clear""1.0""com.example.renderer-clear");
 
    if (!SDL_Init(SDL_INIT_VIDEO)) {
        SDL_Log("Couldn't initialize SDL: %s", SDL_GetError());
        return SDL_APP_FAILURE;
    }
 
    if (!SDL_CreateWindowAndRenderer("examples/renderer/clear"6404800&window, &renderer)) {
        SDL_Log("Couldn't create window/renderer: %s", SDL_GetError());
        return SDL_APP_FAILURE;
    }
 
    // Setup Dear ImGui context
    IMGUI_CHECKVERSION();
    ImGui::CreateContext();
    ImGuiIO& io = ImGui::GetIO(); (void)io;
    // (void)io는 의미 없는 명령.  just to avoid compiler warning on unused variable
    pio = &io;
    // io가 SDL_AppIterate()에서 사용되기 때문에 전역 포인터 변수 pio에 대입.
    io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard;     // Enable Keyboard Controls
    //io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad;      // Enable Gamepad Controls
 
    // Setup Dear ImGui style
    //ImGui::StyleColorsDark();
    ImGui::StyleColorsLight();
 
    // Setup Platform/Renderer backends
    ImGui_ImplSDL3_InitForSDLRenderer(window, renderer);
    ImGui_ImplSDLRenderer3_Init(renderer);
 
    return SDL_APP_CONTINUE;
}
 
SDL_AppResult SDL_AppEvent(void* appstate, SDL_Event* event)
{
    // Poll and handle events (inputs, window resize, etc.)
    // You can read the io.WantCaptureMouse, io.WantCaptureKeyboard flags to tell if dear imgui wants to use your inputs.
    // - When io.WantCaptureMouse is true, do not dispatch mouse input data to your main application, or clear/overwrite your copy of the mouse data.
    // - When io.WantCaptureKeyboard is true, do not dispatch keyboard input data to your main application, or clear/overwrite your copy of the keyboard data.
    // Generally you may always pass all inputs to dear imgui, and hide them from your application based on those two flags.
    ImGui_ImplSDL3_ProcessEvent(event);
 
    switch (event->type) {
    case SDL_EVENT_QUIT:
        return SDL_APP_SUCCESS;
    case SDL_EVENT_KEY_DOWN:
        printf("Key pressed: %s\n", SDL_GetKeyName(event->key.key));
        if (event->key.key == SDLK_ESCAPE)
            return SDL_APP_SUCCESS;
        break;
    default:
        break;
    }
 
    return SDL_APP_CONTINUE;
}
 
SDL_AppResult SDL_AppIterate(void* appstate)
{
    // Start the Dear ImGui frame
    ImGui_ImplSDLRenderer3_NewFrame();
    ImGui_ImplSDL3_NewFrame();
    ImGui::NewFrame();
 
    // Show a simple window that we create ourselves. We use a Begin/End pair to create a named window.
    {
        static float f = 0.0f;
        static int counter = 0;
 
        ImGui::Begin("Hello, world!"); // Create a window called "Hello, world!" and append into it.
        //ImGui::SetWindowSize(ImVec2(100, 20)); // 매 프레임마다 윈도우 사이즈를 (100, 20)으로 지정한다.
        //ImGui::SetWindowSize(ImVec2(100, 20), ImGuiCond_Once); // 프로그램을 시작할때 한 번만 윈도우 사이즈를 지정한다.
        //ImGui::SetWindowSize(ImVec2(100, 20), ImGuiCond_FirstUseEver); // 프로그램을 처음 시작할때(imgui.ini파일이 존재하지 않을때)
                                                                         // 한 번만 윈도우 사이즈를 지정한다.
        ImGui::Text("This is some useful text."); // Display some text (you can use a format strings too)
        if (ImGui::Button("Button")) // Buttons return true when clicked (most widgets return true when edited/activated)
            counter++;
        ImGui::SameLine();
        ImGui::Text("counter = %d", counter);
        ImGui::Text("Application average %.3f ms/frame (%.1f FPS)"1000.0f / pio->Framerate, pio->Framerate);
        ImGui::End();
    }
    // 윈도우 없이 위치가 고정된 버튼 생성하기.
    {
        ImGui::Begin("No Window"0, ImGuiWindowFlags_NoBackground | ImGuiWindowFlags_NoDecoration);
        ImGui::SetWindowPos(ImVec2(1020));
        ImGui::SetWindowSize(ImVec2(10020));
        // WindowSize()는 이 예제 블럭에서 생성하려는 버튼의 사이즈를 지정하는 함수가 아니다. 버튼을 포함하는 윈도우의 사이즈다.
        // 이 예제는 윈도우가 보이지 않으므로 사이즈를 지정하지 않으면 버튼 사이즈보다 훨씬 크게 지정되서 근처의 다른 UI가
        // 이 윈도우에 가려져 제대로 동작하지 않을 수 있다. (보이기는 하지만 클릭이 되지 않는다)
        // 아니면, Begin()에서 ImGuiWindowFlags_NoBringToFrontOnFocus 옵션을 추가하면 다른 윈도우의 UI 동작을 방해하지 않는다.
        // 이 예제에서 ImGuiWindowFlags_NoInputs 옵션은 버튼을 비활성화 시키므로 사용하면 안된다.        
        ImGui::Button("Fixed Button");
        ImGui::End();
    }
 
    // Rendering
    ImGui::Render();
 
    SDL_SetRenderDrawColor(renderer, 255255255, SDL_ALPHA_OPAQUE);
    SDL_RenderClear(renderer);
    ImGui_ImplSDLRenderer3_RenderDrawData(ImGui::GetDrawData(), renderer);
    SDL_RenderPresent(renderer);
 
    return SDL_APP_CONTINUE;
}
 
void SDL_AppQuit(void* appstate, SDL_AppResult result)
{
    // Cleanup    
    ImGui_ImplSDLRenderer3_Shutdown();
    ImGui_ImplSDL3_Shutdown();
    ImGui::DestroyContext();
 
    SDL_DestroyRenderer(renderer);
    SDL_DestroyWindow(window);
    SDL_Quit();
}
 

 

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

 

실행하면 GUI가 표시된다.

 

이번엔 ImGui를 이용해 이미지를 출력해 보자.

 

stb_image.h를 추가한다.
stb_image.h
0.27MB

 

Image Loading and Displaying Examples

위 링크를 참고해 소스를 작성한다.

 

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
#define _CRT_SECURE_NO_WARNINGS
#define STB_IMAGE_IMPLEMENTATION
#define SDL_MAIN_USE_CALLBACKS 1
#include <SDL3/SDL.h>
#include <SDL3/SDL_main.h>
#include <stdio.h>
#include <Windows.h>
 
#include "imgui.h"
#include "imgui_impl_sdl3.h"
#include "imgui_impl_sdlrenderer3.h"
 
#include "stb_image.h"
 
static SDL_Window* window = NULL;
static SDL_Renderer* renderer = NULL;
ImGuiIO* pio = NULL;
 
SDL_Texture* my_texture;
int my_image_width, my_image_height;
 
bool LoadTextureFromMemory(const void* data, size_t data_size, SDL_Renderer* renderer, SDL_Texture** out_texture, int* out_width, int* out_height)
{
    int image_width = 0;
    int image_height = 0;
    int channels = 4;
    unsigned char* image_data = stbi_load_from_memory((const unsigned char*)data, (int)data_size, &image_width, &image_height, NULL4);
    if (image_data == nullptr)
    {
        fprintf(stderr, "Failed to load image: %s\n", stbi_failure_reason());
        return false;
    }
 
    SDL_Surface* surface = SDL_CreateSurfaceFrom(image_width, image_height, SDL_PIXELFORMAT_RGBA32, (void*)image_data, channels * image_width);
    if (surface == nullptr)
    {
        fprintf(stderr, "Failed to create SDL surface: %s\n", SDL_GetError());
        return false;
    }
 
    SDL_Texture* texture = SDL_CreateTextureFromSurface(renderer, surface);
    if (texture == nullptr)
        fprintf(stderr, "Failed to create SDL texture: %s\n", SDL_GetError());
 
    *out_texture = texture;
    *out_width = image_width;
    *out_height = image_height;
 
    SDL_DestroySurface(surface);
    stbi_image_free(image_data);
 
    return true;
}
 
// Open and read a file, then forward to LoadTextureFromMemory()
bool LoadTextureFromFile(const char* file_name, SDL_Renderer* renderer, SDL_Texture** out_texture, int* out_width, int* out_height)
{
    FILE* f = fopen(file_name, "rb");
    if (f == NULL)
        return false;
    fseek(f, 0, SEEK_END);
    size_t file_size = (size_t)ftell(f);
    if (file_size == -1)
        return false;
    fseek(f, 0, SEEK_SET);
    void* file_data = IM_ALLOC(file_size);
    fread(file_data, 1, file_size, f);
    fclose(f);
    bool ret = LoadTextureFromMemory(file_data, file_size, renderer, out_texture, out_width, out_height);
    IM_FREE(file_data);
    return ret;
}
 
SDL_AppResult SDL_AppInit(void** appstate, int argc, char* argv[])
{
    SDL_SetAppMetadata("Example Renderer Clear""1.0""com.example.renderer-clear");
 
    if (!SDL_Init(SDL_INIT_VIDEO)) {
        SDL_Log("Couldn't initialize SDL: %s", SDL_GetError());
        return SDL_APP_FAILURE;
    }
 
    if (!SDL_CreateWindowAndRenderer("examples/renderer/clear"6404800&window, &renderer)) {
        SDL_Log("Couldn't create window/renderer: %s", SDL_GetError());
        return SDL_APP_FAILURE;
    }
 
    // Setup Dear ImGui context
    IMGUI_CHECKVERSION();
    ImGui::CreateContext();
    ImGuiIO& io = ImGui::GetIO(); (void)io;
    // (void)io는 의미 없는 명령.  just to avoid compiler warning on unused variable
    pio = &io;
    // io가 SDL_AppIterate()에서 사용되기 때문에 전역 포인터 변수 pio에 대입.
    io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard;     // Enable Keyboard Controls
    //io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad;      // Enable Gamepad Controls
 
    // Setup Dear ImGui style
    //ImGui::StyleColorsDark();
    ImGui::StyleColorsLight();
 
    // Setup Platform/Renderer backends
    ImGui_ImplSDL3_InitForSDLRenderer(window, renderer);
    ImGui_ImplSDLRenderer3_Init(renderer);
 
    // Load our texture after initialising SDL
    bool ret = LoadTextureFromFile("palvin.jpg", renderer, &my_texture, &my_image_width, &my_image_height);
    IM_ASSERT(ret);
 
    return SDL_APP_CONTINUE;
}
 
SDL_AppResult SDL_AppEvent(void* appstate, SDL_Event* event)
{
    // Poll and handle events (inputs, window resize, etc.)
    // You can read the io.WantCaptureMouse, io.WantCaptureKeyboard flags to tell if dear imgui wants to use your inputs.
    // - When io.WantCaptureMouse is true, do not dispatch mouse input data to your main application, or clear/overwrite your copy of the mouse data.
    // - When io.WantCaptureKeyboard is true, do not dispatch keyboard input data to your main application, or clear/overwrite your copy of the keyboard data.
    // Generally you may always pass all inputs to dear imgui, and hide them from your application based on those two flags.
    ImGui_ImplSDL3_ProcessEvent(event);
 
    switch (event->type) {
    case SDL_EVENT_QUIT:
        return SDL_APP_SUCCESS;
    case SDL_EVENT_KEY_DOWN:
        printf("Key pressed: %s\n", SDL_GetKeyName(event->key.key));
        if (event->key.key == SDLK_ESCAPE)
            return SDL_APP_SUCCESS;
        break;
    default:
        break;
    }
 
    return SDL_APP_CONTINUE;
}
 
SDL_AppResult SDL_AppIterate(void* appstate)
{
    // Start the Dear ImGui frame
    ImGui_ImplSDLRenderer3_NewFrame();
    ImGui_ImplSDL3_NewFrame();
    ImGui::NewFrame();
 
    // Show a simple window that we create ourselves. We use a Begin/End pair to create a named window.
    {
        static float f = 0.0f;
        static int counter = 0;
 
        ImGui::Begin("Hello, world!"); // Create a window called "Hello, world!" and append into it.
        ImGui::Text("This is some useful text."); // Display some text (you can use a format strings too)
        if (ImGui::Button("Button")) // Buttons return true when clicked (most widgets return true when edited/activated)
            counter++;
        ImGui::SameLine();
        ImGui::Text("counter = %d", counter);
        ImGui::Text("Application average %.3f ms/frame (%.1f FPS)"1000.0f / pio->Framerate, pio->Framerate);
        ImGui::End();
    }
    // 윈도우 없이 위치가 고정된 이미지 생성하기.
    {
        ImGui::Begin("No Window"0, ImGuiWindowFlags_NoBackground | ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_NoBringToFrontOnFocus | ImGuiWindowFlags_NoInputs);
        ImGui::SetWindowPos(ImVec2(00));
        ImGui::Text("pointer = %p", my_texture);
        ImGui::Text("size = %d x %d", my_image_width, my_image_height);
        ImGui::Image((ImTextureID)(intptr_t)my_texture, ImVec2((float)my_image_width, (float)my_image_height));
        ImGui::End();
    }
 
    // Rendering
    ImGui::Render();
 
    SDL_SetRenderDrawColor(renderer, 255255255, SDL_ALPHA_OPAQUE);
    SDL_RenderClear(renderer);
    ImGui_ImplSDLRenderer3_RenderDrawData(ImGui::GetDrawData(), renderer);
    SDL_RenderPresent(renderer);
 
    return SDL_APP_CONTINUE;
}
 
void SDL_AppQuit(void* appstate, SDL_AppResult result)
{
    // Cleanup    
    ImGui_ImplSDLRenderer3_Shutdown();
    ImGui_ImplSDL3_Shutdown();
    ImGui::DestroyContext();
 
    SDL_DestroyRenderer(renderer);
    SDL_DestroyWindow(window);
    SDL_Quit();
}
 

 

 

실행하면 이미지가 표시된다.

 

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

C#과 Python 프로그램간 이미지 데이터를 공유해 보자.

 

1) Python 프로그램 데이터를 C# 프로그램에 공유.

mmap

 

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
import time
import mmap
import cv2
 
frame = cv2.imread("image.jpg", cv2.IMREAD_COLOR)
h, w, c = frame.shape
buffer_size = h * w * c
 
# 3개의 4바이트(12바이트) 데이터를 위한 추가 공간 확보
#buffer_size = h * w * c + 12
 
# Open a memory-mapped file.
mm = mmap.mmap(-1, buffer_size, "Local\\MySharedMemory")
 
try:
    # 만약 3개의 4바이트 데이터를 저장하고 싶다면 아래와
    # 같이 한다.
    #mm.write(h.to_bytes(4))
    #mm.write(w.to_bytes(4))
    #mm.write(c.to_bytes(4))
 
    mm.write(frame.tobytes())
    
    # Keep the Python script running for demonstration.
    while True:
        time.sleep(1000)  # Sleep to prevent busy waiting.
finally:
    mm.close()
 

 

 

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
using OpenCvSharp;
using System.IO.MemoryMappedFiles;
using System.Runtime.InteropServices;
 
public class Program
{
    public static void Main()
    {
        int width = 396;
        int height = 495;
        int channels = 3;
 
        using (MemoryMappedFile mmf = MemoryMappedFile.OpenExisting("MySharedMemory"))
        {
            using (MemoryMappedViewAccessor accessor = mmf.CreateViewAccessor())
            {
                byte[] buffer = new byte[width * height * channels];
                accessor.ReadArray(0, buffer, 0, buffer.Length);
 
                // 만약 buffer 앞 부분에 3개의 4바이트 int 데이터가 저장되어 있다면 아래와 같이 읽는다.
                // (물론 위에서 buffer[] 생성시 12바이트의 추가 공간 확보가 필요하다)
                //int h = ((buffer[0] << 24) +  (buffer[1] << 16) + (buffer[2] << 8) + (buffer[3]));
                //Console.WriteLine(h);
                //int w = ((buffer[4] << 24) + (buffer[5] << 16) + (buffer[6] << 8) + (buffer[7]));
                //Console.WriteLine(w);
                //int c = ((buffer[8] << 24) + (buffer[9] << 16) + (buffer[10] << 8) + (buffer[11]));
                //Console.WriteLine(c);
 
                // When buffer[] represents encoded image data (ex. JPEG, PNG, etc.), you can use
                // FromImageData or ImDecode.
                //buffer = System.IO.File.ReadAllBytes("image.jpg");
                //Mat mat = Mat.FromImageData(buffer, ImreadModes.Color);                
                //Mat mat = Cv2.ImDecode(buffer, ImreadModes.Color);
 
                // When buffer[] represens pixel data (BGRBGR...) , you need to hard-code copying operation.
                //Mat mat = new Mat(height, width, MatType.CV_8UC3);
                //Mat.Indexer<Vec3b> indexer = mat.GetGenericIndexer<Vec3b>();
                //for (int y = 0; y < height; y++)
                //{
                //    for (int x = 0; x < width; x++)
                //    {
                //        int pos = y * width * channels + x * channels;
                //        byte blue = buffer[pos + 0];
                //        byte green = buffer[pos + 1];
                //        byte red = buffer[pos + 2];
                //        Vec3b newValue = new Vec3b(blue, green, red);
                //        indexer[y, x] = newValue;
                //    }
                //}
 
                // If buffer[]'s step length is equal to the Mat's, there is a more effective way.
                Mat mat = new Mat(height, width, MatType.CV_8UC3);
                int length = height * width * channels;
                Marshal.Copy(buffer, 0, mat.Data, length);
 
                Cv2.ImShow("image", mat);
                Cv2.WaitKey(0);
                Cv2.DestroyAllWindows();
            }
        }
    }
}
 

 

 

Python 프로그램을 먼저 실행하고 C# 프로그램을 실행한다.

 

2) C# 프로그램 데이터를 Python 프로그램에 공유.

 

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
using System;
using System.IO.MemoryMappedFiles;
using System.Runtime.InteropServices;
using OpenCvSharp;
 
public class Program
{
    public static void Main()
    {   
        Mat image = Cv2.ImRead("image.jpg", ImreadModes.Color);
        int width = image.Width;
        int height = image.Height;
        int channels = image.Channels();
        int buffer_size = width * height * channels;
 
        using (MemoryMappedFile mmf = MemoryMappedFile.CreateNew("MySharedMemory", buffer_size))
        {
            using (MemoryMappedViewAccessor accessor = mmf.CreateViewAccessor())
            {
                byte[] buffer = new byte[buffer_size];
                Marshal.Copy(image.Data, buffer, 0, buffer_size);
                accessor.WriteArray<byte>(0, buffer, 0, buffer_size);
            }
            
            Console.WriteLine("waiting...");
            Console.ReadLine();
        }        
    }
}
 

 

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
import mmap
import numpy as np
import cv2
 
height = 495
width = 396
channels = 3
buffer_size = height * width * channels
 
mm = mmap.mmap(-1, buffer_size, "Local\\MySharedMemory")
 
try:    
    buffer = mm.read(buffer_size)
    image_arr = np.frombuffer(buffer, np.ubyte)
    image = image_arr.reshape(height, width, channels)
    
    cv2.imshow("image", image)
    cv2.waitKey(0)
finally:
    mm.close()
    cv2.destroyAllWindows()
 

 

결과는 같다.

 

※ 참고

2025.02.23 - [OpenCV] - C and Python OpenCV Image Data Share (Memory Mapped File)

 

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

PyQt를 이용해 OpenCV 이미지를 출력해 보자.

 

디자이너에서 Dialog에 Label을 하나 배치하고 사이즈를 충분히 크게 한다. (test.ui)

 

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
import sys
import cv2
from PyQt5 import QtWidgets
from PyQt5 import QtGui
from PyQt5 import uic
 
myui = uic.loadUiType("test.ui")
 
class MyApp(QtWidgets.QWidget, myui[0]):
    def __init__(self):
        super().__init__()
        self.setupUi(self)
 
        # 1) QtWidgets.QGraphicsView()에 이미지 출력하기
        # pixmap = QtGui.QPixmap()
        # pixmap.load("palvin.jpg")
        # scene = QtWidgets.QGraphicsScene()
        # scene.addPixmap(pixmap)
        # self.graphicsView.setScene(scene)
 
        # 2) QtWidgets.QGraphicsView()에 OpenCV 이미지 출력하기
        # img = cv2.imread("palvin.jpg")
        # img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
        # h, w, c = img.shape
        # qimg = QtGui.QImage(img.data, w, h, w*c, QtGui.QImage.Format_RGB888)
        # pixmap = QtGui.QPixmap.fromImage(qimg)
        # scene = QtWidgets.QGraphicsScene()
        # scene.addPixmap(pixmap)
        # self.graphicsView.setScene(scene)
 
        # QtWidgets.Label에 OpenCV 이미지 출력하기
        img = cv2.imread("palvin.jpg")
        img = cv2.resize(img, (self.label.size().width(), self.label.size().height()))
        img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
        h, w, c = img.shape
        qimg = QtGui.QImage(img.data, w, h, w*c, QtGui.QImage.Format_RGB888)
        pixmap = QtGui.QPixmap.fromImage(qimg)
        self.label.setPixmap(pixmap)
 
if __name__ == "__main__":
    app = QtWidgets.QApplication(sys.argv)
    myWindow = MyApp()
    myWindow.show()
   app.exec_()
 

 

소스를 입력하고 실행한다.

 

Label 사이즈에 맞게 이미지가 조절되었다.

 

이번엔 GraphicsView를 사용해 보자. 위 소스에서 1번이나 2번 주석을 해제한다.

1, 2번 주석부분의 소스는 이미지 크기를 조절하지 않는다.

 

Label은 삭제하고 Graphics View를 배치하고 크기를 조정한다.

 

이미지가 Graphics View보다 크지만 스크롤바가 표시된다.

 

Widget 없이 Dialog에 직접 이미지를 출력할 수 도 있다. Dialog만 남기고 모두 삭제한다.

 

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
import sys
import cv2
from PyQt5 import QtWidgets
from PyQt5 import QtGui
from PyQt5 import uic
 
myui = uic.loadUiType("test.ui")
 
class MyApp(QtWidgets.QWidget, myui[0]):
    def __init__(self):
        super().__init__()
        self.setupUi(self)
 
        self.img = cv2.imread("palvin.jpg")        
        self.img = cv2.cvtColor(self.img, cv2.COLOR_BGR2RGB)
        h, w, c = self.img.shape
        h, w = int(h/6), int(w/6)        
        self.img = cv2.resize(self.img, (w, h))
        self.qimg = QtGui.QImage(self.img.data, w, h, w*c, QtGui.QImage.Format_RGB888)
    
    def paintEvent(self, e):
        qp = QtGui.QPainter(self)
        self.drawCVImage(qp)
 
    def drawCVImage(self, qp):
        qp.drawImage(1010self.qimg)
        
if __name__ == "__main__":
    app = QtWidgets.QApplication(sys.argv)
    myWindow = MyApp()
    myWindow.show()
   app.exec_()
 

 

소스를 입력하고 실행한다.

 

Dialog에 직접 이미지가 출력된다.

 

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

로드한 이미지에 컬러키를 설정하고 투명하게 처리해 보자.

 

player.bmp

파란 배경의 BMP 파일을 준비한다.

 

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
#include <iostream>
#include "SDL.h"
 
#pragma comment(lib, "sdl2.lib")
#pragma comment(lib, "sdl2main.lib")
 
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);
 
    SDL_Surface* bmpSurface = SDL_LoadBMP("player.bmp");
    SDL_SetColorKey(bmpSurface, SDL_TRUE, SDL_MapRGB(bmpSurface->format, 000xFF));
    // Set the color key (transparent pixel) in a surface.
    // The color key defines a pixel value that will be treated as transparent in a blit.
    // For example, one can use this to specify that cyan pixels should be considered
    // transparent, and therefore not rendered.
    SDL_Rect destRect = { 00, bmpSurface->w, bmpSurface->h };
    SDL_Texture* texture = SDL_CreateTextureFromSurface(renderer, bmpSurface);
 
    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_RenderCopy(renderer, texture, NULL&destRect);
        SDL_RenderPresent(renderer);
    }
 
    SDL_FreeSurface(bmpSurface);
    SDL_DestroyTexture(texture);
    SDL_DestroyRenderer(renderer);
    SDL_DestroyWindow(window);
    SDL_Quit();
 
    return 0;
}
 

 

파란색(0, 0, 255)을 컬러키로 설정하는 코드를 작성하고 빌드한다.

 

파란색이 투명하게 처리되어 표시된다.

 

컬러키 설정 부분을 주석처리하고 빌드해 보자.

 

//SDL_SetColorKey(bmpSurface, SDL_TRUE, SDL_MapRGB(bmpSurface->format, 0, 0, 0xFF));

 

파란색이 그대로 표시된다.

 

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

SDL 기본 라이브러리는 BMP 형식의 이미지만 처리할 수 있다. JPG나 PNG 형식을 처리하기 위해서는 SDL_image 라이브러리가 필요하다.

 

우선 SDL 기본 라이브러리로 BMP 이미지를 렌더링 해 보자.

 

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
#include <iostream>
#include "SDL.h"
 
#pragma comment(lib, "sdl2.lib")
#pragma comment(lib, "sdl2main.lib")
 
int main(int argc, char* argv[]) {
    if (SDL_Init(SDL_INIT_VIDEO) < 0) {
        printf("SDL Initialization Fail: %s\n", SDL_GetError());
        return -1;
    }
 
    SDL_Window* window = SDL_CreateWindow("SDL Test", SDL_WINDOWPOS_UNDEFINED,
        SDL_WINDOWPOS_UNDEFINED, 640480, SDL_WINDOW_RESIZABLE);
 
    if (!window) {
        printf("SDL_CreateWindow Error: %s\n", SDL_GetError());
        SDL_Quit();
        return -1;
    }
 
    SDL_Renderer* renderer = SDL_CreateRenderer(window, -10);
 
    SDL_Surface* bmpSurface = SDL_LoadBMP("image.bmp");
    // Load a BMP image from a file path.
    if (bmpSurface == NULL) {
        SDL_DestroyRenderer(renderer);
        SDL_DestroyWindow(window);
        printf("SDL_LoadBMP Error: %s\n", SDL_GetError());
        SDL_Quit();
        return -1;
    }
    SDL_Rect destRect = { 00, bmpSurface->w, bmpSurface->h };
 
    SDL_Texture* texture = SDL_CreateTextureFromSurface(renderer, bmpSurface);
    // Create a texture from an existing surface.
    if (texture == NULL) {
        SDL_FreeSurface(bmpSurface);
        // Free an RGB surface.
        SDL_DestroyRenderer(renderer);
        SDL_DestroyWindow(window);
        printf("SDL_CreateTextureFromSurface Error: %s\n", SDL_GetError());
        SDL_Quit();
        return -1;
    }
    SDL_FreeSurface(bmpSurface);
 
    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);
        // Copy a portion of the texture to the current rendering target.
        SDL_RenderPresent(renderer);
    }
 
    SDL_DestroyTexture(texture);
    // Destroy the specified texture.
    SDL_DestroyRenderer(renderer);
    SDL_DestroyWindow(window);
    SDL_Quit();
 
    return 0;
}
 

 

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

 

실행하면 윈도우에 이미지가 렌더링 된다.

 

만약 그래픽 카드의 지원을 받을 수 없는 Embedded System 등에서 소프트웨어 방식의 렌더링만 사용할 수 있다면 Texture, Renderer 를 사용하지 않고 Window Surface, BMP Surface 를 사용해 직접 화면에 이미지를 출력할 수 있다.

 

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
#include <iostream>
#include "SDL.h"
 
#pragma comment(lib, "sdl2.lib")
#pragma comment(lib, "sdl2main.lib")
 
int main(int argc, char* argv[]) {
    if (SDL_Init(SDL_INIT_VIDEO) < 0) {
        printf("SDL Initialization Fail: %s\n", SDL_GetError());
        return -1;
    }
 
    SDL_Window* window = SDL_CreateWindow("SDL Test", SDL_WINDOWPOS_UNDEFINED,
        SDL_WINDOWPOS_UNDEFINED, 640480, SDL_WINDOW_RESIZABLE);
 
    if (!window) {
        printf("SDL_CreateWindow Error: %s\n", SDL_GetError());
        SDL_Quit();
        return -1;
    }
 
    SDL_Surface* windowSurface = SDL_GetWindowSurface(window);
    // Get the SDL surface associated with the window.
    // A new surface will be created with the optimal format for the window,
    // if necessary. This surface will be freed when the window is destroyed.
    // Do not free this surface.
    // This surface will be invalidated if the window is resized. After resizing
    // a window this function must be called again to return a valid surface.
    // You may not combine this with 3D or the rendering API on this window.
 
    SDL_Surface* bmpSurface = SDL_LoadBMP("image.bmp");
    if (bmpSurface == NULL) {
        SDL_DestroyWindow(window);
        printf("SDL_LoadBMP Error: %s\n", SDL_GetError());
        SDL_Quit();
        return -1;
    }
 
    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_BlitSurface(bmpSurface, NULL, windowSurface, NULL);
        // Use this function to perform a fast surface copy to a destination surface.
        SDL_UpdateWindowSurface(window);
        // Copy the window surface to the screen. This is the function you use to
        // reflect any changes to the surface on the screen.
    }
 
    SDL_FreeSurface(bmpSurface);
    SDL_DestroyWindow(window);
    SDL_Quit();
 
    return 0;
}
 

 

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

 

윈도우에 이미지가 출력된다.

 

 

 

이번엔 SDL_image를 이용해 JPG, PNG 등의 이미지를 출력해 보자. 아래 링크에서 SDL_image를 다운받고 적당히 설치한다.

SDL Libraries

 

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
#include <iostream>
#include "SDL.h"
#include "SDL_image.h"
 
#pragma comment(lib, "sdl2.lib")
#pragma comment(lib, "sdl2main.lib")
#pragma comment(lib, "sdl2_image.lib")
 
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, -1, SDL_RENDERER_ACCELERATED);
    // SDL_RENDERER_SOFTWARE: the renderer is a software fallback
    // SDL_RENDERER_ACCELERATED : the renderer uses hardware acceleration
    // SDL_RENDERER_PRESENTVSYNC : present is synchronized with the refresh rate
    // SDL_RENDERER_TARGETTEXTURE : the renderer supports rendering to texture
    // Note that providing no flags gives priority to available SDL_RENDERER_ACCELERATED
    // renderers.
 
    IMG_Init(IMG_INIT_JPG | IMG_INIT_PNG);
    // Initialize SDL_image.
 
    SDL_Surface* imageSurface = IMG_Load("image.png");
    // Load an image from a filesystem path into a software surface.
    SDL_Texture* texture = SDL_CreateTextureFromSurface(renderer, imageSurface);
    SDL_Rect destRect = { 00, imageSurface->w, imageSurface->h };
 
    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, 255, 255, 255, SDL_ALPHA_OPAQUE);
        //SDL_RenderClear(renderer);
        SDL_RenderCopy(renderer, texture, NULL&destRect);
        SDL_RenderPresent(renderer);
    }
 
    SDL_DestroyTexture(texture);
    SDL_FreeSurface(imageSurface);
    IMG_Quit();
    // Deinitialize SDL_image.
    SDL_DestroyRenderer(renderer);
    SDL_DestroyWindow(window);
    SDL_Quit();
 
    return 0;
}
 

 

코드를 입력하고 빌드한다.

 

실행하면 윈도우에 이미지가 렌더링 된다.

 

주석처리된 배경 처리 부분을 아래와 같이 해제하고 알파 채널이 있는 PNG 파일을 렌더링 해 보자.

 

SDL_SetRenderDrawColor(renderer, 0, 0, 255, SDL_ALPHA_OPAQUE);
SDL_RenderClear(renderer);

 

player.png
0.00MB

 

 

 

특별한 처리 없이도 알파 채널이 보존되어 투명한 부분이 잘 표현된다.

 

※ 참고

SDL_image API

 

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

픽셀 이미지(JPG, PNG)를 벡터(AI) 이미지로 바꿔보자.

 

일러스트레이터를 실행하고 변환할 이미지를 불러온다.

 

snoopy.jpg
0.09MB

 

 

Tracing으로 바꾼다.

 

변환할 이미지를 선택하고 Image Trace 탭에서 Auto-Color를 선택한다. 필요하다면 다른 옵션을 조절하고 Expand 버튼을 클릭한다.

 

픽셀 이미지가 벡터 이미지로 변환된다.

 

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

OpenGL 화면에 Pygame 이미지(스프라이트)를 렌더링 해 보자.

 

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
import pygame
from pygame.locals import *
 
from OpenGL.GL import *
from OpenGL.GLU import *
 
verticies = ((1-1-1), (11-1), (-11-1), (-1-1-1),
             (1-11), (111), (-1-11), (-111))
edges = ((0,1), (0,3), (0,4), (2,1), (2,3), (2,7), (6,3), (6,4),
         (6,7), (5,1), (5,4), (5,7))
 
def drawCube():
    glBegin(GL_LINES)
    for edge in edges:
        for vertex in edge:
            glVertex3fv(verticies[vertex])
    glEnd()
 
def drawImage(x, y):
    image = pygame.image.load("player.png").convert()
    image.set_colorkey(image.get_at((00)))
    image = pygame.transform.scale(image, (image.get_width()*1.5,
                                           image.get_height()*1.5))
    imageData = pygame.image.tostring(image, "RGBA"True)
    glWindowPos2d(x-image.get_width()/2, y-image.get_height()/2)
    glDrawPixels(image.get_width(), image.get_height(), GL_RGBA,
                 GL_UNSIGNED_BYTE, imageData)
 
pygame.init()
clock = pygame.time.Clock()
display = (640480)
pygame.display.set_mode(display, DOUBLEBUF | OPENGL)
 
glEnable(GL_BLEND)
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
 
gluPerspective(45, (display[0]/display[1]), 0.150.0)
glTranslatef(0.00.0-5)
 
run = True
while run:    
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            run = False
 
    glRotatef(1311)
    glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT)
    drawCube()
    drawImage(display[0]/2, display[1]/2)
    
    pygame.display.flip()
    clock.tick(60)
 
pygame.quit()
exit()
 

 

 

 

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

이미지 파일을 Drag & Drop으로 열어보자.

 

WinForm에 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
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;
 
namespace WindowsFormsApp1
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
 
            pictureBox1.SizeMode = PictureBoxSizeMode.Zoom;
            pictureBox1.AllowDrop = true;
            // 인텔리센스에는 pictureBox1.AllowDrop가 표시되지 않는다.
            // 하지만 사용은 가능하며 인텔리센스를 사용하고 싶다면 아래처럼
            // Control로 형변환해서 해도 된다.
            // ((Control)pictureBox1).AllowDrop = true;
        }
 
        private void pictureBox1_DragOver(object sender, DragEventArgs e)
        {
            if (e.Data.GetDataPresent(DataFormats.FileDrop))
            {
                e.Effect = DragDropEffects.Copy;
            }
        }
 
        private void pictureBox1_DragDrop(object sender, DragEventArgs e)
        {
            if (e.Data.GetDataPresent(DataFormats.FileDrop))
            {
                string[] files = (string[])e.Data.GetData(DataFormats.FileDrop);
                // 드롭된 파일들의 전체 경로명을 반환.
                foreach (string file in files)
                {
                    if (file.EndsWith("jpg"|| file.EndsWith("JPG"))
                    {
                        // JPG 파일만 디스플레이
                        pictureBox1.Image = System.Drawing.Image.FromFile(file);
                    }
                }
            }
        }
    }
}
 

 

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

 

프로그램을 실행하고 PictureBox 위로 JPG파일을 드래그 & 드롭한다.

 

이미지가 표시된다.

 

반응형
Posted by J-sean
: