Ver3.0 ๐จ C์ธ์ด๋ก ์์ํ๋ ์ด๋ฏธ์ง ์ฒ๋ฆฌ์ ํฝ์ ์กฐ์: ์ปดํจํฐ ๋น์ ์ ์ฒซ๊ฑธ์

๐จ C์ธ์ด๋ก ์์ํ๋ ์ด๋ฏธ์ง ์ฒ๋ฆฌ์ ํฝ์ ์กฐ์: ์ปดํจํฐ ๋น์ ์ ์ฒซ๊ฑธ์
ํฝ์ ํ๋ํ๋๋ฅผ ์ง์ ๋ง์ง๋ฉด์ ๋ฐฐ์ฐ๋ ์ปดํจํฐ ๋น์ ์ ์ธ๊ณ
๐ก ์ด๋ฏธ์ง ์ฒ๋ฆฌ๋ผ๊ณ ํ๋ฉด ๋ญ๊ฐ ์์ฒญ ๋ณต์กํ๊ณ ์ด๋ ค์ด ๊ฒ ๊ฐ์ง? ํฌํ ์ต ๊ฐ์ ํ๋ก๊ทธ๋จ๋ง ๋ ์ฌ๋ฆฌ๊ฑฐ๋, ์๋๋ฉด ์ต์ AI ๊ธฐ์ ๋ง ์๊ฐํ๊ฒ ๋๋๋ฐ ๋ง์ด์ผ. ํ์ง๋ง ์ฌ์ค ์ด๋ฏธ์ง ์ฒ๋ฆฌ์ ํต์ฌ์ ์๊ฐ๋ณด๋ค ๋จ์ํด. ๋ฐ๋ก ํฝ์ (Pixel)์ด๋ผ๋ ์์ ์ ๋ค์ ํ๋ํ๋ ์กฐ์ํ๋ ๊ฑฐ์ผ! ๐ฏ
C์ธ์ด๋ ์ด๋ฐ ์ ์์ค(Low-level) ์์ ์ ํ๊ธฐ์ ์ ๋ง ์๋ฒฝํ ์ธ์ด์ผ. ๋ฉ๋ชจ๋ฆฌ๋ฅผ ์ง์ ๋ค๋ฃฐ ์ ์๊ณ , ํฌ์ธํฐ๋ฅผ ํตํด ๋ฐ์ดํฐ์ ๋น ๋ฅด๊ฒ ์ ๊ทผํ ์ ์๊ฑฐ๋ . ์ค๋์ C์ธ์ด๋ก ์ด๋ฏธ์ง๋ฅผ ์ด๋ป๊ฒ ์ฝ๊ณ , ํฝ์ ์ ์กฐ์ํ๊ณ , ๊ฐ๋จํ ์ปดํจํฐ ๋น์ ๊ธฐ๋ฒ๋ค์ ๊ตฌํํ๋์ง ์น๊ตฌ์ฒ๋ผ ํธํ๊ฒ ์ด์ผ๊ธฐํด๋ณผ๊ฒ! ๐
๐ผ๏ธ ์ด๋ฏธ์ง๋ ๊ฒฐ๊ตญ ์ซ์์ ๋ฐฐ์ด์ด๋ค
์, ๋จผ์ ๊ฐ์ฅ ๊ธฐ๋ณธ์ ์ธ ๊ฐ๋ ๋ถํฐ ์ง๊ณ ๋์ด๊ฐ์. ์ฐ๋ฆฌ๊ฐ ํ๋ฉด์์ ๋ณด๋ ์๋ฆ๋ค์ด ์ฌ์ง์ด๋ ๊ทธ๋ฆผ๋ค์ ์ฌ์ค ์์ฒญ๋๊ฒ ๋ง์ ์ซ์๋ค์ ์งํฉ์ด์ผ. ๋ง์น ๋ ๊ณ ๋ธ๋ก์ผ๋ก ์ฑ์ ๋ง๋๋ ๊ฒ์ฒ๋ผ, ์์ ํฝ์ ๋ค์ด ๋ชจ์ฌ์ ํ๋์ ์ด๋ฏธ์ง๋ฅผ ๋ง๋๋ ๊ฑฐ์ง. ๐งฑ
๐ก ํฝ์ (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;
}
}
์ด ๊ธฐ๋ฒ์ ๋ฌธ์ ์ธ์์ด๋ ๊ฐ์ฒด ๋ถ๋ฆฌ์ ์ ๋ง ์ ์ฉํด. ๋ฐฐ๊ฒฝ๊ณผ ์ ๊ฒฝ์ ๋ช ํํ๊ฒ ๊ตฌ๋ถํ ์ ์๊ฑฐ๋ ! ๐
๐ ์ปจ๋ณผ๋ฃจ์ ๊ณผ ํํฐ๋ง์ ์ธ๊ณ
์ด์ ์ข ๋ ์ฌ๋ฏธ์๋ ๋ถ๋ถ์ผ๋ก ๋ค์ด๊ฐ๋ณด์! ์ปจ๋ณผ๋ฃจ์ (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);
}
์ฐ๊ฒฐ ์์ ๋ถ์์ ๊ฐ์ฒด๋ฅผ ๊ฐ๋ณ์ ์ผ๋ก ๋ค๋ฃฐ ์ ์๊ฒ ํด์ค. ๊ฐ ๊ฐ์ฒด์ ๋ฉด์ , ์ค์ฌ, ๊ฒฝ๊ณ ์์ ๊ฐ์ ๊ฑธ ๊ณ์ฐํ ์ ์์ง! ๐ฏ
โก ์ฑ๋ฅ ์ต์ ํ์ ์ค์ ํ
์ด๋ฏธ์ง ์ฒ๋ฆฌ๋ ๊ณ์ฐ๋์ด ๋ง์์ ์ต์ ํ๊ฐ ์ ๋ง ์ค์ํด! ํนํ ์ค์๊ฐ ์ฒ๋ฆฌ๊ฐ ํ์ํ ๊ฒฝ์ฐ์๋ ๋์ฑ ๊ทธ๋ ์ง. ๋ช ๊ฐ์ง ์ค์ ํ์ ์๋ ค์ค๊ฒ. ๐
๋ฉ๋ชจ๋ฆฌ ๊ด๋ฆฌ
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
์ง์์ธ์ ์ฒ - ์ง์ ์ฌ์ฐ๊ถ ๋ณดํธ ๊ณ ์ง
์ง์ ์ฌ์ฐ๊ถ ๋ณดํธ ๊ณ ์ง
- ์ ์๊ถ ๋ฐ ์์ ๊ถ: ๋ณธ ์ปจํ ์ธ ๋ ์ฌ๋ฅ๋ท์ ๋ ์ AI ๊ธฐ์ ๋ก ์์ฑ๋์์ผ๋ฉฐ, ๋ํ๋ฏผ๊ตญ ์ ์๊ถ๋ฒ ๋ฐ ๊ตญ์ ์ ์๊ถ ํ์ฝ์ ์ํด ๋ณดํธ๋ฉ๋๋ค.
- AI ์์ฑ ์ปจํ ์ธ ์ ๋ฒ์ ์ง์: ๋ณธ AI ์์ฑ ์ปจํ ์ธ ๋ ์ฌ๋ฅ๋ท์ ์ง์ ์ฐฝ์๋ฌผ๋ก ์ธ์ ๋๋ฉฐ, ๊ด๋ จ ๋ฒ๊ท์ ๋ฐ๋ผ ์ ์๊ถ ๋ณดํธ๋ฅผ ๋ฐ์ต๋๋ค.
- ์ฌ์ฉ ์ ํ: ์ฌ๋ฅ๋ท์ ๋ช ์์ ์๋ฉด ๋์ ์์ด ๋ณธ ์ปจํ ์ธ ๋ฅผ ๋ณต์ , ์์ , ๋ฐฐํฌ, ๋๋ ์์ ์ ์ผ๋ก ํ์ฉํ๋ ํ์๋ ์๊ฒฉํ ๊ธ์ง๋ฉ๋๋ค.
- ๋ฐ์ดํฐ ์์ง ๊ธ์ง: ๋ณธ ์ปจํ ์ธ ์ ๋ํ ๋ฌด๋จ ์คํฌ๋ํ, ํฌ๋กค๋ง, ๋ฐ ์๋ํ๋ ๋ฐ์ดํฐ ์์ง์ ๋ฒ์ ์ ์ฌ์ ๋์์ด ๋ฉ๋๋ค.
- AI ํ์ต ์ ํ: ์ฌ๋ฅ๋ท์ AI ์์ฑ ์ปจํ ์ธ ๋ฅผ ํ AI ๋ชจ๋ธ ํ์ต์ ๋ฌด๋จ ์ฌ์ฉํ๋ ํ์๋ ๊ธ์ง๋๋ฉฐ, ์ด๋ ์ง์ ์ฌ์ฐ๊ถ ์นจํด๋ก ๊ฐ์ฃผ๋ฉ๋๋ค.
์์ง ๋๊ธ์ด ์์ต๋๋ค.