반응형

raylib와 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
#include "raylib.h"
#include "opencv2/opencv.hpp"
 
using namespace cv;
 
int main(void)
{
    Mat cvimage = imread("palvin.jpg", IMREAD_COLOR_RGB);
 
   Image rayimage{};
    rayimage.width = cvimage.cols;
    rayimage.height = cvimage.rows;
    rayimage.format = PIXELFORMAT_UNCOMPRESSED_R8G8B8;
    rayimage.mipmaps = 1;
    rayimage.data = cvimage.ptr(); //(void*)(cvimage.data);
 
    const int screenWidth = 450;
    const int screenHeight = 500;
    InitWindow(screenWidth, screenHeight, "raylib example");
    SetTargetFPS(60);
 
    // NOTE: Textures MUST be loaded after Window initialization (OpenGL context is required)
    Texture2D texture = LoadTextureFromImage(rayimage);
 
    while (!WindowShouldClose())
    {
        BeginDrawing();
 
        ClearBackground(RAYWHITE);
 
        DrawTexture(texture, 1010, WHITE);
 
        EndDrawing();
    }
 
    //UnloadImage(rayimage);
    // LoadImage()로 로드한 이미지에만 사용.
    UnloadTexture(texture);
 
    CloseWindow();
 
    return 0;
}
 

 

raylib에서 texture는 윈도우가 생성된 이후 로드되어야 한다.

 

 

반응형
Posted by J-sean
:

보호되어 있는 글입니다.
내용을 보시려면 비밀번호를 입력하세요.

IP Camera ONVIF Protocol

OpenCV 2025. 3. 1. 10:24 |
반응형

 

ONVIF 프로토콜을 지원하는 IP 카메라를 사용해 보자.

(아래 내용은 저렴한 IP Camera를 사용한 예이다. HikVision Camera도 같은 코드로 사용 가능 하지만 저렴한 카메라와 다르게 에러나 노이즈가 거의 없다)

 

※ 참고

HikVision Camera RTSP Stream

HikVision FAQ

 

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
import cv2
 
#capture = cv2.VideoCapture(0, cv2.CAP_DSHOW)
# 노트북에 연결된 카메라는 대부분 cv2.CAP_DSHOW 옵션이 필요 없지만
# 데스크탑에 USB로 연결된 카메라 사용시에는 cv2.CAP_DSHOW 옵션이 필요 할 수 있다.
 
#cap = cv2.VideoCapture('rtsp://admin:@192.168.0.44')
# NVR 사용
 
cap = cv2.VideoCapture('rtsp://admin:123456@192.168.0.161:554/stream0')
# NVR 없이 카메라만 연결 Sub Stream
# 형식: rtsp://[ID]:[PW]@[IP주소]:[PORT번호]/[기타설정]
 
#cap = cv2.VideoCapture('rtsp://admin:123456@192.168.0.161:554/stream1')
# NVR 없이 카메라만 연결 Main Stream
 
# 카메라 Configure - Stream manager - Video Setting - Encoding Format 에서
# H265 로 설정하면 아래와 같은 에러 메세지가 발생한다.
# [hevc @ 0000024133eada80] PPS id out of range: 0
# H264 로 설정하면 괜찮다. 특정 카메라에서만 이런 현상이 나타날 수도 있다.
 
if not cap.isOpened():
    print("Not opened")
    exit()
 
while cv2.waitKey(1< 0:
    ret, frame = cap.read()
    if not ret:
        print("False returned")
        exit()
    cv2.imshow("video", frame)
 
cap.release()
cv2.destroyAllWindows()
 

 

HikVision Camera RTSP Stream Setting

RTSP without Authentication (NVR/DVR/IPC/Encoder)
rtsp://<IP address of device>:<RTSP port>/Streaming/channels/<channel number><stream number>
RTSP with Authentication
rtsp://<username>:<password>@<IP address of device>:<RTSP port>/Streaming/channels/<channel number><stream number>
NOTE: <stream number> represents main stream (01), or the sub stream (02)
Example:
rtsp://192.168.0.100:554/Streaming/channels/101 – get the main stream of the 1st channel
rtsp://192.168.0.100:554/Streaming/channels/102 – get the sub stream of the 1st channel

 

만약 Sub Stream(Stream0)이 아닌 Main Stream(Stream1)을 사용하면 영상은 큰 문제 없이 계속 출력되지만 아래와 같은 에러 메세지가 계속 나타난다. (Sub Stream에서도 종종 에러가 발생하기도 했다)

 

 

이런 에러 때문인지는 모르겠지만 OpenCV에서 rtsp를 이용해 출력한 영상은 카메라에서 자체 지원하는 웹뷰 등을 이용해 확인한 영상보다 노이즈가 심하다.

 

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
import queue
import threading
import cv2
 
q=queue.Queue()
stop = False
 
def Receive():
    print("start Reveiving")
    
    cap = cv2.VideoCapture('rtsp://admin:123456@192.168.0.161:554/stream0')
    if not cap.isOpened():
        print("Not opened")
        exit()
    
    global stop
    ret = True
    while ret and not stop:
        ret, frame = cap.read()
        lock.acquire()
        if not ret:
            print("False returned")
            continue
        q.put(frame)
        lock.release()
    
    cap.release()
    cv2.destroyAllWindows()
 
def Display():
     print("Start Displaying")
 
     global stop
     while cv2.waitKey(1< 0:
         lock.acquire()
         if not q.empty():
             frame=q.get()
             cv2.imshow("stream", frame)
         lock.release()
     stop = True
 
if __name__ == '__main__':
    lock = threading.Lock()
    
    t1 = threading.Thread(target = Receive)
    t2 = threading.Thread(target = Display)
    t1.start()
   t2.start()
 

 

영상을 받아오는 부분과 출력하는 부분을 다른 스레드로 구분하고 각각의 스레드에서 영상에 접근할때 충돌을 방지하기 위해 Primitive Lock을 사용해 봤지만 별 효과는 없다.

오히려 Main Stream(Stream1)에서 Sub Stream(Stream0)으로 바꿔 영상의 크기를 줄이는게 에러 확률을 크게 낮추었다.

 

 

카메라 Video Setting 에서 위와 같이 바꾸는 것도 에러 확률을 크게 낮추었다.

● Resolution: 1920X1080 => 1280X720

● Quality: Good => Worst 

Frame Rate, Max Bitrate는 별 영향이 없었다.

 

Packet Loss가 있어서 그런건지도 모르겠다.

stackoverflow

 

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

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

 

1) 파이썬 프로그램 데이터를 C 프로그램에 공유

mmap

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import time
import mmap
import cv2
 
frame = cv2.imread("image.jpg", cv2.IMREAD_COLOR)
h, w, c = frame.shape
buffer_size = h * w * c
 
mm = mmap.mmap(-1, buffer_size, "Local\\MySharedMemory")
 
try:
    mm.write(frame.tobytes())
 
    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
#include <opencv2/opencv.hpp>
#include <Windows.h>
 
int main() {
    int height = 495;
    int width = 396;
    int channels = 3;
    int buffersize = height * width * channels;
 
    byte* buffer = new byte[buffersize];
    memset(buffer, 0, buffersize);
 
    HANDLE hFMap = CreateFileMapping(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, 0, buffersize, L"MySharedMemory");
    if (hFMap == NULL)
        return -1;
 
    TCHAR* PtrInFile = (TCHAR*)MapViewOfFile(hFMap, FILE_MAP_ALL_ACCESS, 00, buffersize);
    if (PtrInFile == NULL)
        return -1;
 
    memcpy(buffer, PtrInFile, buffersize);
 
    cv::Mat image = cv::Mat(height, width, CV_8UC3, buffer);
    if (image.empty())
        return -1;
 
    // 사용할 Mat이 이미 존재한다면 아래처럼 memcpy()를 사용할 수도 있다.
    //cv::Mat image(height, width, CV_8UC3); // 이미 존재하는 Mat
    //memcpy(image.data, buffer, buffersize); // buffer 사용하지 않고 메모리에서 image.data로 직접 복사해도 된다.
 
    cv::imshow("image", image);
    cv::waitKey(0);
        
    UnmapViewOfFile(PtrInFile);
    CloseHandle(hFMap);
    delete[] buffer;
 
    return 0;
}
 

 

 

파이썬 프로그램을 먼저 실행하고 C 프로그램을 실행한다.

 

 

2) C 프로그램 데이터를 파이썬 프로그램에 공유

 

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
#include <opencv2/opencv.hpp>
#include <Windows.h>
 
int main() {
    cv::Mat image = cv::imread("image.jpg");
    if (image.empty())
        return -1;
 
    int height = image.rows;
    int width = image.cols;
    int channels = image.channels();
    int buffersize = height * width * channels;
 
    HANDLE hFMap = CreateFileMapping(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, 0, buffersize, L"MySharedMemory");
    if (hFMap == NULL)
        return -1;
 
    TCHAR* PtrInFile = (TCHAR*)MapViewOfFile(hFMap, FILE_MAP_ALL_ACCESS, 00, buffersize);
    if (PtrInFile == NULL)
        return -1;
 
    memcpy(PtrInFile, image.data, buffersize);
    // 버퍼를 사용하지 않고 바로 메모리에 데이터 복사.
        
    int a;
    std::cin >> a;
    // 대기
 
    UnmapViewOfFile(PtrInFile);
    CloseHandle(hFMap);
    
    return 0;
}
 

 

 

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.16 - [OpenCV] - C# and Python OpenCV Image Data Share (Memory Mapped File)

 

반응형
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
:

보호되어 있는 글입니다.
내용을 보시려면 비밀번호를 입력하세요.

[SDL] SDL OpenCV

SDL, raylib 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
: