์ฝ˜ํ…์ธ  ๋Œ€ํ‘œ ์ด๋ฏธ์ง€ - ๐ŸŽจ C์–ธ์–ด๋กœ ์‹œ์ž‘ํ•˜๋Š” ์ด๋ฏธ์ง€ ์ฒ˜๋ฆฌ์™€ ํ”ฝ์…€ ์กฐ์ž‘: ์ปดํ“จํ„ฐ ๋น„์ „์˜ ์ฒซ๊ฑธ์Œ

๐ŸŽจ C์–ธ์–ด๋กœ ์‹œ์ž‘ํ•˜๋Š” ์ด๋ฏธ์ง€ ์ฒ˜๋ฆฌ์™€ ํ”ฝ์…€ ์กฐ์ž‘: ์ปดํ“จํ„ฐ ๋น„์ „์˜ ์ฒซ๊ฑธ์Œ

ํ”ฝ์…€ ํ•˜๋‚˜ํ•˜๋‚˜๋ฅผ ์ง์ ‘ ๋งŒ์ง€๋ฉด์„œ ๋ฐฐ์šฐ๋Š” ์ปดํ“จํ„ฐ ๋น„์ „์˜ ์„ธ๊ณ„

๐Ÿ’ก ์ด๋ฏธ์ง€ ์ฒ˜๋ฆฌ๋ผ๊ณ  ํ•˜๋ฉด ๋ญ”๊ฐ€ ์—„์ฒญ ๋ณต์žกํ•˜๊ณ  ์–ด๋ ค์šด ๊ฒƒ ๊ฐ™์ง€? ํฌํ† ์ƒต ๊ฐ™์€ ํ”„๋กœ๊ทธ๋žจ๋งŒ ๋– ์˜ฌ๋ฆฌ๊ฑฐ๋‚˜, ์•„๋‹ˆ๋ฉด ์ตœ์‹  AI ๊ธฐ์ˆ ๋งŒ ์ƒ๊ฐํ•˜๊ฒŒ ๋˜๋Š”๋ฐ ๋ง์ด์•ผ. ํ•˜์ง€๋งŒ ์‚ฌ์‹ค ์ด๋ฏธ์ง€ ์ฒ˜๋ฆฌ์˜ ํ•ต์‹ฌ์€ ์ƒ๊ฐ๋ณด๋‹ค ๋‹จ์ˆœํ•ด. ๋ฐ”๋กœ ํ”ฝ์…€(Pixel)์ด๋ผ๋Š” ์ž‘์€ ์ ๋“ค์„ ํ•˜๋‚˜ํ•˜๋‚˜ ์กฐ์ž‘ํ•˜๋Š” ๊ฑฐ์•ผ! ๐ŸŽฏ

C์–ธ์–ด๋Š” ์ด๋Ÿฐ ์ €์ˆ˜์ค€(Low-level) ์ž‘์—…์„ ํ•˜๊ธฐ์— ์ •๋ง ์™„๋ฒฝํ•œ ์–ธ์–ด์•ผ. ๋ฉ”๋ชจ๋ฆฌ๋ฅผ ์ง์ ‘ ๋‹ค๋ฃฐ ์ˆ˜ ์žˆ๊ณ , ํฌ์ธํ„ฐ๋ฅผ ํ†ตํ•ด ๋ฐ์ดํ„ฐ์— ๋น ๋ฅด๊ฒŒ ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ๊ฑฐ๋“ . ์˜ค๋Š˜์€ C์–ธ์–ด๋กœ ์ด๋ฏธ์ง€๋ฅผ ์–ด๋–ป๊ฒŒ ์ฝ๊ณ , ํ”ฝ์…€์„ ์กฐ์ž‘ํ•˜๊ณ , ๊ฐ„๋‹จํ•œ ์ปดํ“จํ„ฐ ๋น„์ „ ๊ธฐ๋ฒ•๋“ค์„ ๊ตฌํ˜„ํ•˜๋Š”์ง€ ์นœ๊ตฌ์ฒ˜๋Ÿผ ํŽธํ•˜๊ฒŒ ์ด์•ผ๊ธฐํ•ด๋ณผ๊ฒŒ! ๐Ÿ˜Š

์›๋ณธ ์ด๋ฏธ์ง€ ์ฒ˜๋ฆฌ๋œ ์ด๋ฏธ์ง€ ํ”ฝ์…€ ๋‹จ์œ„ ์กฐ์ž‘ RGB ๊ฐ’ ๋ณ€๊ฒฝ, ํ•„ํ„ฐ ์ ์šฉ, ๋ณ€ํ™˜ R G B

๐Ÿ–ผ๏ธ ์ด๋ฏธ์ง€๋Š” ๊ฒฐ๊ตญ ์ˆซ์ž์˜ ๋ฐฐ์—ด์ด๋‹ค

์ž, ๋จผ์ € ๊ฐ€์žฅ ๊ธฐ๋ณธ์ ์ธ ๊ฐœ๋…๋ถ€ํ„ฐ ์งš๊ณ  ๋„˜์–ด๊ฐ€์ž. ์šฐ๋ฆฌ๊ฐ€ ํ™”๋ฉด์—์„œ ๋ณด๋Š” ์•„๋ฆ„๋‹ค์šด ์‚ฌ์ง„์ด๋‚˜ ๊ทธ๋ฆผ๋“ค์€ ์‚ฌ์‹ค ์—„์ฒญ๋‚˜๊ฒŒ ๋งŽ์€ ์ˆซ์ž๋“ค์˜ ์ง‘ํ•ฉ์ด์•ผ. ๋งˆ์น˜ ๋ ˆ๊ณ  ๋ธ”๋ก์œผ๋กœ ์„ฑ์„ ๋งŒ๋“œ๋Š” ๊ฒƒ์ฒ˜๋Ÿผ, ์ž‘์€ ํ”ฝ์…€๋“ค์ด ๋ชจ์—ฌ์„œ ํ•˜๋‚˜์˜ ์ด๋ฏธ์ง€๋ฅผ ๋งŒ๋“œ๋Š” ๊ฑฐ์ง€. ๐Ÿงฑ

๐Ÿ’ก ํ”ฝ์…€(Pixel)์ด๋ž€?

ํ”ฝ์…€์€ 'Picture Element'์˜ ์ค„์ž„๋ง์ด์•ผ. ๋””์ง€ํ„ธ ์ด๋ฏธ์ง€๋ฅผ ๊ตฌ์„ฑํ•˜๋Š” ๊ฐ€์žฅ ์ž‘์€ ๋‹จ์œ„๋กœ, ๊ฐ ํ”ฝ์…€์€ ํ•˜๋‚˜์˜ ์ƒ‰์ƒ ์ •๋ณด๋ฅผ ๊ฐ€์ง€๊ณ  ์žˆ์–ด. 1920x1080 ํ•ด์ƒ๋„์˜ ์ด๋ฏธ์ง€๋ผ๋ฉด? ๋ฌด๋ ค 2,073,600๊ฐœ์˜ ํ”ฝ์…€๋กœ ์ด๋ฃจ์–ด์ ธ ์žˆ๋‹ค๋Š” ๋œป์ด์•ผ! ๐Ÿ˜ฎ

RGB ์ƒ‰์ƒ ๋ชจ๋ธ์˜ ์ดํ•ด

๋Œ€๋ถ€๋ถ„์˜ ๋””์ง€ํ„ธ ์ด๋ฏธ์ง€๋Š” RGB ์ƒ‰์ƒ ๋ชจ๋ธ์„ ์‚ฌ์šฉํ•ด. Red(๋นจ๊ฐ•), Green(์ดˆ๋ก), Blue(ํŒŒ๋ž‘) ์„ธ ๊ฐ€์ง€ ์ƒ‰์˜ ์กฐํ•ฉ์œผ๋กœ ๋ชจ๋“  ์ƒ‰์„ ํ‘œํ˜„ํ•˜๋Š” ๊ฑฐ์•ผ. ๊ฐ ์ƒ‰์ƒ ์ฑ„๋„์€ ๋ณดํ†ต 0๋ถ€ํ„ฐ 255๊นŒ์ง€์˜ ๊ฐ’์„ ๊ฐ€์ ธ. (8๋น„ํŠธ = 2^8 = 256๊ฐ€์ง€)

๐Ÿ”ด Red ์ฑ„๋„

0 = ๋นจ๊ฐ• ์—†์Œ
255 = ์ตœ๋Œ€ ๋นจ๊ฐ•

๐ŸŸข Green ์ฑ„๋„

0 = ์ดˆ๋ก ์—†์Œ
255 = ์ตœ๋Œ€ ์ดˆ๋ก

๐Ÿ”ต Blue ์ฑ„๋„

0 = ํŒŒ๋ž‘ ์—†์Œ
255 = ์ตœ๋Œ€ ํŒŒ๋ž‘

์˜ˆ๋ฅผ ๋“ค์–ด๋ณผ๊นŒ? ์ˆœ์ˆ˜ํ•œ ๋นจ๊ฐ„์ƒ‰์€ (255, 0, 0), ํฐ์ƒ‰์€ (255, 255, 255), ๊ฒ€์€์ƒ‰์€ (0, 0, 0)์ด์•ผ. ๋…ธ๋ž€์ƒ‰์€? ๋นจ๊ฐ•๊ณผ ์ดˆ๋ก์„ ์„ž์œผ๋ฉด ๋˜๋‹ˆ๊นŒ (255, 255, 0)์ด์ง€! ๐ŸŽจ

๋ฉ”๋ชจ๋ฆฌ์—์„œ์˜ ์ด๋ฏธ์ง€ ํ‘œํ˜„

C์–ธ์–ด์—์„œ ์ด๋ฏธ์ง€๋ฅผ ๋‹ค๋ฃฐ ๋•Œ๋Š” ๋ณดํ†ต 2์ฐจ์› ๋ฐฐ์—ด์ด๋‚˜ 1์ฐจ์› ๋ฐฐ์—ด๋กœ ํ”ฝ์…€ ๋ฐ์ดํ„ฐ๋ฅผ ์ €์žฅํ•ด. ๊ฐ ํ”ฝ์…€์˜ RGB ๊ฐ’์„ ์ˆœ์ฐจ์ ์œผ๋กœ ๋ฉ”๋ชจ๋ฆฌ์— ๋ฐฐ์น˜ํ•˜๋Š” ๊ฑฐ์•ผ.

// ๊ฐ„๋‹จํ•œ ํ”ฝ์…€ ๊ตฌ์กฐ์ฒด ์ •์˜
typedef struct {
    unsigned char r;  // Red (0-255)
    unsigned char g;  // Green (0-255)
    unsigned char b;  // Blue (0-255)
} Pixel;

// ์ด๋ฏธ์ง€ ๊ตฌ์กฐ์ฒด ์ •์˜
typedef struct {
    int width;        // ์ด๋ฏธ์ง€ ๋„ˆ๋น„
    int height;       // ์ด๋ฏธ์ง€ ๋†’์ด
    Pixel *data;      // ํ”ฝ์…€ ๋ฐ์ดํ„ฐ ๋ฐฐ์—ด
} Image;

์ด๋ ‡๊ฒŒ ๊ตฌ์กฐ์ฒด๋กœ ์ •์˜ํ•˜๋ฉด ์ด๋ฏธ์ง€๋ฅผ ๋‹ค๋ฃจ๊ธฐ๊ฐ€ ํ›จ์”ฌ ํŽธํ•ด์ ธ. ๊ฐ ํ”ฝ์…€์— ์ ‘๊ทผํ•  ๋•Œ๋„ ๋ช…ํ™•ํ•˜๊ฒŒ RGB ๊ฐ’์„ ๊ตฌ๋ถ„ํ•  ์ˆ˜ ์žˆ๊ฑฐ๋“ ! ๐Ÿ‘

๐Ÿ“‚ ์ด๋ฏธ์ง€ ํŒŒ์ผ ์ฝ๊ธฐ์™€ ์“ฐ๊ธฐ

์ด์ œ ์‹ค์ œ๋กœ ์ด๋ฏธ์ง€ ํŒŒ์ผ์„ ์ฝ๊ณ  ์“ฐ๋Š” ๋ฐฉ๋ฒ•์„ ์•Œ์•„๋ณด์ž. C์–ธ์–ด์—์„œ๋Š” ์—ฌ๋Ÿฌ ์ด๋ฏธ์ง€ ํฌ๋งท์„ ๋‹ค๋ฃฐ ์ˆ˜ ์žˆ๋Š”๋ฐ, ๊ฐ€์žฅ ๊ฐ„๋‹จํ•œ BMP(Bitmap) ํฌ๋งท๋ถ€ํ„ฐ ์‹œ์ž‘ํ•˜๋Š” ๊ฒŒ ์ข‹์•„. BMP๋Š” ์••์ถ•์ด ์—†์–ด์„œ ๊ตฌ์กฐ๊ฐ€ ๋‹จ์ˆœํ•˜๊ฑฐ๋“ ! ๐Ÿ“

๐ŸŽฏ ์™œ BMP๋ถ€ํ„ฐ ์‹œ์ž‘ํ• ๊นŒ?

JPEG์ด๋‚˜ PNG ๊ฐ™์€ ํฌ๋งท์€ ์••์ถ• ์•Œ๊ณ ๋ฆฌ์ฆ˜์ด ๋ณต์žกํ•ด์„œ ์ฒ˜์Œ ๋ฐฐ์šฐ๊ธฐ์—” ์–ด๋ ค์›Œ. BMP๋Š” ํ”ฝ์…€ ๋ฐ์ดํ„ฐ๊ฐ€ ๊ฑฐ์˜ ๊ทธ๋Œ€๋กœ ์ €์žฅ๋˜์–ด ์žˆ์–ด์„œ ์ด๋ฏธ์ง€ ์ฒ˜๋ฆฌ์˜ ๊ธฐ๋ณธ ์›๋ฆฌ๋ฅผ ์ดํ•ดํ•˜๊ธฐ์— ์™„๋ฒฝํ•ด! ๋‚˜์ค‘์— ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ๋‹ค๋ฅธ ํฌ๋งท๋„ ์‰ฝ๊ฒŒ ๋‹ค๋ฃฐ ์ˆ˜ ์žˆ์–ด. ๐Ÿ˜Š

BMP ํŒŒ์ผ ๊ตฌ์กฐ ์ดํ•ดํ•˜๊ธฐ

BMP ํŒŒ์ผ์€ ํฌ๊ฒŒ ์„ธ ๋ถ€๋ถ„์œผ๋กœ ๋‚˜๋‰˜์–ด:

1. ํŒŒ์ผ ํ—ค๋” (14๋ฐ”์ดํŠธ)
ํŒŒ์ผ์˜ ๊ธฐ๋ณธ ์ •๋ณด๋ฅผ ๋‹ด๊ณ  ์žˆ์–ด. ํŒŒ์ผ ํƒ€์ž…, ํŒŒ์ผ ํฌ๊ธฐ, ๋ฐ์ดํ„ฐ ์‹œ์ž‘ ์œ„์น˜ ๋“ฑ์ด ์—ฌ๊ธฐ ์žˆ์ง€.

2. ์ •๋ณด ํ—ค๋” (40๋ฐ”์ดํŠธ ์ด์ƒ)
์ด๋ฏธ์ง€์˜ ์‹ค์ œ ์ •๋ณด๋“ค์ด์•ผ. ๋„ˆ๋น„, ๋†’์ด, ์ƒ‰์ƒ ๊นŠ์ด, ์••์ถ• ๋ฐฉ์‹ ๋“ฑ์„ ํฌํ•จํ•ด.

3. ํ”ฝ์…€ ๋ฐ์ดํ„ฐ
์‹ค์ œ ์ด๋ฏธ์ง€์˜ RGB ๊ฐ’๋“ค์ด ์ €์žฅ๋œ ๋ถ€๋ถ„์ด์•ผ. ์ฃผ์˜ํ•  ์ ์€ BMP๋Š” BGR ์ˆœ์„œ๋กœ ์ €์žฅ๋œ๋‹ค๋Š” ๊ฑฐ!

// BMP ํŒŒ์ผ ํ—ค๋” ๊ตฌ์กฐ์ฒด
#pragma pack(push, 1)  // ๊ตฌ์กฐ์ฒด ํŒจ๋”ฉ ์ œ๊ฑฐ
typedef struct {
    unsigned short type;        // ํŒŒ์ผ ํƒ€์ž… (BM = 0x4D42)
    unsigned int size;          // ํŒŒ์ผ ํฌ๊ธฐ
    unsigned short reserved1;   // ์˜ˆ์•ฝ๋จ
    unsigned short reserved2;   // ์˜ˆ์•ฝ๋จ
    unsigned int offset;        // ํ”ฝ์…€ ๋ฐ์ดํ„ฐ ์‹œ์ž‘ ์œ„์น˜
} BMPFileHeader;

// BMP ์ •๋ณด ํ—ค๋” ๊ตฌ์กฐ์ฒด
typedef struct {
    unsigned int size;          // ํ—ค๋” ํฌ๊ธฐ
    int width;                  // ์ด๋ฏธ์ง€ ๋„ˆ๋น„
    int height;                 // ์ด๋ฏธ์ง€ ๋†’์ด
    unsigned short planes;      // ์ƒ‰์ƒ ํ‰๋ฉด ์ˆ˜
    unsigned short bitCount;    // ํ”ฝ์…€๋‹น ๋น„ํŠธ ์ˆ˜
    unsigned int compression;   // ์••์ถ• ๋ฐฉ์‹
    unsigned int imageSize;     // ์ด๋ฏธ์ง€ ํฌ๊ธฐ
    int xPixelsPerMeter;        // ์ˆ˜ํ‰ ํ•ด์ƒ๋„
    int yPixelsPerMeter;        // ์ˆ˜์ง ํ•ด์ƒ๋„
    unsigned int colorsUsed;    // ์‚ฌ์šฉ๋œ ์ƒ‰์ƒ ์ˆ˜
    unsigned int colorsImportant; // ์ค‘์š”ํ•œ ์ƒ‰์ƒ ์ˆ˜
} BMPInfoHeader;
#pragma pack(pop)

๐Ÿ’ก #pragma pack์ด ์™œ ํ•„์š”ํ• ๊นŒ?

์ปดํŒŒ์ผ๋Ÿฌ๋Š” ๊ธฐ๋ณธ์ ์œผ๋กœ ๊ตฌ์กฐ์ฒด ๋ฉค๋ฒ„๋“ค์„ ๋ฉ”๋ชจ๋ฆฌ ์ •๋ ฌ์„ ์œ„ํ•ด ํŒจ๋”ฉ(padding)์„ ์ถ”๊ฐ€ํ•ด. ํ•˜์ง€๋งŒ BMP ํŒŒ์ผ ๊ตฌ์กฐ๋Š” ์ •ํ™•ํžˆ ๋ฐ”์ดํŠธ ๋‹จ์œ„๋กœ ๋งž์ถฐ์ ธ ์žˆ์–ด์„œ, ํŒจ๋”ฉ์ด ์žˆ์œผ๋ฉด ํŒŒ์ผ์„ ์ œ๋Œ€๋กœ ์ฝ์„ ์ˆ˜ ์—†์–ด. ๊ทธ๋ž˜์„œ #pragma pack(1)๋กœ ํŒจ๋”ฉ์„ ์ œ๊ฑฐํ•˜๋Š” ๊ฑฐ์•ผ! ๐ŸŽฏ

BMP ํŒŒ์ผ ์ฝ๊ธฐ ๊ตฌํ˜„

์ด์ œ ์‹ค์ œ๋กœ BMP ํŒŒ์ผ์„ ์ฝ๋Š” ํ•จ์ˆ˜๋ฅผ ๋งŒ๋“ค์–ด๋ณด์ž. ํŒŒ์ผ์„ ์—ด๊ณ , ํ—ค๋”๋ฅผ ์ฝ๊ณ , ํ”ฝ์…€ ๋ฐ์ดํ„ฐ๋ฅผ ๋ฉ”๋ชจ๋ฆฌ์— ๋กœ๋“œํ•˜๋Š” ๊ณผ์ •์ด์•ผ.

Image* loadBMP(const char* filename) {
    FILE* file = fopen(filename, "rb");
    if (!file) {
        printf("ํŒŒ์ผ์„ ์—ด ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค: %s\n", filename);
        return NULL;
    }
    
    // ํ—ค๋” ์ฝ๊ธฐ
    BMPFileHeader fileHeader;
    BMPInfoHeader infoHeader;
    
    fread(&fileHeader, sizeof(BMPFileHeader), 1, file);
    fread(&infoHeader, sizeof(BMPInfoHeader), 1, file);
    
    // BMP ํŒŒ์ผ์ธ์ง€ ํ™•์ธ
    if (fileHeader.type != 0x4D42) {
        printf("BMP ํŒŒ์ผ์ด ์•„๋‹™๋‹ˆ๋‹ค!\n");
        fclose(file);
        return NULL;
    }
    
    // 24๋น„ํŠธ BMP๋งŒ ์ง€์›
    if (infoHeader.bitCount != 24) {
        printf("24๋น„ํŠธ BMP๋งŒ ์ง€์›ํ•ฉ๋‹ˆ๋‹ค!\n");
        fclose(file);
        return NULL;
    }
    
    // ์ด๋ฏธ์ง€ ๊ตฌ์กฐ์ฒด ์ƒ์„ฑ
    Image* img = (Image*)malloc(sizeof(Image));
    img->width = infoHeader.width;
    img->height = abs(infoHeader.height);  // ๋†’์ด๊ฐ€ ์Œ์ˆ˜์ผ ์ˆ˜ ์žˆ์Œ
    img->data = (Pixel*)malloc(img->width * img->height * sizeof(Pixel));
    
    // ํ”ฝ์…€ ๋ฐ์ดํ„ฐ ์œ„์น˜๋กœ ์ด๋™
    fseek(file, fileHeader.offset, SEEK_SET);
    
    // BMP๋Š” ํ–‰์ด 4๋ฐ”์ดํŠธ ๋‹จ์œ„๋กœ ์ •๋ ฌ๋จ
    int rowSize = ((img->width * 3 + 3) / 4) * 4;
    unsigned char* rowData = (unsigned char*)malloc(rowSize);
    
    // ํ”ฝ์…€ ๋ฐ์ดํ„ฐ ์ฝ๊ธฐ (BMP๋Š” ์•„๋ž˜์—์„œ ์œ„๋กœ ์ €์žฅ๋จ)
    for (int y = img->height - 1; y >= 0; y--) {
        fread(rowData, 1, rowSize, file);
        for (int x = 0; x < img->width; x++) {
            int idx = y * img->width + x;
            // BMP๋Š” BGR ์ˆœ์„œ๋กœ ์ €์žฅ๋จ
            img->data[idx].b = rowData[x * 3];
            img->data[idx].g = rowData[x * 3 + 1];
            img->data[idx].r = rowData[x * 3 + 2];
        }
    }
    
    free(rowData);
    fclose(file);
    return img;
}

์ฝ”๋“œ๊ฐ€ ์ข€ ๊ธธ์ง€? ํ•˜์ง€๋งŒ ํ•˜๋‚˜ํ•˜๋‚˜ ๋œฏ์–ด๋ณด๋ฉด ์–ด๋ ต์ง€ ์•Š์•„. ํŒŒ์ผ์„ ์—ด๊ณ , ํ—ค๋” ์ •๋ณด๋ฅผ ์ฝ๊ณ , ํ”ฝ์…€ ๋ฐ์ดํ„ฐ๋ฅผ ์šฐ๋ฆฌ๊ฐ€ ๋งŒ๋“  Image ๊ตฌ์กฐ์ฒด์— ์ €์žฅํ•˜๋Š” ๊ฑฐ์•ผ. ํŠนํžˆ ์ฃผ์˜ํ•  ์ ์€:

โœ… BMP๋Š” BGR ์ˆœ์„œ๋กœ ์ €์žฅ๋˜๋‹ˆ๊นŒ RGB๋กœ ๋ณ€ํ™˜ํ•ด์•ผ ํ•ด
โœ… ๊ฐ ํ–‰์ด 4๋ฐ”์ดํŠธ ๋‹จ์œ„๋กœ ์ •๋ ฌ๋˜์–ด ์žˆ์–ด
โœ… ์ด๋ฏธ์ง€๊ฐ€ ์•„๋ž˜์—์„œ ์œ„๋กœ ์ €์žฅ๋˜์–ด ์žˆ์–ด (๋†’์ด๊ฐ€ ์–‘์ˆ˜์ผ ๋•Œ)

BMP ํŒŒ์ผ ์ €์žฅํ•˜๊ธฐ

์ด๋ฏธ์ง€๋ฅผ ์ฒ˜๋ฆฌํ•œ ํ›„์—๋Š” ๊ฒฐ๊ณผ๋ฅผ ํŒŒ์ผ๋กœ ์ €์žฅํ•ด์•ผ๊ฒ ์ง€? ์ €์žฅ ํ•จ์ˆ˜๋„ ๋งŒ๋“ค์–ด๋ณด์ž!

int saveBMP(const char* filename, Image* img) {
    FILE* file = fopen(filename, "wb");
    if (!file) {
        printf("ํŒŒ์ผ์„ ์ƒ์„ฑํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค: %s\n", filename);
        return 0;
    }
    
    // ํ–‰ ํฌ๊ธฐ ๊ณ„์‚ฐ (4๋ฐ”์ดํŠธ ์ •๋ ฌ)
    int rowSize = ((img->width * 3 + 3) / 4) * 4;
    int imageSize = rowSize * img->height;
    
    // ํŒŒ์ผ ํ—ค๋” ์ž‘์„ฑ
    BMPFileHeader fileHeader;
    fileHeader.type = 0x4D42;
    fileHeader.size = sizeof(BMPFileHeader) + sizeof(BMPInfoHeader) + imageSize;
    fileHeader.reserved1 = 0;
    fileHeader.reserved2 = 0;
    fileHeader.offset = sizeof(BMPFileHeader) + sizeof(BMPInfoHeader);
    
    // ์ •๋ณด ํ—ค๋” ์ž‘์„ฑ
    BMPInfoHeader infoHeader;
    infoHeader.size = sizeof(BMPInfoHeader);
    infoHeader.width = img->width;
    infoHeader.height = img->height;
    infoHeader.planes = 1;
    infoHeader.bitCount = 24;
    infoHeader.compression = 0;
    infoHeader.imageSize = imageSize;
    infoHeader.xPixelsPerMeter = 2835;  // 72 DPI
    infoHeader.yPixelsPerMeter = 2835;
    infoHeader.colorsUsed = 0;
    infoHeader.colorsImportant = 0;
    
    // ํ—ค๋” ์“ฐ๊ธฐ
    fwrite(&fileHeader, sizeof(BMPFileHeader), 1, file);
    fwrite(&infoHeader, sizeof(BMPInfoHeader), 1, file);
    
    // ํ”ฝ์…€ ๋ฐ์ดํ„ฐ ์“ฐ๊ธฐ
    unsigned char* rowData = (unsigned char*)calloc(rowSize, 1);
    
    for (int y = img->height - 1; y >= 0; y--) {
        for (int x = 0; x < img->width; x++) {
            int idx = y * img->width + x;
            rowData[x * 3] = img->data[idx].b;      // B
            rowData[x * 3 + 1] = img->data[idx].g;  // G
            rowData[x * 3 + 2] = img->data[idx].r;  // R
        }
        fwrite(rowData, 1, rowSize, file);
    }
    
    free(rowData);
    fclose(file);
    return 1;
}

์ €์žฅ ํ•จ์ˆ˜๋Š” ์ฝ๊ธฐ์˜ ์—ญ์ˆœ์ด์•ผ. ํ—ค๋”๋ฅผ ๋งŒ๋“ค๊ณ , ํ”ฝ์…€ ๋ฐ์ดํ„ฐ๋ฅผ BGR ์ˆœ์„œ๋กœ ๋ณ€ํ™˜ํ•ด์„œ ํŒŒ์ผ์— ์“ฐ๋Š” ๊ฑฐ์ง€. ์ด์ œ ์šฐ๋ฆฌ๋Š” ์ด๋ฏธ์ง€๋ฅผ ์ฝ๊ณ  ์“ธ ์ˆ˜ ์žˆ๊ฒŒ ๋์–ด! ๐ŸŽ‰

๐ŸŽจ ๊ธฐ๋ณธ์ ์ธ ํ”ฝ์…€ ์กฐ์ž‘ ๊ธฐ๋ฒ•๋“ค

์ด๋ฏธ์ง€๋ฅผ ์ฝ๊ณ  ์“ธ ์ˆ˜ ์žˆ๊ฒŒ ๋์œผ๋‹ˆ, ์ด์ œ ๋ณธ๊ฒฉ์ ์œผ๋กœ ํ”ฝ์…€์„ ์กฐ์ž‘ํ•ด๋ณด์ž! ๊ฐ€์žฅ ๊ธฐ๋ณธ์ ์ธ ๊ธฐ๋ฒ•๋“ค๋ถ€ํ„ฐ ํ•˜๋‚˜์”ฉ ์‚ดํŽด๋ณผ๊ฒŒ. ์ด๋Ÿฐ ๊ฐ„๋‹จํ•œ ์กฐ์ž‘๋“ค์ด ๋ชจ์—ฌ์„œ ๋ณต์žกํ•œ ์ด๋ฏธ์ง€ ์ฒ˜๋ฆฌ ์•Œ๊ณ ๋ฆฌ์ฆ˜์ด ๋˜๋Š” ๊ฑฐ์•ผ. ๐Ÿš€

๊ทธ๋ ˆ์ด์Šค์ผ€์ผ ๋ณ€ํ™˜ (Grayscale Conversion)

์ปฌ๋Ÿฌ ์ด๋ฏธ์ง€๋ฅผ ํ‘๋ฐฑ์œผ๋กœ ๋งŒ๋“œ๋Š” ๊ฑฐ์•ผ. ๊ฐ€์žฅ ๊ธฐ๋ณธ์ ์ด๋ฉด์„œ๋„ ๋งŽ์ด ์‚ฌ์šฉ๋˜๋Š” ๊ธฐ๋ฒ•์ด์ง€. RGB ์„ธ ๊ฐ’์„ ํ•˜๋‚˜์˜ ๋ฐ๊ธฐ ๊ฐ’์œผ๋กœ ๋ณ€ํ™˜ํ•˜๋Š” ๊ฑด๋ฐ, ๋‹จ์ˆœํžˆ ํ‰๊ท ์„ ๋‚ด๋ฉด ์•ˆ ๋ผ! ๐Ÿ‘€

๐Ÿค” ์™œ ํ‰๊ท ์„ ๋‚ด๋ฉด ์•ˆ ๋ ๊นŒ?

์‚ฌ๋žŒ์˜ ๋ˆˆ์€ ์ƒ‰์ƒ์— ๋”ฐ๋ผ ๋ฐ๊ธฐ๋ฅผ ๋‹ค๋ฅด๊ฒŒ ์ธ์‹ํ•ด. ์ดˆ๋ก์ƒ‰์„ ๊ฐ€์žฅ ๋ฐ๊ฒŒ ๋А๋ผ๊ณ , ํŒŒ๋ž€์ƒ‰์„ ๊ฐ€์žฅ ์–ด๋‘ก๊ฒŒ ๋А๋ผ์ง€. ๊ทธ๋ž˜์„œ ํ‘œ์ค€ ๊ฐ€์ค‘์น˜๋ฅผ ์‚ฌ์šฉํ•ด์•ผ ํ•ด:
Gray = 0.299 ร— R + 0.587 ร— G + 0.114 ร— B

void convertToGrayscale(Image* img) {
    for (int i = 0; i < img->width * img->height; i++) {
        // ๊ฐ€์ค‘ ํ‰๊ท  ๊ณ„์‚ฐ
        unsigned char gray = (unsigned char)(
            0.299 * img->data[i].r +
            0.587 * img->data[i].g +
            0.114 * img->data[i].b
        );
        
        // ๋ชจ๋“  ์ฑ„๋„์— ๊ฐ™์€ ๊ฐ’ ์ ์šฉ
        img->data[i].r = gray;
        img->data[i].g = gray;
        img->data[i].b = gray;
    }
}

์ด๋ ‡๊ฒŒ ํ•˜๋ฉด ์ž์—ฐ์Šค๋Ÿฌ์šด ํ‘๋ฐฑ ์ด๋ฏธ์ง€๊ฐ€ ๋งŒ๋“ค์–ด์ ธ! ๊ฐ„๋‹จํ•˜์ง€? ๐Ÿ˜Š

๋ฐ๊ธฐ ์กฐ์ ˆ (Brightness Adjustment)

์ด๋ฏธ์ง€๋ฅผ ๋ฐ๊ฒŒ ํ•˜๊ฑฐ๋‚˜ ์–ด๋‘ก๊ฒŒ ๋งŒ๋“œ๋Š” ๊ฑฐ์•ผ. ๊ฐ ํ”ฝ์…€์˜ RGB ๊ฐ’์— ์ผ์ •ํ•œ ๊ฐ’์„ ๋”ํ•˜๊ฑฐ๋‚˜ ๋นผ๋ฉด ๋ผ. ํ•˜์ง€๋งŒ ์กฐ์‹ฌํ•ด์•ผ ํ•  ๊ฒŒ ์žˆ์–ด!

void adjustBrightness(Image* img, int value) {
    for (int i = 0; i < img->width * img->height; i++) {
        // ๊ฐ ์ฑ„๋„์— ๊ฐ’์„ ๋”ํ•˜๋˜, 0-255 ๋ฒ”์œ„๋ฅผ ๋ฒ—์–ด๋‚˜์ง€ ์•Š๋„๋ก
        int r = img->data[i].r + value;
        int g = img->data[i].g + value;
        int b = img->data[i].b + value;
        
        // ํด๋ฆฌํ•‘(Clipping) - ๋ฒ”์œ„ ์ œํ•œ
        img->data[i].r = (r > 255) ? 255 : (r < 0) ? 0 : r;
        img->data[i].g = (g > 255) ? 255 : (g < 0) ? 0 : g;
        img->data[i].b = (b > 255) ? 255 : (b < 0) ? 0 : b;
    }
}

๐Ÿ’ก ํด๋ฆฌํ•‘(Clipping)์ด ์ค‘์š”ํ•œ ์ด์œ 

RGB ๊ฐ’์€ 0๋ถ€ํ„ฐ 255๊นŒ์ง€๋งŒ ํ‘œํ˜„ํ•  ์ˆ˜ ์žˆ์–ด. ๋งŒ์•ฝ ๊ณ„์‚ฐ ๊ฒฐ๊ณผ๊ฐ€ ์ด ๋ฒ”์œ„๋ฅผ ๋ฒ—์–ด๋‚˜๋ฉด? ์˜ค๋ฒ„ํ”Œ๋กœ์šฐ๊ฐ€ ๋ฐœ์ƒํ•ด์„œ ์ด์ƒํ•œ ์ƒ‰์ด ๋‚˜์˜ฌ ์ˆ˜ ์žˆ์–ด. ๊ทธ๋ž˜์„œ ํ•ญ์ƒ ๋ฒ”์œ„๋ฅผ ์ฒดํฌํ•˜๊ณ  ์ œํ•œํ•ด์•ผ ํ•ด! ์ด๊ฑธ ํด๋ฆฌํ•‘์ด๋ผ๊ณ  ๋ถˆ๋Ÿฌ. ๐Ÿ“

๋Œ€๋น„ ์กฐ์ ˆ (Contrast Adjustment)

๋Œ€๋น„๋Š” ๋ฐ์€ ๋ถ€๋ถ„๊ณผ ์–ด๋‘์šด ๋ถ€๋ถ„์˜ ์ฐจ์ด๋ฅผ ๋งํ•ด. ๋Œ€๋น„๋ฅผ ๋†’์ด๋ฉด ์ด๋ฏธ์ง€๊ฐ€ ๋” ์„ ๋ช…ํ•ด ๋ณด์ด๊ณ , ๋‚ฎ์ถ”๋ฉด ๋ถ€๋“œ๋Ÿฌ์›Œ ๋ณด์—ฌ.

void adjustContrast(Image* img, float factor) {
    // factor > 1.0: ๋Œ€๋น„ ์ฆ๊ฐ€
    // factor < 1.0: ๋Œ€๋น„ ๊ฐ์†Œ
    
    for (int i = 0; i < img->width * img->height; i++) {
        // 128์„ ์ค‘์‹ฌ์œผ๋กœ ํ™•๋Œ€/์ถ•์†Œ
        int r = (int)((img->data[i].r - 128) * factor + 128);
        int g = (int)((img->data[i].g - 128) * factor + 128);
        int b = (int)((img->data[i].b - 128) * factor + 128);
        
        // ํด๋ฆฌํ•‘
        img->data[i].r = (r > 255) ? 255 : (r < 0) ? 0 : r;
        img->data[i].g = (g > 255) ? 255 : (g < 0) ? 0 : g;
        img->data[i].b = (b > 255) ? 255 : (b < 0) ? 0 : b;
    }
}

128์„ ์ค‘์‹ฌ์œผ๋กœ ํ•˜๋Š” ์ด์œ ๋Š”? 0~255์˜ ์ค‘๊ฐ„๊ฐ’์ด 128์ด๊ฑฐ๋“ . ์ด ์ค‘์‹ฌ์ ์„ ๊ธฐ์ค€์œผ๋กœ ๋ฐ์€ ํ”ฝ์…€์€ ๋” ๋ฐ๊ฒŒ, ์–ด๋‘์šด ํ”ฝ์…€์€ ๋” ์–ด๋‘ก๊ฒŒ ๋งŒ๋“œ๋Š” ๊ฑฐ์•ผ! ๐ŸŽฏ

์ด๋ฏธ์ง€ ๋ฐ˜์ „ (Inversion)

๋„ค๊ฑฐํ‹ฐ๋ธŒ ํ•„๋ฆ„์ฒ˜๋Ÿผ ์ƒ‰์„ ๋ฐ˜์ „์‹œํ‚ค๋Š” ๊ฑฐ์•ผ. ์ •๋ง ๊ฐ„๋‹จํ•ด!

void invertImage(Image* img) {
    for (int i = 0; i < img->width * img->height; i++) {
        img->data[i].r = 255 - img->data[i].r;
        img->data[i].g = 255 - img->data[i].g;
        img->data[i].b = 255 - img->data[i].b;
    }
}

255์—์„œ ํ˜„์žฌ ๊ฐ’์„ ๋นผ๋ฉด ๋ผ. ํฐ์ƒ‰(255)์€ ๊ฒ€์€์ƒ‰(0)์ด ๋˜๊ณ , ๊ฒ€์€์ƒ‰์€ ํฐ์ƒ‰์ด ๋˜๋Š” ๊ฑฐ์ง€! ๐Ÿ”„

์ž„๊ณ„๊ฐ’ ์ฒ˜๋ฆฌ (Thresholding)

์ด๋ฏธ์ง€๋ฅผ ์™„์ „ํ•œ ํ‘๋ฐฑ(0 ๋˜๋Š” 255)์œผ๋กœ ๋งŒ๋“œ๋Š” ๊ฑฐ์•ผ. ํŠน์ • ๊ฐ’์„ ๊ธฐ์ค€์œผ๋กœ ๊ทธ๋ณด๋‹ค ๋ฐ์œผ๋ฉด ํฐ์ƒ‰, ์–ด๋‘์šฐ๋ฉด ๊ฒ€์€์ƒ‰์œผ๋กœ ๋งŒ๋“ค์–ด.

void threshold(Image* img, unsigned char thresholdValue) {
    // ๋จผ์ € ๊ทธ๋ ˆ์ด์Šค์ผ€์ผ๋กœ ๋ณ€ํ™˜
    convertToGrayscale(img);
    
    for (int i = 0; i < img->width * img->height; i++) {
        unsigned char value = (img->data[i].r >= thresholdValue) ? 255 : 0;
        img->data[i].r = value;
        img->data[i].g = value;
        img->data[i].b = value;
    }
}

์ด ๊ธฐ๋ฒ•์€ ๋ฌธ์ž ์ธ์‹์ด๋‚˜ ๊ฐ์ฒด ๋ถ„๋ฆฌ์— ์ •๋ง ์œ ์šฉํ•ด. ๋ฐฐ๊ฒฝ๊ณผ ์ „๊ฒฝ์„ ๋ช…ํ™•ํ•˜๊ฒŒ ๊ตฌ๋ถ„ํ•  ์ˆ˜ ์žˆ๊ฑฐ๋“ ! ๐Ÿ“

์ด๋ฏธ์ง€ ์ฒ˜๋ฆฌ ํŒŒ์ดํ”„๋ผ์ธ ์›๋ณธ ์ด๋ฏธ์ง€ RGB ํ”ฝ์…€ ๋ฐ์ดํ„ฐ ์ „์ฒ˜๋ฆฌ ๊ทธ๋ ˆ์ด์Šค์ผ€์ผ ๋…ธ์ด์ฆˆ ์ œ๊ฑฐ ํ•„ํ„ฐ ์ ์šฉ ๋ธ”๋Ÿฌ, ์ƒคํ”„๋‹ ์—ฃ์ง€ ๊ฒ€์ถœ ๊ฒฐ๊ณผ ์ถœ๋ ฅ ์ฒ˜๋ฆฌ๋œ ์ด๋ฏธ์ง€ ํ”ฝ์…€ ๋‹จ์œ„ ์ฒ˜๋ฆฌ ๋ฐ๊ธฐ ์กฐ์ ˆ ๋Œ€๋น„ ์กฐ์ ˆ ์ƒ‰์ƒ ๋ณ€ํ™˜ ์ž„๊ณ„๊ฐ’ ์ฒ˜๋ฆฌ ์˜์—ญ ๊ธฐ๋ฐ˜ ์ฒ˜๋ฆฌ ๋ธ”๋Ÿฌ๋ง ์ƒคํ”„๋‹ ์—ฃ์ง€ ๊ฒ€์ถœ ๋ชจํด๋กœ์ง€ ๊ณ ๊ธ‰ ์ฒ˜๋ฆฌ ํžˆ์Šคํ† ๊ทธ๋žจ ํ‰ํ™œํ™” ํŠน์ง• ์ถ”์ถœ ๊ฐ์ฒด ๊ฒ€์ถœ ํŒจํ„ด ์ธ์‹

๐Ÿ” ์ปจ๋ณผ๋ฃจ์…˜๊ณผ ํ•„ํ„ฐ๋ง์˜ ์„ธ๊ณ„

์ด์ œ ์ข€ ๋” ์žฌ๋ฏธ์žˆ๋Š” ๋ถ€๋ถ„์œผ๋กœ ๋“ค์–ด๊ฐ€๋ณด์ž! ์ปจ๋ณผ๋ฃจ์…˜(Convolution)์€ ์ด๋ฏธ์ง€ ์ฒ˜๋ฆฌ์—์„œ ์ •๋ง ์ค‘์š”ํ•œ ๊ฐœ๋…์ด์•ผ. ์ฃผ๋ณ€ ํ”ฝ์…€๋“ค์„ ๊ณ ๋ คํ•ด์„œ ์ƒˆ๋กœ์šด ๊ฐ’์„ ๊ณ„์‚ฐํ•˜๋Š” ๊ฑฐ์ง€. ์ด๊ฒŒ ๋ฐ”๋กœ ๋ธ”๋Ÿฌ, ์ƒคํ”„๋‹, ์—ฃ์ง€ ๊ฒ€์ถœ ๊ฐ™์€ ํšจ๊ณผ์˜ ํ•ต์‹ฌ์ด์•ผ! ๐ŸŽฏ

์ปค๋„(Kernel)์ด๋ž€?

์ปจ๋ณผ๋ฃจ์…˜์„ ์ดํ•ดํ•˜๋ ค๋ฉด ๋จผ์ € ์ปค๋„์„ ์•Œ์•„์•ผ ํ•ด. ์ปค๋„์€ ์ž‘์€ ํ–‰๋ ฌ์ด์•ผ. ๋ณดํ†ต 3ร—3์ด๋‚˜ 5ร—5 ํฌ๊ธฐ๋ฅผ ๋งŽ์ด ์จ. ์ด ์ปค๋„์„ ์ด๋ฏธ์ง€ ์œ„์—์„œ ์Šฌ๋ผ์ด๋”ฉํ•˜๋ฉด์„œ ๊ฐ ์œ„์น˜์—์„œ ์ฃผ๋ณ€ ํ”ฝ์…€๋“ค๊ณผ ๊ณฑํ•˜๊ณ  ๋”ํ•˜๋Š” ๊ฑฐ์•ผ.

๐Ÿงฎ ์ปจ๋ณผ๋ฃจ์…˜ ๊ณ„์‚ฐ ๊ณผ์ •

1. ์ปค๋„์„ ์ด๋ฏธ์ง€์˜ ํ•œ ํ”ฝ์…€ ์œ„์— ์˜ฌ๋ ค๋†“์•„
2. ์ปค๋„์˜ ๊ฐ ๊ฐ’๊ณผ ๋Œ€์‘ํ•˜๋Š” ํ”ฝ์…€ ๊ฐ’์„ ๊ณฑํ•ด
3. ๋ชจ๋“  ๊ณฑ์…ˆ ๊ฒฐ๊ณผ๋ฅผ ๋”ํ•ด
4. ๊ทธ ํ•ฉ์„ ์ƒˆ๋กœ์šด ํ”ฝ์…€ ๊ฐ’์œผ๋กœ ์‚ฌ์šฉํ•ด
5. ์ปค๋„์„ ํ•œ ์นธ์”ฉ ์ด๋™ํ•˜๋ฉด์„œ ๋ฐ˜๋ณตํ•ด

// ์ปค๋„ ๊ตฌ์กฐ์ฒด
typedef struct {
    int size;        // ์ปค๋„ ํฌ๊ธฐ (์˜ˆ: 3, 5, 7)
    float* data;     // ์ปค๋„ ๊ฐ’๋“ค
    float divisor;   // ์ •๊ทœํ™”๋ฅผ ์œ„ํ•œ ๋‚˜๋ˆ—์…ˆ ๊ฐ’
} Kernel;

// 3x3 ์ปค๋„ ์ƒ์„ฑ ํ•จ์ˆ˜
Kernel* createKernel(int size) {
    Kernel* kernel = (Kernel*)malloc(sizeof(Kernel));
    kernel->size = size;
    kernel->data = (float*)calloc(size * size, sizeof(float));
    kernel->divisor = 1.0f;
    return kernel;
}

// ์ปจ๋ณผ๋ฃจ์…˜ ์ ์šฉ ํ•จ์ˆ˜
Image* applyConvolution(Image* img, Kernel* kernel) {
    Image* result = (Image*)malloc(sizeof(Image));
    result->width = img->width;
    result->height = img->height;
    result->data = (Pixel*)malloc(img->width * img->height * sizeof(Pixel));
    
    int halfSize = kernel->size / 2;
    
    for (int y = 0; y < img->height; y++) {
        for (int x = 0; x < img->width; x++) {
            float sumR = 0, sumG = 0, sumB = 0;
            
            // ์ปค๋„ ์ ์šฉ
            for (int ky = -halfSize; ky <= halfSize; ky++) {
                for (int kx = -halfSize; kx <= halfSize; kx++) {
                    int px = x + kx;
                    int py = y + ky;
                    
                    // ๊ฒฝ๊ณ„ ์ฒ˜๋ฆฌ
                    if (px < 0) px = 0;
                    if (px >= img->width) px = img->width - 1;
                    if (py < 0) py = 0;
                    if (py >= img->height) py = img->height - 1;
                    
                    int idx = py * img->width + px;
                    int kidx = (ky + halfSize) * kernel->size + (kx + halfSize);
                    
                    sumR += img->data[idx].r * kernel->data[kidx];
                    sumG += img->data[idx].g * kernel->data[kidx];
                    sumB += img->data[idx].b * kernel->data[kidx];
                }
            }
            
            // ์ •๊ทœํ™” ๋ฐ ํด๋ฆฌํ•‘
            int outIdx = y * img->width + x;
            int r = (int)(sumR / kernel->divisor);
            int g = (int)(sumG / kernel->divisor);
            int b = (int)(sumB / kernel->divisor);
            
            result->data[outIdx].r = (r > 255) ? 255 : (r < 0) ? 0 : r;
            result->data[outIdx].g = (g > 255) ? 255 : (g < 0) ? 0 : g;
            result->data[outIdx].b = (b > 255) ? 255 : (b < 0) ? 0 : b;
        }
    }
    
    return result;
}

์ฝ”๋“œ๊ฐ€ ์ข€ ๋ณต์žกํ•ด ๋ณด์ด์ง€? ํ•˜์ง€๋งŒ ํ•ต์‹ฌ์€ ๊ฐ„๋‹จํ•ด. ๊ฐ ํ”ฝ์…€ ์ฃผ๋ณ€์˜ ๊ฐ’๋“ค์„ ์ปค๋„๊ณผ ๊ณฑํ•ด์„œ ๋”ํ•˜๋Š” ๊ฑฐ์•ผ. ๊ฒฝ๊ณ„ ์ฒ˜๋ฆฌ๋„ ์ค‘์š”ํ•œ๋ฐ, ์ด๋ฏธ์ง€ ๊ฐ€์žฅ์ž๋ฆฌ์—์„œ๋Š” ์ฃผ๋ณ€ ํ”ฝ์…€์ด ์—†์œผ๋‹ˆ๊นŒ ๊ฐ€์žฅ ๊ฐ€๊นŒ์šด ํ”ฝ์…€์„ ์‚ฌ์šฉํ•˜๋Š” ๊ฑฐ์ง€! ๐ŸŽจ

๋ธ”๋Ÿฌ(Blur) ํšจ๊ณผ - ์ด๋ฏธ์ง€ ๋ถ€๋“œ๋Ÿฝ๊ฒŒ ๋งŒ๋“ค๊ธฐ

๋ธ”๋Ÿฌ๋Š” ์ด๋ฏธ์ง€๋ฅผ ํ๋ฆฟํ•˜๊ฒŒ ๋งŒ๋“œ๋Š” ๊ฑฐ์•ผ. ์ฃผ๋ณ€ ํ”ฝ์…€๋“ค์˜ ํ‰๊ท ์„ ๋‚ด๋ฉด ๋ผ. ๊ฐ€์žฅ ๊ฐ„๋‹จํ•œ ๊ฑด ๋ฐ•์Šค ๋ธ”๋Ÿฌ(Box Blur)์•ผ.

// 3x3 ๋ฐ•์Šค ๋ธ”๋Ÿฌ ์ปค๋„
Kernel* createBoxBlurKernel() {
    Kernel* kernel = createKernel(3);
    
    // ๋ชจ๋“  ๊ฐ’์„ 1๋กœ ์„ค์ •
    for (int i = 0; i < 9; i++) {
        kernel->data[i] = 1.0f;
    }
    
    // ํ‰๊ท ์„ ๋‚ด๊ธฐ ์œ„ํ•ด 9๋กœ ๋‚˜๋ˆ”
    kernel->divisor = 9.0f;
    
    return kernel;
}

// ๊ฐ€์šฐ์‹œ์•ˆ ๋ธ”๋Ÿฌ ์ปค๋„ (๋” ์ž์—ฐ์Šค๋Ÿฌ์šด ๋ธ”๋Ÿฌ)
Kernel* createGaussianBlurKernel() {
    Kernel* kernel = createKernel(3);
    
    // ๊ฐ€์šฐ์‹œ์•ˆ ๋ถ„ํฌ๋ฅผ ๋”ฐ๋ฅด๋Š” ๊ฐ€์ค‘์น˜
    kernel->data[0] = 1.0f;  kernel->data[1] = 2.0f;  kernel->data[2] = 1.0f;
    kernel->data[3] = 2.0f;  kernel->data[4] = 4.0f;  kernel->data[5] = 2.0f;
    kernel->data[6] = 1.0f;  kernel->data[7] = 2.0f;  kernel->data[8] = 1.0f;
    
    kernel->divisor = 16.0f;  // ๋ชจ๋“  ๊ฐ’์˜ ํ•ฉ
    
    return kernel;
}

๐Ÿ’ก ๊ฐ€์šฐ์‹œ์•ˆ ๋ธ”๋Ÿฌ๊ฐ€ ๋” ์ข‹์€ ์ด์œ 

๋ฐ•์Šค ๋ธ”๋Ÿฌ๋Š” ๋ชจ๋“  ์ฃผ๋ณ€ ํ”ฝ์…€์„ ๋˜‘๊ฐ™์ด ์ทจ๊ธ‰ํ•ด. ํ•˜์ง€๋งŒ ๊ฐ€์šฐ์‹œ์•ˆ ๋ธ”๋Ÿฌ๋Š” ์ค‘์‹ฌ์— ๊ฐ€๊นŒ์šด ํ”ฝ์…€์— ๋” ํฐ ๊ฐ€์ค‘์น˜๋ฅผ ์ค˜. ๊ทธ๋ž˜์„œ ๋” ์ž์—ฐ์Šค๋Ÿฝ๊ณ  ๋ถ€๋“œ๋Ÿฌ์šด ๊ฒฐ๊ณผ๊ฐ€ ๋‚˜์™€! ์‚ฌ์ง„ ํŽธ์ง‘ ํ”„๋กœ๊ทธ๋žจ์—์„œ ์‚ฌ์šฉํ•˜๋Š” ๋ธ”๋Ÿฌ๊ฐ€ ๋ฐ”๋กœ ์ด๊ฑฐ์•ผ. ๐Ÿ“ธ

์ƒคํ”„๋‹(Sharpening) - ์ด๋ฏธ์ง€ ์„ ๋ช…ํ•˜๊ฒŒ ๋งŒ๋“ค๊ธฐ

์ƒคํ”„๋‹์€ ๋ธ”๋Ÿฌ์˜ ๋ฐ˜๋Œ€์•ผ. ์ด๋ฏธ์ง€์˜ ๋””ํ…Œ์ผ์„ ๊ฐ•์กฐํ•ด์„œ ๋” ์„ ๋ช…ํ•˜๊ฒŒ ๋งŒ๋“œ๋Š” ๊ฑฐ์ง€. ์—ฃ์ง€ ๋ถ€๋ถ„์„ ๊ฐ•ํ™”ํ•˜๋Š” ์›๋ฆฌ์•ผ!

// ์ƒคํ”„๋‹ ์ปค๋„
Kernel* createSharpenKernel() {
    Kernel* kernel = createKernel(3);
    
    // ์ค‘์‹ฌ์€ ๊ฐ•ํ•˜๊ฒŒ, ์ฃผ๋ณ€์€ ์•ฝํ•˜๊ฒŒ
    kernel->data[0] =  0.0f;  kernel->data[1] = -1.0f;  kernel->data[2] =  0.0f;
    kernel->data[3] = -1.0f;  kernel->data[4] =  5.0f;  kernel->data[5] = -1.0f;
    kernel->data[6] =  0.0f;  kernel->data[7] = -1.0f;  kernel->data[8] =  0.0f;
    
    kernel->divisor = 1.0f;
    
    return kernel;
}

// ๋” ๊ฐ•ํ•œ ์ƒคํ”„๋‹
Kernel* createStrongSharpenKernel() {
    Kernel* kernel = createKernel(3);
    
    kernel->data[0] = -1.0f;  kernel->data[1] = -1.0f;  kernel->data[2] = -1.0f;
    kernel->data[3] = -1.0f;  kernel->data[4] =  9.0f;  kernel->data[5] = -1.0f;
    kernel->data[6] = -1.0f;  kernel->data[7] = -1.0f;  kernel->data[8] = -1.0f;
    
    kernel->divisor = 1.0f;
    
    return kernel;
}

์ƒคํ”„๋‹ ์ปค๋„์˜ ํŠน์ง•์€ ์ค‘์‹ฌ ๊ฐ’์ด ํฌ๊ณ , ์ฃผ๋ณ€ ๊ฐ’์ด ์Œ์ˆ˜๋ผ๋Š” ๊ฑฐ์•ผ. ์ด๋ ‡๊ฒŒ ํ•˜๋ฉด ์ฃผ๋ณ€๊ณผ์˜ ์ฐจ์ด๊ฐ€ ๊ฐ•์กฐ๋˜๋ฉด์„œ ์—ฃ์ง€๊ฐ€ ์„ ๋ช…ํ•ด์ ธ! โœจ

์—ฃ์ง€ ๊ฒ€์ถœ(Edge Detection)

์—ฃ์ง€ ๊ฒ€์ถœ์€ ์ด๋ฏธ์ง€์—์„œ ๋ฌผ์ฒด์˜ ๊ฒฝ๊ณ„์„ ์„ ์ฐพ๋Š” ๊ฑฐ์•ผ. ์ปดํ“จํ„ฐ ๋น„์ „์—์„œ ์ •๋ง ์ค‘์š”ํ•œ ๊ธฐ๋ฒ•์ด์ง€! ๊ฐ€์žฅ ์œ ๋ช…ํ•œ ๊ฑด ์†Œ๋ฒจ(Sobel) ํ•„ํ„ฐ์•ผ.

// ์†Œ๋ฒจ X ๋ฐฉํ–ฅ ์ปค๋„ (์ˆ˜์ง ์—ฃ์ง€ ๊ฒ€์ถœ)
Kernel* createSobelXKernel() {
    Kernel* kernel = createKernel(3);
    
    kernel->data[0] = -1.0f;  kernel->data[1] = 0.0f;  kernel->data[2] = 1.0f;
    kernel->data[3] = -2.0f;  kernel->data[4] = 0.0f;  kernel->data[5] = 2.0f;
    kernel->data[6] = -1.0f;  kernel->data[7] = 0.0f;  kernel->data[8] = 1.0f;
    
    kernel->divisor = 1.0f;
    
    return kernel;
}

// ์†Œ๋ฒจ Y ๋ฐฉํ–ฅ ์ปค๋„ (์ˆ˜ํ‰ ์—ฃ์ง€ ๊ฒ€์ถœ)
Kernel* createSobelYKernel() {
    Kernel* kernel = createKernel(3);
    
    kernel->data[0] = -1.0f;  kernel->data[1] = -2.0f;  kernel->data[2] = -1.0f;
    kernel->data[3] =  0.0f;  kernel->data[4] =  0.0f;  kernel->data[5] =  0.0f;
    kernel->data[6] =  1.0f;  kernel->data[7] =  2.0f;  kernel->data[8] =  1.0f;
    
    kernel->divisor = 1.0f;
    
    return kernel;
}

// ์†Œ๋ฒจ ์—ฃ์ง€ ๊ฒ€์ถœ ์ ์šฉ
Image* applySobelEdgeDetection(Image* img) {
    // ๋จผ์ € ๊ทธ๋ ˆ์ด์Šค์ผ€์ผ๋กœ ๋ณ€ํ™˜
    convertToGrayscale(img);
    
    // X, Y ๋ฐฉํ–ฅ ์ปค๋„ ์ƒ์„ฑ
    Kernel* sobelX = createSobelXKernel();
    Kernel* sobelY = createSobelYKernel();
    
    // ๊ฐ ๋ฐฉํ–ฅ์œผ๋กœ ์ปจ๋ณผ๋ฃจ์…˜ ์ ์šฉ
    Image* gradX = applyConvolution(img, sobelX);
    Image* gradY = applyConvolution(img, sobelY);
    
    // ๊ฒฐ๊ณผ ์ด๋ฏธ์ง€ ์ƒ์„ฑ
    Image* result = (Image*)malloc(sizeof(Image));
    result->width = img->width;
    result->height = img->height;
    result->data = (Pixel*)malloc(img->width * img->height * sizeof(Pixel));
    
    // ๊ทธ๋ž˜๋””์–ธํŠธ ํฌ๊ธฐ ๊ณ„์‚ฐ
    for (int i = 0; i < img->width * img->height; i++) {
        float gx = gradX->data[i].r;
        float gy = gradY->data[i].r;
        
        // ๊ทธ๋ž˜๋””์–ธํŠธ ํฌ๊ธฐ = sqrt(gx^2 + gy^2)
        int magnitude = (int)sqrt(gx * gx + gy * gy);
        
        magnitude = (magnitude > 255) ? 255 : magnitude;
        
        result->data[i].r = magnitude;
        result->data[i].g = magnitude;
        result->data[i].b = magnitude;
    }
    
    // ๋ฉ”๋ชจ๋ฆฌ ์ •๋ฆฌ
    free(gradX->data);
    free(gradX);
    free(gradY->data);
    free(gradY);
    free(sobelX->data);
    free(sobelX);
    free(sobelY->data);
    free(sobelY);
    
    return result;
}

์†Œ๋ฒจ ํ•„ํ„ฐ๋Š” X ๋ฐฉํ–ฅ๊ณผ Y ๋ฐฉํ–ฅ์˜ ๋ณ€ํ™”๋ฅผ ๊ฐ๊ฐ ๊ณ„์‚ฐํ•œ ๋‹ค์Œ, ๋‘ ๊ฐ’์„ ํ•ฉ์ณ์„œ ์ตœ์ข… ์—ฃ์ง€๋ฅผ ์ฐพ์•„. ์ˆ˜ํ•™์ ์œผ๋กœ๋Š” ๊ทธ๋ž˜๋””์–ธํŠธ(๊ธฐ์šธ๊ธฐ)๋ฅผ ๊ณ„์‚ฐํ•˜๋Š” ๊ฑฐ์•ผ! ๐Ÿ“

๐ŸŽ“ ์žฌ๋Šฅ๋„ท์—์„œ ๋” ๋ฐฐ์šฐ๊ธฐ

์ด๋ฏธ์ง€ ์ฒ˜๋ฆฌ๋Š” ์ •๋ง ๊นŠ๊ณ  ๋„“์€ ๋ถ„์•ผ์•ผ. ์—ฌ๊ธฐ์„œ ๋ฐฐ์šด ๊ธฐ์ดˆ ๊ธฐ๋ฒ•๋“ค์€ ์‹œ์ž‘์— ๋ถˆ๊ณผํ•ด! ์žฌ๋Šฅ๋„ท์—์„œ๋Š” ๋” ๊ณ ๊ธ‰ ๊ธฐ๋ฒ•๋“ค, ์˜ˆ๋ฅผ ๋“ค์–ด ํžˆ์Šคํ† ๊ทธ๋žจ ํ‰ํ™œํ™”, ๋ชจํด๋กœ์ง€ ์—ฐ์‚ฐ, ํŠน์ง• ์ถ”์ถœ ๊ฐ™์€ ๊ฒƒ๋“ค๋„ ๋ฐฐ์šธ ์ˆ˜ ์žˆ์–ด. ์ „๋ฌธ๊ฐ€๋“ค์˜ ์‹ค์ „ ๋…ธํ•˜์šฐ๋ฅผ ์ง์ ‘ ๋“ค์–ด๋ณด๋Š” ๊ฒƒ๋„ ์ข‹์€ ๋ฐฉ๋ฒ•์ด์ง€! ๐Ÿš€

๐ŸŽญ ๊ณ ๊ธ‰ ์ด๋ฏธ์ง€ ์ฒ˜๋ฆฌ ๊ธฐ๋ฒ•๋“ค

๊ธฐ๋ณธ๊ธฐ๋ฅผ ๋‹ค์กŒ์œผ๋‹ˆ ์ด์ œ ์ข€ ๋” ๊ณ ๊ธ‰ ๊ธฐ๋ฒ•๋“ค์„ ์‚ดํŽด๋ณด์ž! ์ด๋Ÿฐ ๊ธฐ๋ฒ•๋“ค์€ ์‹ค์ œ ์ปดํ“จํ„ฐ ๋น„์ „ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์—์„œ ๋งŽ์ด ์‚ฌ์šฉ๋ผ. ๐Ÿ˜Ž

ํžˆ์Šคํ† ๊ทธ๋žจ ํ‰ํ™œํ™” (Histogram Equalization)

ํžˆ์Šคํ† ๊ทธ๋žจ์€ ์ด๋ฏธ์ง€์—์„œ ๊ฐ ๋ฐ๊ธฐ ๊ฐ’์ด ์–ผ๋งˆ๋‚˜ ๋งŽ์ด ๋‚˜ํƒ€๋‚˜๋Š”์ง€๋ฅผ ๋ณด์—ฌ์ฃผ๋Š” ๊ทธ๋ž˜ํ”„์•ผ. ํžˆ์Šคํ† ๊ทธ๋žจ ํ‰ํ™œํ™”๋Š” ์ด ๋ถ„ํฌ๋ฅผ ๊ท ๋“ฑํ•˜๊ฒŒ ๋งŒ๋“ค์–ด์„œ ๋Œ€๋น„๋ฅผ ๊ฐœ์„ ํ•˜๋Š” ๊ธฐ๋ฒ•์ด์•ผ!

// ํžˆ์Šคํ† ๊ทธ๋žจ ๊ณ„์‚ฐ
void calculateHistogram(Image* img, int histogram[256]) {
    // ์ดˆ๊ธฐํ™”
    for (int i = 0; i < 256; i++) {
        histogram[i] = 0;
    }
    
    // ๊ทธ๋ ˆ์ด์Šค์ผ€์ผ ๊ฐ’ ์นด์šดํŠธ
    for (int i = 0; i < img->width * img->height; i++) {
        unsigned char gray = (unsigned char)(
            0.299 * img->data[i].r +
            0.587 * img->data[i].g +
            0.114 * img->data[i].b
        );
        histogram[gray]++;
    }
}

// ํžˆ์Šคํ† ๊ทธ๋žจ ํ‰ํ™œํ™”
void equalizeHistogram(Image* img) {
    int histogram[256];
    int cdf[256];  // ๋ˆ„์  ๋ถ„ํฌ ํ•จ์ˆ˜
    
    // ๊ทธ๋ ˆ์ด์Šค์ผ€์ผ๋กœ ๋ณ€ํ™˜
    convertToGrayscale(img);
    
    // ํžˆ์Šคํ† ๊ทธ๋žจ ๊ณ„์‚ฐ
    calculateHistogram(img, histogram);
    
    // ๋ˆ„์  ๋ถ„ํฌ ํ•จ์ˆ˜ ๊ณ„์‚ฐ
    cdf[0] = histogram[0];
    for (int i = 1; i < 256; i++) {
        cdf[i] = cdf[i-1] + histogram[i];
    }
    
    // ์ •๊ทœํ™”
    int totalPixels = img->width * img->height;
    int cdfMin = cdf[0];
    
    // ์ƒˆ๋กœ์šด ๊ฐ’ ๊ณ„์‚ฐ ๋ฐ ์ ์šฉ
    for (int i = 0; i < totalPixels; i++) {
        unsigned char oldValue = img->data[i].r;
        unsigned char newValue = (unsigned char)(
            ((float)(cdf[oldValue] - cdfMin) / (totalPixels - cdfMin)) * 255
        );
        
        img->data[i].r = newValue;
        img->data[i].g = newValue;
        img->data[i].b = newValue;
    }
}

๐Ÿค” ํžˆ์Šคํ† ๊ทธ๋žจ ํ‰ํ™œํ™”๋Š” ์–ธ์ œ ์œ ์šฉํ• ๊นŒ?

์–ด๋‘์šด ๊ณณ์—์„œ ์ฐ์€ ์‚ฌ์ง„์ด๋‚˜ ๋Œ€๋น„๊ฐ€ ๋‚ฎ์€ ์ด๋ฏธ์ง€๋ฅผ ๊ฐœ์„ ํ•  ๋•Œ ์ •๋ง ํšจ๊ณผ์ ์ด์•ผ! ์˜๋ฃŒ ์˜์ƒ ๋ถ„์„์ด๋‚˜ ๋ณด์•ˆ ์นด๋ฉ”๋ผ ์˜์ƒ ์ฒ˜๋ฆฌ์—์„œ ๋งŽ์ด ์‚ฌ์šฉ๋ผ. ์ˆจ๊ฒจ์ง„ ๋””ํ…Œ์ผ์„ ๋“œ๋Ÿฌ๋‚ด๋Š” ๋งˆ๋ฒ• ๊ฐ™์€ ๊ธฐ๋ฒ•์ด์ง€! โœจ

์ด๋ฏธ์ง€ ํšŒ์ „ (Rotation)

์ด๋ฏธ์ง€๋ฅผ ํšŒ์ „์‹œํ‚ค๋Š” ๊ฑด ์ƒ๊ฐ๋ณด๋‹ค ๋ณต์žกํ•ด. ๋‹จ์ˆœํžˆ ํ”ฝ์…€ ์œ„์น˜๋งŒ ๋ฐ”๊พธ๋Š” ๊ฒŒ ์•„๋‹ˆ๋ผ ์ขŒํ‘œ ๋ณ€ํ™˜์„ ํ•ด์•ผ ํ•˜๊ฑฐ๋“ !

// ์ด๋ฏธ์ง€ ํšŒ์ „ (๊ฐ๋„๋Š” ๋ผ๋””์•ˆ)
Image* rotateImage(Image* img, float angle) {
    float cosA = cos(angle);
    float sinA = sin(angle);
    
    // ํšŒ์ „ ํ›„ ์ด๋ฏธ์ง€ ํฌ๊ธฐ ๊ณ„์‚ฐ
    int newWidth = (int)(fabs(img->width * cosA) + fabs(img->height * sinA));
    int newHeight = (int)(fabs(img->width * sinA) + fabs(img->height * cosA));
    
    // ๊ฒฐ๊ณผ ์ด๋ฏธ์ง€ ์ƒ์„ฑ
    Image* result = (Image*)malloc(sizeof(Image));
    result->width = newWidth;
    result->height = newHeight;
    result->data = (Pixel*)calloc(newWidth * newHeight, sizeof(Pixel));
    
    // ์ค‘์‹ฌ์  ๊ณ„์‚ฐ
    float centerX = img->width / 2.0f;
    float centerY = img->height / 2.0f;
    float newCenterX = newWidth / 2.0f;
    float newCenterY = newHeight / 2.0f;
    
    // ์—ญ๋ณ€ํ™˜์„ ์‚ฌ์šฉํ•œ ํšŒ์ „
    for (int y = 0; y < newHeight; y++) {
        for (int x = 0; x < newWidth; x++) {
            // ์ƒˆ ์ขŒํ‘œ๋ฅผ ์ค‘์‹ฌ ๊ธฐ์ค€์œผ๋กœ ๋ณ€ํ™˜
            float dx = x - newCenterX;
            float dy = y - newCenterY;
            
            // ์—ญํšŒ์ „ ์ ์šฉ
            float srcX = dx * cosA + dy * sinA + centerX;
            float srcY = -dx * sinA + dy * cosA + centerY;
            
            // ์›๋ณธ ์ด๋ฏธ์ง€ ๋ฒ”์œ„ ๋‚ด์— ์žˆ๋Š”์ง€ ํ™•์ธ
            if (srcX >= 0 && srcX < img->width - 1 &&
                srcY >= 0 && srcY < img->height - 1) {
                
                // ๋ฐ”์ด๋ฆฌ๋‹ˆ์–ด ๋ณด๊ฐ„
                int x0 = (int)srcX;
                int y0 = (int)srcY;
                int x1 = x0 + 1;
                int y1 = y0 + 1;
                
                float fx = srcX - x0;
                float fy = srcY - y0;
                
                int idx00 = y0 * img->width + x0;
                int idx01 = y0 * img->width + x1;
                int idx10 = y1 * img->width + x0;
                int idx11 = y1 * img->width + x1;
                
                int outIdx = y * newWidth + x;
                
                // ๊ฐ ์ฑ„๋„์— ๋Œ€ํ•ด ๋ณด๊ฐ„
                result->data[outIdx].r = (unsigned char)(
                    (1 - fx) * (1 - fy) * img->data[idx00].r +
                    fx * (1 - fy) * img->data[idx01].r +
                    (1 - fx) * fy * img->data[idx10].r +
                    fx * fy * img->data[idx11].r
                );
                
                result->data[outIdx].g = (unsigned char)(
                    (1 - fx) * (1 - fy) * img->data[idx00].g +
                    fx * (1 - fy) * img->data[idx01].g +
                    (1 - fx) * fy * img->data[idx10].g +
                    fx * fy * img->data[idx11].g
                );
                
                result->data[outIdx].b = (unsigned char)(
                    (1 - fx) * (1 - fy) * img->data[idx00].b +
                    fx * (1 - fy) * img->data[idx01].b +
                    (1 - fx) * fy * img->data[idx10].b +
                    fx * fy * img->data[idx11].b
                );
            }
        }
    }
    
    return result;
}

๐Ÿ’ก ๋ฐ”์ด๋ฆฌ๋‹ˆ์–ด ๋ณด๊ฐ„(Bilinear Interpolation)์ด๋ž€?

ํšŒ์ „ํ•˜๋ฉด ํ”ฝ์…€์ด ์ •์ˆ˜ ์ขŒํ‘œ์— ๋”ฑ ๋–จ์–ด์ง€์ง€ ์•Š์•„. ์˜ˆ๋ฅผ ๋“ค์–ด (3.7, 5.2) ๊ฐ™์€ ์œ„์น˜์— ๋งคํ•‘๋  ์ˆ˜ ์žˆ์ง€. ์ด๋Ÿด ๋•Œ ์ฃผ๋ณ€ 4๊ฐœ ํ”ฝ์…€์˜ ๊ฐ’์„ ๊ฑฐ๋ฆฌ์— ๋”ฐ๋ผ ๊ฐ€์ค‘ ํ‰๊ท ์„ ๋‚ด๋Š” ๊ฒŒ ๋ฐ”์ด๋ฆฌ๋‹ˆ์–ด ๋ณด๊ฐ„์ด์•ผ. ์ด๋ ‡๊ฒŒ ํ•˜๋ฉด ํšŒ์ „๋œ ์ด๋ฏธ์ง€๊ฐ€ ํ›จ์”ฌ ๋ถ€๋“œ๋Ÿฝ๊ณ  ์ž์—ฐ์Šค๋Ÿฌ์›Œ์ ธ! ๐ŸŽจ

์ด๋ฏธ์ง€ ํฌ๊ธฐ ์กฐ์ ˆ (Scaling)

์ด๋ฏธ์ง€๋ฅผ ํ™•๋Œ€ํ•˜๊ฑฐ๋‚˜ ์ถ•์†Œํ•˜๋Š” ๊ฒƒ๋„ ์ž์ฃผ ํ•„์š”ํ•œ ์ž‘์—…์ด์•ผ. ๋‹จ์ˆœํžˆ ํ”ฝ์…€์„ ๋ณต์‚ฌํ•˜๊ฑฐ๋‚˜ ๊ฑด๋„ˆ๋›ฐ๋ฉด ํ’ˆ์งˆ์ด ๋–จ์–ด์ง€๋‹ˆ๊นŒ, ๋ณด๊ฐ„์„ ์‚ฌ์šฉํ•ด์•ผ ํ•ด!

// ๋ฐ”์ด๋ฆฌ๋‹ˆ์–ด ๋ณด๊ฐ„์„ ์‚ฌ์šฉํ•œ ์ด๋ฏธ์ง€ ํฌ๊ธฐ ์กฐ์ ˆ
Image* resizeImage(Image* img, int newWidth, int newHeight) {
    Image* result = (Image*)malloc(sizeof(Image));
    result->width = newWidth;
    result->height = newHeight;
    result->data = (Pixel*)malloc(newWidth * newHeight * sizeof(Pixel));
    
    float xRatio = (float)img->width / newWidth;
    float yRatio = (float)img->height / newHeight;
    
    for (int y = 0; y < newHeight; y++) {
        for (int x = 0; x < newWidth; x++) {
            float srcX = x * xRatio;
            float srcY = y * yRatio;
            
            int x0 = (int)srcX;
            int y0 = (int)srcY;
            int x1 = (x0 + 1 < img->width) ? x0 + 1 : x0;
            int y1 = (y0 + 1 < img->height) ? y0 + 1 : y0;
            
            float fx = srcX - x0;
            float fy = srcY - y0;
            
            int idx00 = y0 * img->width + x0;
            int idx01 = y0 * img->width + x1;
            int idx10 = y1 * img->width + x0;
            int idx11 = y1 * img->width + x1;
            
            int outIdx = y * newWidth + x;
            
            result->data[outIdx].r = (unsigned char)(
                (1 - fx) * (1 - fy) * img->data[idx00].r +
                fx * (1 - fy) * img->data[idx01].r +
                (1 - fx) * fy * img->data[idx10].r +
                fx * fy * img->data[idx11].r
            );
            
            result->data[outIdx].g = (unsigned char)(
                (1 - fx) * (1 - fy) * img->data[idx00].g +
                fx * (1 - fy) * img->data[idx01].g +
                (1 - fx) * fy * img->data[idx10].g +
                fx * fy * img->data[idx11].g
            );
            
            result->data[outIdx].b = (unsigned char)(
                (1 - fx) * (1 - fy) * img->data[idx00].b +
                fx * (1 - fy) * img->data[idx01].b +
                (1 - fx) * fy * img->data[idx10].b +
                fx * fy * img->data[idx11].b
            );
        }
    }
    
    return result;
}

์ด ์ฝ”๋“œ๋Š” ํšŒ์ „๊ณผ ๋น„์Šทํ•œ ์›๋ฆฌ์•ผ. ์ƒˆ ์ด๋ฏธ์ง€์˜ ๊ฐ ํ”ฝ์…€์ด ์›๋ณธ ์ด๋ฏธ์ง€์˜ ์–ด๋””์— ํ•ด๋‹นํ•˜๋Š”์ง€ ๊ณ„์‚ฐํ•˜๊ณ , ์ฃผ๋ณ€ ํ”ฝ์…€๋“ค์„ ๋ณด๊ฐ„ํ•ด์„œ ๊ฐ’์„ ๊ตฌํ•˜๋Š” ๊ฑฐ์ง€! ๐Ÿ“

๐Ÿ”ฌ ์ปดํ“จํ„ฐ ๋น„์ „์˜ ๊ธฐ์ดˆ - ํŠน์ง• ๊ฒ€์ถœ

์ด์ œ ์ง„์งœ ์ปดํ“จํ„ฐ ๋น„์ „ ์˜์—ญ์œผ๋กœ ๋“ค์–ด๊ฐ€๋ณด์ž! ์ด๋ฏธ์ง€์—์„œ ์˜๋ฏธ ์žˆ๋Š” ์ •๋ณด๋ฅผ ์ถ”์ถœํ•˜๋Š” ๊ฑฐ์•ผ. ๋ฌผ์ฒด๋ฅผ ์ธ์‹ํ•˜๊ฑฐ๋‚˜ ์ถ”์ ํ•˜๋ ค๋ฉด ๋จผ์ € ํŠน์ง•(Feature)์„ ์ฐพ์•„์•ผ ํ•ด. ๐ŸŽฏ

์ฝ”๋„ˆ ๊ฒ€์ถœ (Corner Detection)

์ฝ”๋„ˆ๋Š” ์ด๋ฏธ์ง€์—์„œ ์ •๋ณด๊ฐ€ ๋งŽ์€ ์ง€์ ์ด์•ผ. ๋‘ ์—ฃ์ง€๊ฐ€ ๋งŒ๋‚˜๋Š” ๊ณณ์ด๋‹ˆ๊นŒ ์œ„์น˜๋ฅผ ํŠน์ •ํ•˜๊ธฐ ์‰ฝ๊ฑฐ๋“ . ๊ฐ€์žฅ ์œ ๋ช…ํ•œ ์•Œ๊ณ ๋ฆฌ์ฆ˜์€ ํ•ด๋ฆฌ์Šค ์ฝ”๋„ˆ ๊ฒ€์ถœ๊ธฐ(Harris Corner Detector)์•ผ!

๐ŸŽฏ ์™œ ์ฝ”๋„ˆ๊ฐ€ ์ค‘์š”ํ• ๊นŒ?

์ง์„  ์—ฃ์ง€๋Š” ๋ฐฉํ–ฅ์ด ์• ๋งคํ•ด. ํ•˜์ง€๋งŒ ์ฝ”๋„ˆ๋Š” ๋ช…ํ™•ํ•œ ์œ„์น˜๋ฅผ ๊ฐ€์ ธ! ๊ทธ๋ž˜์„œ ์ด๋ฏธ์ง€ ๋งค์นญ, ๊ฐ์ฒด ์ถ”์ , 3D ์žฌ๊ตฌ์„ฑ ๊ฐ™์€ ์ž‘์—…์—์„œ ์ฝ”๋„ˆ๋ฅผ ๋งŽ์ด ์‚ฌ์šฉํ•ด. ๊ฑด๋ฌผ์˜ ๋ชจ์„œ๋ฆฌ, ์ฐฝ๋ฌธ ๋ชจ์„œ๋ฆฌ ๊ฐ™์€ ๊ฒŒ ๋Œ€ํ‘œ์ ์ธ ์ฝ”๋„ˆ์•ผ. ๐Ÿข

// ๊ฐ„๋‹จํ•œ ์ฝ”๋„ˆ ๊ฒ€์ถœ (ํ•ด๋ฆฌ์Šค ์•Œ๊ณ ๋ฆฌ์ฆ˜ ๊ฐ„์†Œํ™” ๋ฒ„์ „)
typedef struct {
    int x;
    int y;
    float response;
} Corner;

Corner* detectCorners(Image* img, int* numCorners, float threshold) {
    // ๊ทธ๋ ˆ์ด์Šค์ผ€์ผ๋กœ ๋ณ€ํ™˜
    convertToGrayscale(img);
    
    // ๊ทธ๋ž˜๋””์–ธํŠธ ๊ณ„์‚ฐ
    Kernel* sobelX = createSobelXKernel();
    Kernel* sobelY = createSobelYKernel();
    
    Image* gradX = applyConvolution(img, sobelX);
    Image* gradY = applyConvolution(img, sobelY);
    
    // ์ฝ”๋„ˆ ์‘๋‹ต ๊ณ„์‚ฐ
    float* cornerResponse = (float*)malloc(img->width * img->height * sizeof(float));
    
    for (int y = 1; y < img->height - 1; y++) {
        for (int x = 1; x < img->width - 1; x++) {
            int idx = y * img->width + x;
            
            // ๊ตฌ์กฐ ํ…์„œ ๊ณ„์‚ฐ (3x3 ์œˆ๋„์šฐ)
            float Ixx = 0, Iyy = 0, Ixy = 0;
            
            for (int dy = -1; dy <= 1; dy++) {
                for (int dx = -1; dx <= 1; dx++) {
                    int nidx = (y + dy) * img->width + (x + dx);
                    float gx = gradX->data[nidx].r;
                    float gy = gradY->data[nidx].r;
                    
                    Ixx += gx * gx;
                    Iyy += gy * gy;
                    Ixy += gx * gy;
                }
            }
            
            // ํ•ด๋ฆฌ์Šค ์‘๋‹ต ๊ณ„์‚ฐ
            float det = Ixx * Iyy - Ixy * Ixy;
            float trace = Ixx + Iyy;
            float k = 0.04f;
            
            cornerResponse[idx] = det - k * trace * trace;
        }
    }
    
    // ์ž„๊ณ„๊ฐ’๋ณด๋‹ค ํฐ ์‘๋‹ต์„ ๊ฐ€์ง„ ์ ๋“ค์„ ์ฝ”๋„ˆ๋กœ ์„ ํƒ
    Corner* corners = (Corner*)malloc(img->width * img->height * sizeof(Corner));
    *numCorners = 0;
    
    for (int y = 1; y < img->height - 1; y++) {
        for (int x = 1; x < img->width - 1; x++) {
            int idx = y * img->width + x;
            
            if (cornerResponse[idx] > threshold) {
                // ๋น„์ตœ๋Œ€ ์–ต์ œ (์ฃผ๋ณ€๋ณด๋‹ค ํฐ ๊ฐ’๋งŒ ์„ ํƒ)
                int isMax = 1;
                for (int dy = -1; dy <= 1 && isMax; dy++) {
                    for (int dx = -1; dx <= 1 && isMax; dx++) {
                        if (dx == 0 && dy == 0) continue;
                        int nidx = (y + dy) * img->width + (x + dx);
                        if (cornerResponse[nidx] > cornerResponse[idx]) {
                            isMax = 0;
                        }
                    }
                }
                
                if (isMax) {
                    corners[*numCorners].x = x;
                    corners[*numCorners].y = y;
                    corners[*numCorners].response = cornerResponse[idx];
                    (*numCorners)++;
                }
            }
        }
    }
    
    // ๋ฉ”๋ชจ๋ฆฌ ์ •๋ฆฌ
    free(cornerResponse);
    free(gradX->data);
    free(gradX);
    free(gradY->data);
    free(gradY);
    free(sobelX->data);
    free(sobelX);
    free(sobelY->data);
    free(sobelY);
    
    return corners;
}

ํ•ด๋ฆฌ์Šค ์ฝ”๋„ˆ ๊ฒ€์ถœ๊ธฐ๋Š” ๊ฐ ํ”ฝ์…€ ์ฃผ๋ณ€์˜ ๊ทธ๋ž˜๋””์–ธํŠธ ๋ณ€ํ™”๋ฅผ ๋ถ„์„ํ•ด. ๋ชจ๋“  ๋ฐฉํ–ฅ์œผ๋กœ ํฐ ๋ณ€ํ™”๊ฐ€ ์žˆ์œผ๋ฉด ๊ทธ๊ฒŒ ๋ฐ”๋กœ ์ฝ”๋„ˆ์•ผ! ์ˆ˜ํ•™์ ์œผ๋กœ๋Š” ๊ตฌ์กฐ ํ…์„œ(Structure Tensor)์˜ ๊ณ ์œ ๊ฐ’์„ ๋ถ„์„ํ•˜๋Š” ๊ฑฐ์ง€. ๐Ÿ˜Š

๋ธ”๋กญ ๊ฒ€์ถœ (Blob Detection)

๋ธ”๋กญ(Blob)์€ ์ฃผ๋ณ€๊ณผ ๋‹ค๋ฅธ ๋ฐ๊ธฐ๋ฅผ ๊ฐ€์ง„ ์˜์—ญ์ด์•ผ. ์˜ˆ๋ฅผ ๋“ค์–ด ๋ฐ์€ ๋ฐฐ๊ฒฝ์— ์žˆ๋Š” ์–ด๋‘์šด ์ ์ด๋‚˜ ๊ทธ ๋ฐ˜๋Œ€์˜ ๊ฒฝ์šฐ์ง€. ๊ฐ„๋‹จํ•œ ๋ธ”๋กญ ๊ฒ€์ถœ์„ ๊ตฌํ˜„ํ•ด๋ณด์ž!

// LoG (Laplacian of Gaussian) ์ปค๋„ ์ƒ์„ฑ
Kernel* createLoGKernel(int size, float sigma) {
    Kernel* kernel = createKernel(size);
    int halfSize = size / 2;
    float sum = 0;
    
    for (int y = -halfSize; y <= halfSize; y++) {
        for (int x = -halfSize; x <= halfSize; x++) {
            int idx = (y + halfSize) * size + (x + halfSize);
            float r2 = x * x + y * y;
            float sigma2 = sigma * sigma;
            
            // LoG ๊ณต์‹
            kernel->data[idx] = -1.0f / (M_PI * sigma2 * sigma2) *
                               (1 - r2 / (2 * sigma2)) *
                               exp(-r2 / (2 * sigma2));
            
            sum += kernel->data[idx];
        }
    }
    
    // ์ •๊ทœํ™” (ํ•ฉ์ด 0์ด ๋˜๋„๋ก)
    for (int i = 0; i < size * size; i++) {
        kernel->data[i] -= sum / (size * size);
    }
    
    kernel->divisor = 1.0f;
    return kernel;
}

// ๋ธ”๋กญ ๊ฒ€์ถœ
Image* detectBlobs(Image* img, float sigma) {
    convertToGrayscale(img);
    
    int kernelSize = (int)(6 * sigma) | 1;  // ํ™€์ˆ˜๋กœ ๋งŒ๋“ค๊ธฐ
    Kernel* logKernel = createLoGKernel(kernelSize, sigma);
    
    Image* result = applyConvolution(img, logKernel);
    
    // ์ ˆ๋Œ“๊ฐ’ ์ทจํ•˜๊ณ  ์ •๊ทœํ™”
    for (int i = 0; i < result->width * result->height; i++) {
        int value = abs((int)result->data[i].r - 128);
        value = (value > 255) ? 255 : value;
        
        result->data[i].r = value;
        result->data[i].g = value;
        result->data[i].b = value;
    }
    
    free(logKernel->data);
    free(logKernel);
    
    return result;
}

๐Ÿ’ก LoG (Laplacian of Gaussian)๋ž€?

๊ฐ€์šฐ์‹œ์•ˆ ๋ธ”๋Ÿฌ๋ฅผ ์ ์šฉํ•œ ํ›„ ๋ผํ”Œ๋ผ์‹œ์•ˆ(2์ฐจ ๋ฏธ๋ถ„)์„ ๊ณ„์‚ฐํ•˜๋Š” ๊ฑฐ์•ผ. ์ด๋ ‡๊ฒŒ ํ•˜๋ฉด ํŠน์ • ํฌ๊ธฐ์˜ ๋ธ”๋กญ์— ๊ฐ•ํ•˜๊ฒŒ ๋ฐ˜์‘ํ•ด! sigma ๊ฐ’์„ ๋ฐ”๊พธ๋ฉด ๋‹ค๋ฅธ ํฌ๊ธฐ์˜ ๋ธ”๋กญ์„ ์ฐพ์„ ์ˆ˜ ์žˆ์–ด. ์„ธํฌ ์ด๋ฏธ์ง€ ๋ถ„์„์ด๋‚˜ ๋ณ„ ๊ฒ€์ถœ ๊ฐ™์€ ๋ฐ ๋งŽ์ด ์“ฐ์—ฌ. ๐Ÿ”ฌ

์—ฐ๊ฒฐ ์š”์†Œ ๋ถ„์„ (Connected Component Analysis)

์ด์ง„ ์ด๋ฏธ์ง€(ํ‘๋ฐฑ๋งŒ ์žˆ๋Š” ์ด๋ฏธ์ง€)์—์„œ ์—ฐ๊ฒฐ๋œ ํ”ฝ์…€๋“ค์„ ํ•˜๋‚˜์˜ ๊ฐ์ฒด๋กœ ๋ฌถ๋Š” ๊ฑฐ์•ผ. ๊ฐ์ฒด๋ฅผ ์„ธ๊ฑฐ๋‚˜ ๊ฐ ๊ฐ์ฒด์˜ ํŠน์„ฑ์„ ๋ถ„์„ํ•  ๋•Œ ์œ ์šฉํ•ด!

// ๊ฐ„๋‹จํ•œ ์—ฐ๊ฒฐ ์š”์†Œ ๋ ˆ์ด๋ธ”๋ง (4-์—ฐ๊ฒฐ์„ฑ)
int* labelConnectedComponents(Image* img, int* numComponents) {
    int* labels = (int*)calloc(img->width * img->height, sizeof(int));
    int currentLabel = 1;
    
    // ์ด์ง„ํ™” (์ž„๊ณ„๊ฐ’ 128)
    for (int i = 0; i < img->width * img->height; i++) {
        if (img->data[i].r > 128) {
            labels[i] = -1;  // ์ „๊ฒฝ ํ”ฝ์…€ ํ‘œ์‹œ
        }
    }
    
    // ํ”Œ๋Ÿฌ๋“œ ํ•„ ๋ฐฉ์‹์œผ๋กœ ๋ ˆ์ด๋ธ”๋ง
    for (int y = 0; y < img->height; y++) {
        for (int x = 0; x < img->width; x++) {
            int idx = y * img->width + x;
            
            if (labels[idx] == -1) {
                // ์ƒˆ๋กœ์šด ์ปดํฌ๋„ŒํŠธ ๋ฐœ๊ฒฌ
                floodFill(labels, img->width, img->height, x, y, currentLabel);
                currentLabel++;
            }
        }
    }
    
    *numComponents = currentLabel - 1;
    return labels;
}

// ํ”Œ๋Ÿฌ๋“œ ํ•„ ํ—ฌํผ ํ•จ์ˆ˜
void floodFill(int* labels, int width, int height, int x, int y, int label) {
    // ์Šคํƒ ๊ธฐ๋ฐ˜ ํ”Œ๋Ÿฌ๋“œ ํ•„ (์žฌ๊ท€ ๋Œ€์‹ )
    int* stackX = (int*)malloc(width * height * sizeof(int));
    int* stackY = (int*)malloc(width * height * sizeof(int));
    int stackSize = 0;
    
    stackX[stackSize] = x;
    stackY[stackSize] = y;
    stackSize++;
    
    while (stackSize > 0) {
        stackSize--;
        int cx = stackX[stackSize];
        int cy = stackY[stackSize];
        int idx = cy * width + cx;
        
        if (cx < 0 || cx >= width || cy < 0 || cy >= height) continue;
        if (labels[idx] != -1) continue;
        
        labels[idx] = label;
        
        // 4๋ฐฉํ–ฅ ์ด์›ƒ ์ถ”๊ฐ€
        stackX[stackSize] = cx + 1; stackY[stackSize] = cy; stackSize++;
        stackX[stackSize] = cx - 1; stackY[stackSize] = cy; stackSize++;
        stackX[stackSize] = cx; stackY[stackSize] = cy + 1; stackSize++;
        stackX[stackSize] = cx; stackY[stackSize] = cy - 1; stackSize++;
    }
    
    free(stackX);
    free(stackY);
}

์—ฐ๊ฒฐ ์š”์†Œ ๋ถ„์„์€ ๊ฐ์ฒด๋ฅผ ๊ฐœ๋ณ„์ ์œผ๋กœ ๋‹ค๋ฃฐ ์ˆ˜ ์žˆ๊ฒŒ ํ•ด์ค˜. ๊ฐ ๊ฐ์ฒด์˜ ๋ฉด์ , ์ค‘์‹ฌ, ๊ฒฝ๊ณ„ ์ƒ์ž ๊ฐ™์€ ๊ฑธ ๊ณ„์‚ฐํ•  ์ˆ˜ ์žˆ์ง€! ๐ŸŽฏ

์ปดํ“จํ„ฐ ๋น„์ „ ์ฒ˜๋ฆฌ ๋‹จ๊ณ„ 1. ์ „์ฒ˜๋ฆฌ ๋…ธ์ด์ฆˆ ์ œ๊ฑฐ ๊ทธ๋ ˆ์ด์Šค์ผ€์ผ ๋ณ€ํ™˜ ์ •๊ทœํ™” 2. ํŠน์ง• ์ถ”์ถœ ์—ฃ์ง€ ๊ฒ€์ถœ ์ฝ”๋„ˆ ๊ฒ€์ถœ ๋ธ”๋กญ ๊ฒ€์ถœ 3. ๋ถ„์„ ๊ฐ์ฒด ๋ถ„๋ฆฌ ํŒจํ„ด ๋งค์นญ ์ธก์ • ์ €์ˆ˜์ค€ ์ฒ˜๋ฆฌ ํ”ฝ์…€ ๋‹จ์œ„ ์—ฐ์‚ฐ ํ•„ํ„ฐ๋ง ์ƒ‰์ƒ ๋ณ€ํ™˜ ๊ธฐํ•˜ํ•™์  ๋ณ€ํ™˜ ํžˆ์Šคํ† ๊ทธ๋žจ ์ฒ˜๋ฆฌ ๋ชจํด๋กœ์ง€ ์—ฐ์‚ฐ ์ค‘๊ฐ„ ์ˆ˜์ค€ ์ฒ˜๋ฆฌ ์—ฃ์ง€ ์—ฐ๊ฒฐ ์˜์—ญ ๋ถ„ํ•  ํŠน์ง• ๊ธฐ์ˆ  ํ…์Šค์ฒ˜ ๋ถ„์„ ๋ชจ์…˜ ๋ถ„์„ ๊นŠ์ด ์ถ”์ • ๊ณ ์ˆ˜์ค€ ์ฒ˜๋ฆฌ ๊ฐ์ฒด ์ธ์‹ ์žฅ๋ฉด ์ดํ•ด ์–ผ๊ตด ์ธ์‹ ํ–‰๋™ ์ธ์‹ 3D ์žฌ๊ตฌ์„ฑ ์˜๋ฏธ๋ก ์  ๋ถ„ํ•  ์‘์šฉ ๋ถ„์•ผ ์ž์œจ ์ฃผํ–‰ ์˜๋ฃŒ ์˜์ƒ ๋ถ„์„ ๋ณด์•ˆ ๊ฐ์‹œ ์ฆ๊ฐ• ํ˜„์‹ค ๋กœ๋ด‡ ๋น„์ „ ํ’ˆ์งˆ ๊ฒ€์‚ฌ ๋ฌธ์„œ ๋ถ„์„ ์Šคํฌ์ธ  ๋ถ„์„

โšก ์„ฑ๋Šฅ ์ตœ์ ํ™”์™€ ์‹ค์ „ ํŒ

์ด๋ฏธ์ง€ ์ฒ˜๋ฆฌ๋Š” ๊ณ„์‚ฐ๋Ÿ‰์ด ๋งŽ์•„์„œ ์ตœ์ ํ™”๊ฐ€ ์ •๋ง ์ค‘์š”ํ•ด! ํŠนํžˆ ์‹ค์‹œ๊ฐ„ ์ฒ˜๋ฆฌ๊ฐ€ ํ•„์š”ํ•œ ๊ฒฝ์šฐ์—๋Š” ๋”์šฑ ๊ทธ๋ ‡์ง€. ๋ช‡ ๊ฐ€์ง€ ์‹ค์ „ ํŒ์„ ์•Œ๋ ค์ค„๊ฒŒ. ๐Ÿš€

๋ฉ”๋ชจ๋ฆฌ ๊ด€๋ฆฌ

C์–ธ์–ด์—์„œ๋Š” ๋ฉ”๋ชจ๋ฆฌ๋ฅผ ์ง์ ‘ ๊ด€๋ฆฌํ•ด์•ผ ํ•˜๋‹ˆ๊นŒ ์กฐ์‹ฌํ•ด์•ผ ํ•ด. ๋ฉ”๋ชจ๋ฆฌ ๋ˆ„์ˆ˜๋Š” ํ”„๋กœ๊ทธ๋žจ์„ ๋А๋ฆฌ๊ฒŒ ๋งŒ๋“ค๊ณ  ๊ฒฐ๊ตญ ํฌ๋ž˜์‹œ๋ฅผ ์ผ์œผํ‚ฌ ์ˆ˜ ์žˆ์–ด!

// ์ด๋ฏธ์ง€ ๋ฉ”๋ชจ๋ฆฌ ํ•ด์ œ ํ•จ์ˆ˜
void freeImage(Image* img) {
    if (img) {
        if (img->data) {
            free(img->data);
        }
        free(img);
    }
}

// ์ปค๋„ ๋ฉ”๋ชจ๋ฆฌ ํ•ด์ œ ํ•จ์ˆ˜
void freeKernel(Kernel* kernel) {
    if (kernel) {
        if (kernel->data) {
            free(kernel->data);
        }
        free(kernel);
    }
}

// ์‚ฌ์šฉ ์˜ˆ์‹œ
int main() {
    Image* img = loadBMP("input.bmp");
    if (!img) return 1;
    
    // ์ด๋ฏธ์ง€ ์ฒ˜๋ฆฌ
    convertToGrayscale(img);
    
    Kernel* blurKernel = createGaussianBlurKernel();
    Image* blurred = applyConvolution(img, blurKernel);
    
    saveBMP("output.bmp", blurred);
    
    // ๋ฉ”๋ชจ๋ฆฌ ์ •๋ฆฌ - ์ค‘์š”!
    freeImage(img);
    freeImage(blurred);
    freeKernel(blurKernel);
    
    return 0;
}

โš ๏ธ ๋ฉ”๋ชจ๋ฆฌ ๋ˆ„์ˆ˜ ์ฃผ์˜!

malloc์œผ๋กœ ํ• ๋‹นํ•œ ๋ฉ”๋ชจ๋ฆฌ๋Š” ๋ฐ˜๋“œ์‹œ free๋กœ ํ•ด์ œํ•ด์•ผ ํ•ด! ํŠนํžˆ ์ด๋ฏธ์ง€ ์ฒ˜๋ฆฌ๋Š” ๋ฉ”๋ชจ๋ฆฌ๋ฅผ ๋งŽ์ด ์‚ฌ์šฉํ•˜๋‹ˆ๊นŒ ๋”์šฑ ์กฐ์‹ฌํ•ด์•ผ ํ•ด. Valgrind ๊ฐ™์€ ๋„๊ตฌ๋กœ ๋ฉ”๋ชจ๋ฆฌ ๋ˆ„์ˆ˜๋ฅผ ์ฒดํฌํ•˜๋Š” ์Šต๊ด€์„ ๋“ค์ด์ž! ๐Ÿ’ช

์บ์‹œ ์นœํ™”์ ์ธ ์ฝ”๋“œ ์ž‘์„ฑ

๋ฉ”๋ชจ๋ฆฌ ์ ‘๊ทผ ํŒจํ„ด์ด ์„ฑ๋Šฅ์— ํฐ ์˜ํ–ฅ์„ ์ค˜. ์—ฐ์†๋œ ๋ฉ”๋ชจ๋ฆฌ๋ฅผ ์ˆœ์ฐจ์ ์œผ๋กœ ์ ‘๊ทผํ•˜๋Š” ๊ฒŒ ํ›จ์”ฌ ๋นจ๋ผ!

// ๋‚˜์œ ์˜ˆ - ์—ด ์šฐ์„  ์ ‘๊ทผ
void badExample(Image* img) {
    for (int x = 0; x < img->width; x++) {
        for (int y = 0; y < img->height; y++) {
            int idx = y * img->width + x;  // ๋ฉ”๋ชจ๋ฆฌ ์ ํ”„!
            img->data[idx].r = 0;
        }
    }
}

// ์ข‹์€ ์˜ˆ - ํ–‰ ์šฐ์„  ์ ‘๊ทผ
void goodExample(Image* img) {
    for (int y = 0; y < img->height; y++) {
        for (int x = 0; x < img->width; x++) {
            int idx = y * img->width + x;  // ์ˆœ์ฐจ์  ์ ‘๊ทผ!
            img->data[idx].r = 0;
        }
    }
}

์ด๋ฏธ์ง€ ๋ฐ์ดํ„ฐ๋Š” ํ–‰ ์šฐ์„ (row-major)์œผ๋กœ ์ €์žฅ๋˜์–ด ์žˆ์–ด. ๊ทธ๋ž˜์„œ y๋ฅผ ๋ฐ”๊นฅ ๋ฃจํ”„๋กœ, x๋ฅผ ์•ˆ์ชฝ ๋ฃจํ”„๋กœ ํ•˜๋ฉด ๋ฉ”๋ชจ๋ฆฌ๋ฅผ ์ˆœ์ฐจ์ ์œผ๋กœ ์ ‘๊ทผํ•˜๊ฒŒ ๋ผ. ์ด๊ฒŒ ์บ์‹œ ํžˆํŠธ์œจ์„ ๋†’์—ฌ์„œ ํ›จ์”ฌ ๋น ๋ฅด์ง€! ๐Ÿƒ

SIMD๋ฅผ ํ™œ์šฉํ•œ ๋ณ‘๋ ฌ ์ฒ˜๋ฆฌ

ํ˜„๋Œ€ CPU๋Š” SIMD(Single Instruction Multiple Data) ๋ช…๋ น์–ด๋ฅผ ์ง€์›ํ•ด. ํ•œ ๋ฒˆ์— ์—ฌ๋Ÿฌ ํ”ฝ์…€์„ ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ์–ด!

#include <emmintrin.h>  // SSE2

// SIMD๋ฅผ ์‚ฌ์šฉํ•œ ๋ฐ๊ธฐ ์กฐ์ ˆ (SSE2)
void adjustBrightnessSIMD(Image* img, int value) {
    int totalPixels = img->width * img->height;
    unsigned char* data = (unsigned char*)img->data;
    
    // 16๋ฐ”์ดํŠธ์”ฉ ์ฒ˜๋ฆฌ (RGB ํ”ฝ์…€ 5๊ฐœ + 1๋ฐ”์ดํŠธ)
    int simdCount = (totalPixels * 3) / 16;
    
    __m128i addValue = _mm_set1_epi8(value);
    
    for (int i = 0; i < simdCount; i++) {
        __m128i pixels = _mm_loadu_si128((__m128i*)(data + i * 16));
        
        // ๋ถ€ํ˜ธ ์žˆ๋Š” ๋ง์…ˆ ํ›„ ํฌํ™” ์—ฐ์‚ฐ
        if (value >= 0) {
            pixels = _mm_adds_epu8(pixels, addValue);
        } else {
            pixels = _mm_subs_epu8(pixels, addValue);
        }
        
        _mm_storeu_si128((__m128i*)(data + i * 16), pixels);
    }
    
    // ๋‚˜๋จธ์ง€ ํ”ฝ์…€ ์ฒ˜๋ฆฌ
    for (int i = simdCount * 16; i < totalPixels * 3; i++) {
        int newValue = data[i] + value;
        data[i] = (newValue > 255) ? 255 : (newValue < 0) ? 0 : newValue;
    }
}

๐Ÿ’ก SIMD ์ตœ์ ํ™” ํŒ

SIMD๋Š” ๊ฐ•๋ ฅํ•˜์ง€๋งŒ ๋ชจ๋“  ๊ฒฝ์šฐ์— ๋น ๋ฅธ ๊ฑด ์•„๋‹ˆ์•ผ. ๋ฐ์ดํ„ฐ๊ฐ€ ์ถฉ๋ถ„ํžˆ ํฌ๊ณ , ๊ฐ™์€ ์—ฐ์‚ฐ์„ ๋ฐ˜๋ณตํ•  ๋•Œ ํšจ๊ณผ์ ์ด์•ผ. ๋˜ ๋ฉ”๋ชจ๋ฆฌ ์ •๋ ฌ๋„ ์ค‘์š”ํ•ด! 16๋ฐ”์ดํŠธ ์ •๋ ฌ๋œ ๋ฉ”๋ชจ๋ฆฌ๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ๋” ๋น ๋ฅด์ง€. ํ•˜์ง€๋งŒ ์ฝ”๋“œ๊ฐ€ ๋ณต์žกํ•ด์ง€๋‹ˆ๊นŒ ์ •๋ง ํ•„์š”ํ•œ ๊ณณ์—๋งŒ ์‚ฌ์šฉํ•˜์ž! ๐ŸŽฏ

๋ฉ€ํ‹ฐ์Šค๋ ˆ๋”ฉ

์ด๋ฏธ์ง€ ์ฒ˜๋ฆฌ๋Š” ๊ฐ ํ”ฝ์…€์ด ๋…๋ฆฝ์ ์ธ ๊ฒฝ์šฐ๊ฐ€ ๋งŽ์•„์„œ ๋ณ‘๋ ฌํ™”ํ•˜๊ธฐ ์ข‹์•„! OpenMP๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ์‰ฝ๊ฒŒ ๋ฉ€ํ‹ฐ์Šค๋ ˆ๋”ฉ์„ ์ ์šฉํ•  ์ˆ˜ ์žˆ์–ด.

#include <omp.h>

// OpenMP๋ฅผ ์‚ฌ์šฉํ•œ ๋ณ‘๋ ฌ ์ฒ˜๋ฆฌ
void convertToGrayscaleParallel(Image* img) {
    int totalPixels = img->width * img->height;
    
    #pragma omp parallel for
    for (int i = 0; i < totalPixels; i++) {
        unsigned char gray = (unsigned char)(
            0.299 * img->data[i].r +
            0.587 * img->data[i].g +
            0.114 * img->data[i].b
        );
        
        img->data[i].r = gray;
        img->data[i].g = gray;
        img->data[i].b = gray;
    }
}

// ์ปจ๋ณผ๋ฃจ์…˜๋„ ๋ณ‘๋ ฌํ™” ๊ฐ€๋Šฅ
Image* applyConvolutionParallel(Image* img, Kernel* kernel) {
    Image* result = (Image*)malloc(sizeof(Image));
    result->width = img->width;
    result->height = img->height;
    result->data = (Pixel*)malloc(img->width * img->height * sizeof(Pixel));
    
    int halfSize = kernel->size / 2;
    
    #pragma omp parallel for collapse(2)
    for (int y = 0; y < img->height; y++) {
        for (int x = 0; x < img->width; x++) {
            float sumR = 0, sumG = 0, sumB = 0;
            
            for (int ky = -halfSize; ky <= halfSize; ky++) {
                for (int kx = -halfSize; kx <= halfSize; kx++) {
                    int px = x + kx;
                    int py = y + ky;
                    
                    if (px < 0) px = 0;
                    if (px >= img->width) px = img->width - 1;
                    if (py < 0) py = 0;
                    if (py >= img->height) py = img->height - 1;
                    
                    int idx = py * img->width + px;
                    int kidx = (ky + halfSize) * kernel->size + (kx + halfSize);
                    
                    sumR += img->data[idx].r * kernel->data[kidx];
                    sumG += img->data[idx].g * kernel->data[kidx];
                    sumB += img->data[idx].b * kernel->data[kidx];
                }
            }
            
            int outIdx = y * img->width + x;
            int r = (int)(sumR / kernel->divisor);
            int g = (int)(sumG / kernel->divisor);
            int b = (int)(sumB / kernel->divisor);
            
            result->data[outIdx].r = (r > 255) ? 255 : (r < 0) ? 0 : r;
            result->data[outIdx].g = (g > 255) ? 255 : (g < 0) ? 0 : g;
            result->data[outIdx].b = (b > 255) ? 255 : (b < 0) ? 0 : b;
        }
    }
    
    return result;
}

OpenMP๋Š” ์ •๋ง ๊ฐ„๋‹จํ•˜์ง€? #pragma omp parallel for ํ•œ ์ค„๋งŒ ์ถ”๊ฐ€ํ•˜๋ฉด ์ž๋™์œผ๋กœ ๋ณ‘๋ ฌ ์ฒ˜๋ฆฌ๊ฐ€ ๋ผ! collapse(2)๋Š” ์ค‘์ฒฉ ๋ฃจํ”„๋ฅผ ๋ชจ๋‘ ๋ณ‘๋ ฌํ™”ํ•˜๋ผ๋Š” ๋œป์ด์•ผ. ๐Ÿš€

๐Ÿ› ๏ธ ์‹ค์ „ ํ”„๋กœ์ ํŠธ ์˜ˆ์ œ

์ด๋ก ๋งŒ ๋ฐฐ์šฐ๋ฉด ์žฌ๋ฏธ์—†์ง€! ์‹ค์ œ๋กœ ๋™์ž‘ํ•˜๋Š” ํ”„๋กœ๊ทธ๋žจ์„ ๋งŒ๋“ค์–ด๋ณด์ž. ๊ฐ„๋‹จํ•œ ์ด๋ฏธ์ง€ ์ฒ˜๋ฆฌ ๋„๊ตฌ๋ฅผ ๋งŒ๋“ค์–ด๋ณผ๊ฒŒ. ๐Ÿ˜Š

๋ช…๋ น์ค„ ์ด๋ฏธ์ง€ ์ฒ˜๋ฆฌ ๋„๊ตฌ

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>

// ์•ž์—์„œ ์ •์˜ํ•œ ๋ชจ๋“  ๊ตฌ์กฐ์ฒด์™€ ํ•จ์ˆ˜๋“ค์ด ํฌํ•จ๋˜์–ด ์žˆ๋‹ค๊ณ  ๊ฐ€์ •

void printUsage() {
    printf("์‚ฌ์šฉ๋ฒ•: imageproc [์˜ต์…˜] ์ž…๋ ฅํŒŒ์ผ ์ถœ๋ ฅํŒŒ์ผ\n");
    printf("์˜ต์…˜:\n");
    printf("  -gray          ๊ทธ๋ ˆ์ด์Šค์ผ€์ผ ๋ณ€ํ™˜\n");
    printf("  -bright N      ๋ฐ๊ธฐ ์กฐ์ ˆ (N: -255 ~ 255)\n");
    printf("  -contrast F    ๋Œ€๋น„ ์กฐ์ ˆ (F: 0.0 ~ 3.0)\n");
    printf("  -invert        ์ƒ‰์ƒ ๋ฐ˜์ „\n");
    printf("  -blur          ๊ฐ€์šฐ์‹œ์•ˆ ๋ธ”๋Ÿฌ\n");
    printf("  -sharpen       ์ƒคํ”„๋‹\n");
    printf("  -edge          ์—ฃ์ง€ ๊ฒ€์ถœ (์†Œ๋ฒจ)\n");
    printf("  -threshold N   ์ž„๊ณ„๊ฐ’ ์ฒ˜๋ฆฌ (N: 0 ~ 255)\n");
    printf("  -rotate A      ํšŒ์ „ (A: ๊ฐ๋„)\n");
    printf("  -resize WxH    ํฌ๊ธฐ ์กฐ์ ˆ (์˜ˆ: 800x600)\n");
}

int main(int argc, char* argv[]) {
    if (argc < 4) {
        printUsage();
        return 1;
    }
    
    char* option = argv[1];
    char* inputFile = argv[2];
    char* outputFile = argv[3];
    
    // ์ด๋ฏธ์ง€ ๋กœ๋“œ
    printf("์ด๋ฏธ์ง€ ๋กœ๋”ฉ ์ค‘: %s\n", inputFile);
    Image* img = loadBMP(inputFile);
    if (!img) {
        printf("์ด๋ฏธ์ง€๋ฅผ ๋กœ๋“œํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค!\n");
        return 1;
    }
    
    printf("์ด๋ฏธ์ง€ ํฌ๊ธฐ: %dx%d\n", img->width, img->height);
    
    // ์˜ต์…˜์— ๋”ฐ๋ผ ์ฒ˜๋ฆฌ
    if (strcmp(option, "-gray") == 0) {
        printf("๊ทธ๋ ˆ์ด์Šค์ผ€์ผ ๋ณ€ํ™˜ ์ค‘...\n");
        convertToGrayscale(img);
    }
    else if (strcmp(option, "-bright") == 0) {
        if (argc < 5) {
            printf("๋ฐ๊ธฐ ๊ฐ’์„ ์ง€์ •ํ•ด์ฃผ์„ธ์š”!\n");
            freeImage(img);
            return 1;
        }
        int value = atoi(argv[4]);
        printf("๋ฐ๊ธฐ ์กฐ์ ˆ ์ค‘ (๊ฐ’: %d)...\n", value);
        adjustBrightness(img, value);
    }
    else if (strcmp(option, "-contrast") == 0) {
        if (argc < 5) {
            printf("๋Œ€๋น„ ๊ฐ’์„ ์ง€์ •ํ•ด์ฃผ์„ธ์š”!\n");
            freeImage(img);
            return 1;
        }
        float factor = atof(argv[4]);
        printf("๋Œ€๋น„ ์กฐ์ ˆ ์ค‘ (๋ฐฐ์œจ: %.2f)...\n", factor);
        adjustContrast(img, factor);
    }
    else if (strcmp(option, "-invert") == 0) {
        printf("์ƒ‰์ƒ ๋ฐ˜์ „ ์ค‘...\n");
        invertImage(img);
    }
    else if (strcmp(option, "-blur") == 0) {
        printf("๋ธ”๋Ÿฌ ์ ์šฉ ์ค‘...\n");
        Kernel* kernel = createGaussianBlurKernel();
        Image* result = applyConvolution(img, kernel);
        freeImage(img);
        img = result;
        freeKernel(kernel);
    }
    else if (strcmp(option, "-sharpen") == 0) {
        printf("์ƒคํ”„๋‹ ์ ์šฉ ์ค‘...\n");
        Kernel* kernel = createSharpenKernel();
        Image* result = applyConvolution(img, kernel);
        freeImage(img);
        img = result;
        freeKernel(kernel);
    }
    else if (strcmp(option, "-edge") == 0) {
        printf("์—ฃ์ง€ ๊ฒ€์ถœ ์ค‘...\n");
        Image* result = applySobelEdgeDetection(img);
        freeImage(img);
        img = result;
    }
    else if (strcmp(option, "-threshold") == 0) {
        if (argc < 5) {
            printf("์ž„๊ณ„๊ฐ’์„ ์ง€์ •ํ•ด์ฃผ์„ธ์š”!\n");
            freeImage(img);
            return 1;
        }
        unsigned char value = (unsigned char)atoi(argv[4]);
        printf("์ž„๊ณ„๊ฐ’ ์ฒ˜๋ฆฌ ์ค‘ (๊ฐ’: %d)...\n", value);
        threshold(img, value);
    }
    else if (strcmp(option, "-rotate") == 0) {
        if (argc < 5) {
            printf("ํšŒ์ „ ๊ฐ๋„๋ฅผ ์ง€์ •ํ•ด์ฃผ์„ธ์š”!\n");
            freeImage(img);
            return 1;
        }
        float angle = atof(argv[4]) * M_PI / 180.0f;  // ๋„๋ฅผ ๋ผ๋””์•ˆ์œผ๋กœ
        printf("ํšŒ์ „ ์ค‘ (๊ฐ๋„: %.1f๋„)...\n", atof(argv[4]));
        Image* result = rotateImage(img, angle);
        freeImage(img);
        img = result;
    }
    else if (strcmp(option, "-resize") == 0) {
        if (argc < 5) {
            printf("ํฌ๊ธฐ๋ฅผ ์ง€์ •ํ•ด์ฃผ์„ธ์š”! (์˜ˆ: 800x600)\n");
            freeImage(img);
            return 1;
        }
        int newWidth, newHeight;
        sscanf(argv[4], "%dx%d", &newWidth, &newHeight);
        printf("ํฌ๊ธฐ ์กฐ์ ˆ ์ค‘ (%dx%d)...\n", newWidth, newHeight);
        Image* result = resizeImage(img, newWidth, newHeight);
        freeImage(img);
        img = result;
    }
    else {
        printf("์•Œ ์ˆ˜ ์—†๋Š” ์˜ต์…˜: %s\n", option);
        printUsage();
        freeImage(img);
        return 1;
    }
    
    // ๊ฒฐ๊ณผ ์ €์žฅ
    printf("๊ฒฐ๊ณผ ์ €์žฅ ์ค‘: %s\n", outputFile);
    if (saveBMP(outputFile, img)) {
        printf("์™„๋ฃŒ!\n");
    } else {
        printf("์ €์žฅ ์‹คํŒจ!\n");
    }
    
    freeImage(img);
    return 0;
}

์ด์ œ ์ด๋ ‡๊ฒŒ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์–ด:

# ๊ทธ๋ ˆ์ด์Šค์ผ€์ผ ๋ณ€ํ™˜
./imageproc -gray input.bmp output.bmp

# ๋ฐ๊ธฐ 50 ์ฆ๊ฐ€
./imageproc -bright 50 input.bmp output.bmp

# ๋ธ”๋Ÿฌ ์ ์šฉ
./imageproc -blur input.bmp output.bmp

# ์—ฃ์ง€ ๊ฒ€์ถœ
./imageproc -edge input.bmp output.bmp

# 45๋„ ํšŒ์ „
./imageproc -rotate 45 input.bmp output.bmp

# 800x600์œผ๋กœ ํฌ๊ธฐ ์กฐ์ ˆ
./imageproc -resize 800x600 input.bmp output.bmp

๊ฐ„๋‹จํ•˜์ง€๋งŒ ์‹ค์šฉ์ ์ธ ๋„๊ตฌ๊ฐ€ ์™„์„ฑ๋์–ด! ๐ŸŽ‰

๋ฐฐ์น˜ ์ฒ˜๋ฆฌ ์Šคํฌ๋ฆฝํŠธ

์—ฌ๋Ÿฌ ์ด๋ฏธ์ง€๋ฅผ ํ•œ ๋ฒˆ์— ์ฒ˜๋ฆฌํ•˜๊ณ  ์‹ถ๋‹ค๋ฉด? ๊ฐ„๋‹จํ•œ ๋ฐฐ์น˜ ์Šคํฌ๋ฆฝํŠธ๋ฅผ ๋งŒ๋“ค ์ˆ˜ ์žˆ์–ด!

#!/bin/bash
# batch_process.sh

if [ $# -lt 2 ]; then
    echo "์‚ฌ์šฉ๋ฒ•: $0 [์˜ต์…˜] [์ž…๋ ฅ๋””๋ ‰ํ† ๋ฆฌ]"
    exit 1
fi

OPTION=$1
INPUT_DIR=$2
OUTPUT_DIR="${INPUT_DIR}_processed"

mkdir -p "$OUTPUT_DIR"

for file in "$INPUT_DIR"/*.bmp; do
    filename=$(basename "$file")
    echo "์ฒ˜๋ฆฌ ์ค‘: $filename"
    ./imageproc $OPTION "$file" "$OUTPUT_DIR/$filename"
done

echo "๋ชจ๋“  ํŒŒ์ผ ์ฒ˜๋ฆฌ ์™„๋ฃŒ!"

์ด์ œ ํด๋” ์•ˆ์˜ ๋ชจ๋“  ์ด๋ฏธ์ง€๋ฅผ ํ•œ ๋ฒˆ์— ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ์–ด! ๐Ÿ˜Ž

๐Ÿ“š ๋” ๋‚˜์•„๊ฐ€๊ธฐ - ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ํ™œ์šฉ

์ง€๊ธˆ๊นŒ์ง€ ๋ชจ๋“  ๊ฑธ ์ง์ ‘ ๊ตฌํ˜„ํ•ด๋ดค๋Š”๋ฐ, ์‹ค์ œ ํ”„๋กœ์ ํŠธ์—์„œ๋Š” ๊ฒ€์ฆ๋œ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๊ฒŒ ์ข‹์•„. ์‹œ๊ฐ„๋„ ์ ˆ์•ฝ๋˜๊ณ  ์„ฑ๋Šฅ๋„ ํ›จ์”ฌ ์ข‹๊ฑฐ๋“ ! ๐Ÿš€

OpenCV - ์ปดํ“จํ„ฐ ๋น„์ „์˜ ํ‘œ์ค€

OpenCV๋Š” ๊ฐ€์žฅ ์œ ๋ช…ํ•œ ์ปดํ“จํ„ฐ ๋น„์ „ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์•ผ. C++๋กœ ์ž‘์„ฑ๋์ง€๋งŒ C ์ธํ„ฐํŽ˜์ด์Šค๋„ ์ œ๊ณตํ•ด.

#include <opencv2/opencv.hpp>

using namespace cv;

int main() {
    // ์ด๋ฏธ์ง€ ๋กœ๋“œ
    Mat img = imread("input.jpg");
    
    // ๊ทธ๋ ˆ์ด์Šค์ผ€์ผ ๋ณ€ํ™˜
    Mat gray;
    cvtColor(img, gray, COLOR_BGR2GRAY);
    
    // ๊ฐ€์šฐ์‹œ์•ˆ ๋ธ”๋Ÿฌ
    Mat blurred;
    GaussianBlur(gray, blurred, Size(5, 5), 1.5);
    
    // ์บ๋‹ˆ ์—ฃ์ง€ ๊ฒ€์ถœ
    Mat edges;
    Canny(blurred, edges, 50, 150);
    
    // ๊ฒฐ๊ณผ ์ €์žฅ
    imwrite("output.jpg", edges);
    
    return 0;
}

OpenCV๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ๋ช‡ ์ค„๋กœ ๋ณต์žกํ•œ ์ž‘์—…์„ ํ•  ์ˆ˜ ์žˆ์–ด! ํ•˜์ง€๋งŒ ์šฐ๋ฆฌ๊ฐ€ ๋ฐฐ์šด ๊ธฐ์ดˆ ์›๋ฆฌ๋ฅผ ์•Œ๊ณ  ์žˆ์œผ๋ฉด OpenCV๋ฅผ ํ›จ์”ฌ ํšจ๊ณผ์ ์œผ๋กœ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์ง€. ๐Ÿ’ช

stb_image - ๊ฐ€๋ฒผ์šด ์ด๋ฏธ์ง€ ๋กœ๋”

BMP๋งŒ ์ง€์›ํ•˜๋Š” ๊ฑด ๋ถˆํŽธํ•˜์ง€? stb_image๋Š” ํ—ค๋” ํŒŒ์ผ ํ•˜๋‚˜๋กœ JPEG, PNG, GIF ๋“ฑ ๋‹ค์–‘ํ•œ ํฌ๋งท์„ ์ง€์›ํ•ด!

#define STB_IMAGE_IMPLEMENTATION
#include "stb_image.h"

#define STB_IMAGE_WRITE_IMPLEMENTATION
#include "stb_image_write.h"

int main() {
    int width, height, channels;
    
    // ์ด๋ฏธ์ง€ ๋กœ๋“œ (์ž๋™์œผ๋กœ ํฌ๋งท ๊ฐ์ง€)
    unsigned char* data = stbi_load("input.jpg", &width, &height, &channels, 0);
    
    if (data) {
        printf("์ด๋ฏธ์ง€ ํฌ๊ธฐ: %dx%d, ์ฑ„๋„: %d\n", width, height, channels);
        
        // ์ด๋ฏธ์ง€ ์ฒ˜๋ฆฌ...
        
        // PNG๋กœ ์ €์žฅ
        stbi_write_png("output.png", width, height, channels, data, width * channels);
        
        // ๋ฉ”๋ชจ๋ฆฌ ํ•ด์ œ
        stbi_image_free(data);
    }
    
    return 0;
}

stb_image๋Š” ์ •๋ง ๊ฐ„๋‹จํ•˜๊ณ  ๊ฐ€๋ฒผ์›Œ์„œ ์ž‘์€ ํ”„๋กœ์ ํŠธ์— ๋”ฑ์ด์•ผ! ๐Ÿ‘

๐ŸŽ“ ์žฌ๋Šฅ๋„ท์—์„œ ์ „๋ฌธ๊ฐ€์™€ ํ•จ๊ป˜ํ•˜๊ธฐ

์ด๋ฏธ์ง€ ์ฒ˜๋ฆฌ์™€ ์ปดํ“จํ„ฐ ๋น„์ „์€ ์ •๋ง ๋ฐฉ๋Œ€ํ•œ ๋ถ„์•ผ์•ผ. ์—ฌ๊ธฐ์„œ ๋‹ค๋ฃฌ ๊ฑด ๋น™์‚ฐ์˜ ์ผ๊ฐ์— ๋ถˆ๊ณผํ•ด! ์žฌ๋Šฅ๋„ท์—์„œ๋Š” ์‹ค๋ฌด ๊ฒฝํ—˜์ด ํ’๋ถ€ํ•œ ์ „๋ฌธ๊ฐ€๋“ค์ด ๋” ๊นŠ์ด ์žˆ๋Š” ๋‚ด์šฉ์„ ๊ฐ€๋ฅด์ณ์ค˜. ๋จธ์‹ ๋Ÿฌ๋‹์„ ํ™œ์šฉํ•œ ์ด๋ฏธ์ง€ ๋ถ„๋ฅ˜, ๊ฐ์ฒด ๊ฒ€์ถœ, ์–ผ๊ตด ์ธ์‹ ๊ฐ™์€ ์ตœ์‹  ๊ธฐ์ˆ ๋“ค๋„ ๋ฐฐ์šธ ์ˆ˜ ์žˆ์–ด. ํ˜ผ์ž ๊ณต๋ถ€ํ•˜๋‹ค ๋ง‰ํžˆ๋ฉด ์ „๋ฌธ๊ฐ€์˜ ๋„์›€์„ ๋ฐ›๋Š” ๊ฒƒ๋„ ์ข‹์€ ๋ฐฉ๋ฒ•์ด์•ผ! ๐ŸŒŸ

๐ŸŽฏ ๋งˆ๋ฌด๋ฆฌํ•˜๋ฉฐ

์™€, ์ •๋ง ๊ธด ์—ฌ์ •์ด์—ˆ์ง€? ์šฐ๋ฆฌ๋Š” ํ”ฝ์…€์˜ ๊ธฐ๋ณธ ๊ฐœ๋…๋ถ€ํ„ฐ ์‹œ์ž‘ํ•ด์„œ ๋ณต์žกํ•œ ์ปดํ“จํ„ฐ ๋น„์ „ ๊ธฐ๋ฒ•๊นŒ์ง€ ํ•จ๊ป˜ ์‚ดํŽด๋ดค์–ด. C์–ธ์–ด๋กœ ์ด๋ฏธ์ง€๋ฅผ ์ง์ ‘ ๋‹ค๋ฃจ๋ฉด์„œ ๋งŽ์€ ๊ฑธ ๋ฐฐ์› ์„ ๊ฑฐ์•ผ! ๐ŸŽ‰

๐ŸŽจ ๋ฐฐ์šด ๋‚ด์šฉ ์ •๋ฆฌ

โ€ข ์ด๋ฏธ์ง€์˜ ๊ธฐ๋ณธ ๊ตฌ์กฐ
โ€ข BMP ํŒŒ์ผ ์ฝ๊ธฐ/์“ฐ๊ธฐ
โ€ข ํ”ฝ์…€ ๋‹จ์œ„ ์กฐ์ž‘
โ€ข ์ปจ๋ณผ๋ฃจ์…˜๊ณผ ํ•„ํ„ฐ๋ง
โ€ข ํŠน์ง• ๊ฒ€์ถœ ๊ธฐ๋ฒ•
โ€ข ์„ฑ๋Šฅ ์ตœ์ ํ™”

๐Ÿ’ก ํ•ต์‹ฌ ํฌ์ธํŠธ

โ€ข ์ด๋ฏธ์ง€๋Š” ์ˆซ์ž์˜ ๋ฐฐ์—ด
โ€ข ํ”ฝ์…€ ์กฐ์ž‘์ด ๋ชจ๋“  ๊ฒƒ์˜ ๊ธฐ์ดˆ
โ€ข ์ปค๋„๋กœ ๋‹ค์–‘ํ•œ ํšจ๊ณผ ๊ตฌํ˜„
โ€ข ๋ฉ”๋ชจ๋ฆฌ ๊ด€๋ฆฌ๊ฐ€ ์ค‘์š”
โ€ข ์ตœ์ ํ™”๋กœ ์„ฑ๋Šฅ ํ–ฅ์ƒ
โ€ข ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ํ™œ์šฉ๋„ ์ค‘์š”

์ด๋ฏธ์ง€ ์ฒ˜๋ฆฌ๋Š” ์ •๋ง ์žฌ๋ฏธ์žˆ๋Š” ๋ถ„์•ผ์•ผ. ๋ˆˆ์— ๋ณด์ด๋Š” ๊ฒฐ๊ณผ๊ฐ€ ๋ฐ”๋กœ ๋‚˜์˜ค๋‹ˆ๊นŒ ์„ฑ์ทจ๊ฐ๋„ ํฌ๊ณ ! ์—ฌ๊ธฐ์„œ ๋ฐฐ์šด ๊ธฐ์ดˆ๋ฅผ ๋ฐ”ํƒ•์œผ๋กœ ๋” ๋ณต์žกํ•œ ํ”„๋กœ์ ํŠธ์— ๋„์ „ํ•ด๋ด. ์ž์œจ์ฃผํ–‰, ์˜๋ฃŒ ์˜์ƒ ๋ถ„์„, AR/VR, ๋กœ๋ด‡ ๋น„์ „ ๋“ฑ ์ •๋ง ๋‹ค์–‘ํ•œ ๋ถ„์•ผ์—์„œ ์ด๋ฏธ์ง€ ์ฒ˜๋ฆฌ๊ฐ€ ์‚ฌ์šฉ๋˜๊ณ  ์žˆ์–ด. ๐Ÿš€

๐ŸŒŸ ๋‹ค์Œ ๋‹จ๊ณ„ ์ถ”์ฒœ

1. OpenCV ๋ฐฐ์šฐ๊ธฐ - ์‹ค๋ฌด์—์„œ ๊ฐ€์žฅ ๋งŽ์ด ์‚ฌ์šฉ๋˜๋Š” ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์•ผ
2. ๋จธ์‹ ๋Ÿฌ๋‹ ๊ณต๋ถ€ํ•˜๊ธฐ - ๋”ฅ๋Ÿฌ๋‹์„ ํ™œ์šฉํ•œ ์ด๋ฏธ์ง€ ์ธ์‹์€ ์ •๋ง ๊ฐ•๋ ฅํ•ด
3. ์‹ค์ „ ํ”„๋กœ์ ํŠธ ๋งŒ๋“ค๊ธฐ - ์–ผ๊ตด ์ธ์‹, ๊ฐ์ฒด ์ถ”์ , ์ด๋ฏธ์ง€ ํ•„ํ„ฐ ์•ฑ ๋“ฑ
4. ์ˆ˜ํ•™ ๊ณต๋ถ€ํ•˜๊ธฐ - ์„ ํ˜•๋Œ€์ˆ˜์™€ ์‹ ํ˜ธ์ฒ˜๋ฆฌ ์ด๋ก ์„ ์•Œ๋ฉด ๋” ๊นŠ์ด ์ดํ•ดํ•  ์ˆ˜ ์žˆ์–ด
5. ์ปค๋ฎค๋‹ˆํ‹ฐ ์ฐธ์—ฌํ•˜๊ธฐ - ๋‹ค๋ฅธ ๊ฐœ๋ฐœ์ž๋“ค๊ณผ ์ง€์‹์„ ๋‚˜๋ˆ„๊ณ  ๋ฐฐ์›Œ!

C์–ธ์–ด๋กœ ์ด๋ฏธ์ง€ ์ฒ˜๋ฆฌ๋ฅผ ๋ฐฐ์šฐ๋ฉด์„œ ์ €์ˆ˜์ค€ ํ”„๋กœ๊ทธ๋ž˜๋ฐ์˜ ์žฌ๋ฏธ๋„ ๋А๊ผˆ์„ ๊ฑฐ์•ผ. ๋ฉ”๋ชจ๋ฆฌ๋ฅผ ์ง์ ‘ ๋‹ค๋ฃจ๊ณ , ํฌ์ธํ„ฐ๋กœ ๋ฐ์ดํ„ฐ๋ฅผ ์กฐ์ž‘ํ•˜๊ณ , ์ตœ์ ํ™”๋ฅผ ๊ณ ๋ฏผํ•˜๋Š” ๊ณผ์ •์ด ์ฒ˜์Œ์—” ์–ด๋ ค์›Œ ๋ณด์ด์ง€๋งŒ ์ต์ˆ™ํ•ด์ง€๋ฉด ์ •๋ง ๊ฐ•๋ ฅํ•œ ๋„๊ตฌ๊ฐ€ ๋ผ. ๐Ÿ’ช

์•ž์œผ๋กœ๋„ ๊ณ„์† ๊ณต๋ถ€ํ•˜๊ณ  ์‹คํ—˜ํ•ด๋ด. ์‹คํŒจ๋ฅผ ๋‘๋ ค์›Œํ•˜์ง€ ๋ง๊ณ ! ๋ฒ„๊ทธ๋ฅผ ๋งŒ๋‚˜๋ฉด ๋””๋ฒ„๊น…ํ•˜๋ฉด์„œ ๋ฐฐ์šฐ๊ณ , ์„ฑ๋Šฅ์ด ๋А๋ฆฌ๋ฉด ํ”„๋กœํŒŒ์ผ๋งํ•˜๋ฉด์„œ ์ตœ์ ํ™”ํ•˜๊ณ . ๊ทธ๋ ‡๊ฒŒ ํ•˜๋‚˜์”ฉ ํ•ด๊ฒฐํ•ด๋‚˜๊ฐ€๋‹ค ๋ณด๋ฉด ์–ด๋А์ƒˆ ์ „๋ฌธ๊ฐ€๊ฐ€ ๋˜์–ด ์žˆ์„ ๊ฑฐ์•ผ. ๐Ÿ˜Š

์ด ๊ธ€์ด ์ด๋ฏธ์ง€ ์ฒ˜๋ฆฌ์™€ ์ปดํ“จํ„ฐ ๋น„์ „์˜ ์„ธ๊ณ„๋กœ ๋“ค์–ด๊ฐ€๋Š” ์ฒซ๊ฑธ์Œ์ด ๋˜์—ˆ์œผ๋ฉด ์ข‹๊ฒ ์–ด. ๊ถ๊ธˆํ•œ ๊ฒŒ ์žˆ๊ฑฐ๋‚˜ ๋” ๋ฐฐ์šฐ๊ณ  ์‹ถ๋‹ค๋ฉด ์–ธ์ œ๋“ ์ง€ ๊ณต๋ถ€๋ฅผ ๊ณ„์†ํ•ด! ๊ทธ๋ฆฌ๊ณ  ์žฌ๋Šฅ๋„ท ๊ฐ™์€ ํ”Œ๋žซํผ์—์„œ ์ „๋ฌธ๊ฐ€๋“ค์˜ ๋„์›€์„ ๋ฐ›๋Š” ๊ฒƒ๋„ ์žŠ์ง€ ๋งˆ. ํ•จ๊ป˜ ๋ฐฐ์šฐ๊ณ  ์„ฑ์žฅํ•˜๋Š” ๊ฒŒ ๊ฐ€์žฅ ๋น ๋ฅธ ๊ธธ์ด๋‹ˆ๊นŒ! ๐ŸŒˆ

์ž, ์ด์ œ ์ง์ ‘ ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•˜๊ณ  ์‹คํ—˜ํ•ด๋ณผ ์‹œ๊ฐ„์ด์•ผ. ์นด๋ฉ”๋ผ๋กœ ์ฐ์€ ์‚ฌ์ง„์„ ๋ถˆ๋Ÿฌ์™€์„œ ์—ฌ๋Ÿฌ ํ•„ํ„ฐ๋ฅผ ์ ์šฉํ•ด๋ณด๊ณ , ์—ฃ์ง€๋ฅผ ๊ฒ€์ถœํ•ด๋ณด๊ณ , ํŠน์ง•์„ ์ถ”์ถœํ•ด๋ด. ๊ทธ๋ฆฌ๊ณ  ์ž์‹ ๋งŒ์˜ ์ฐฝ์˜์ ์ธ ์ด๋ฏธ์ง€ ์ฒ˜๋ฆฌ ์•Œ๊ณ ๋ฆฌ์ฆ˜์„ ๋งŒ๋“ค์–ด๋ด! ๐ŸŽจ

ํ–‰์šด์„ ๋นŒ์–ด! ๊ทธ๋ฆฌ๊ณ  ์ฆ๊ฑฐ์šด ์ฝ”๋”ฉ ๋˜๊ธธ! Happy Coding! ๐Ÿš€โœจ

๋Œ“๊ธ€ ์ž‘์„ฑ

์ด ๊ธ€์— ๋Œ€ํ•œ ์—ฌ๋Ÿฌ๋ถ„์˜ ์ƒ๊ฐ์„ ๋“ค๋ ค์ฃผ์„ธ์š”

๋Œ“๊ธ€ 0
๋Œ“๊ธ€์„ ๋ถˆ๋Ÿฌ์˜ค๋Š” ์ค‘์ž…๋‹ˆ๋‹ค.

์•„์ง ๋Œ“๊ธ€์ด ์—†์Šต๋‹ˆ๋‹ค.