격자를 컬러 이미지로 변환하기

CBeginner
지금 연습하기

소개

작은 인쇄 작업실에 색상 격자로 만든 엽서가 필요합니다. 먼저 완성된 4×4 예제를 실행하고 잉크를 따뜻한 색으로 바꾼 뒤, 직사각형 문자 도안을 실제 이미지 파일로 변환하는 C 프로그램을 완성합니다.

C 변수, 조건문, 반복문, 배열, 함수와 기본 터미널 탐색을 알고 있어야 합니다. 2차원 인덱스와 이미지 출력은 여기서 설명합니다. 준비된 브라우저 환경에는 Workspace 터미널과 Postcard Preview가 있습니다. 이미지는 읽을 수 있는 숫자로 이루어진 P3 PPM 파일이며 PNG가 아닙니다. 읽기와 연습 시간은 작성자의 추정치이며 초보자를 대상으로 측정한 완료 시간이 아닙니다.

작은 예제를 출력하고 색상 바꾸기

이 단계에서는 완성된 C 예제가 이미지로 바뀌는 모습을 확인한 뒤 잉크 색 하나를 바꿉니다.

Workspace 탭을 엽니다. 터미널은 /home/labex/project/postcards에 있습니다. 아래에 명시한 cd는 다른 디렉터리로 이동한 뒤 돌아올 때도 사용할 수 있습니다. pwd는 현재 디렉터리를 표시하고 ls는 그 안의 파일을 나열합니다.

cd /home/labex/project/postcards
pwd
ls

준비된 파일은 sample.c, 미완성 card.c, 제공된 art.h입니다. sample.c와 card.c만 변경하고 art.h는 그대로 둡니다. cat은 파일을 편집하지 않고 표시합니다.

cat sample.c

예제는 4행 4열 격자를 사용합니다. grid[row][col]에서 첫 인덱스는 위에서 아래로 행을 선택하고, 두 번째는 왼쪽에서 오른쪽으로 열을 선택합니다. 둘 다 0부터 시작합니다. 각 항목은 0 또는 1이며 color의 RGB 세 성분을 선택합니다. RGB는 빨강, 초록, 파랑이며 각 성분은 0부터 255까지의 정수입니다. color[c][0]의 두 번째 인덱스는 빨강, [1]은 초록, [2]는 파랑을 선택합니다.

printf는 표준 출력에 텍스트를 씁니다. 보통 표준 출력은 이 터미널입니다. \n은 새 줄을 시작합니다. P3 파일은 P3, 너비와 높이, 성분의 최댓값 255 순서로 시작합니다. 그다음 RGB 세 성분이 이어지며 각 행은 왼쪽에서 오른쪽으로, 전체는 맨 위 행에서 아래 행으로 읽습니다. 공백 문자가 숫자를 구분하므로 한 줄에 RGB 한 묶음을 써도 유효합니다. P3는 # 주석도 허용합니다.

예제를 컴파일합니다. gcc는 C 컴파일러입니다. -std=c11은 C11을 선택하고, -Wall -Wextra는 유용한 경고를 켜며, -o sample은 실행 파일의 이름을 sample로 지정합니다.

gcc -std=c11 -Wall -Wextra sample.c -o sample

./sample은 현재 디렉터리의 실행 파일을 실행합니다. >는 표준 출력을 터미널 대신 파일로 리다이렉션합니다. 셸은 프로그램이 실행되기 전에 그 파일을 열거나 덮어씁니다. 아래에 지정한 새 파일 이름을 사용하세요.

./sample > sample.ppm

Postcard Preview 탭으로 전환합니다. Refresh files를 누르고 Postcard file에서 sample.ppm을 선택한 다음 Open postcard를 누릅니다. 상태에는 4 × 4 pixels · sample.ppm이 표시됩니다. 크림색 배경 위에 어두운 비대칭 무늬가 보일 것입니다. 미리보기는 방금 만든 파일을 표시하며 빠진 픽셀을 채우지 않습니다.

Workspace로 돌아옵니다. 어두운 잉크 {30, 76, 64}를 따뜻한 색 {170, 70, 45}로 바꿉니다. 아래 텍스트 편집 명령 sed는 s/old/new/로 정확히 그 텍스트를 치환하고, -i로 변경을 sample.c에 저장합니다. 작은따옴표는 치환식을 변경되지 않은 하나의 인수로 sed에 전달합니다.

sed -i 's/{30, 76, 64}/{170, 70, 45}/' sample.c

소스를 수정한 뒤에는 다시 컴파일해야 합니다. 기존 실행 파일은 저절로 바뀌지 않습니다.

gcc -std=c11 -Wall -Wextra sample.c -o sample
./sample > sample.ppm

Postcard Preview로 전환하고 sample.ppm을 선택한 상태에서 Open postcard를 다시 누릅니다. 전경색만 바뀌고 크기와 크림색 픽셀은 그대로여야 합니다.

터미널에서 파일을 찾을 수 없다고 하면 배운 cd와 ls로 작업 공간에 돌아옵니다. 미리보기에서 파일이 없다고 하면 Workspace로 돌아가 컴파일 성공을 확인한 뒤 ./sample > sample.ppm을 실행합니다. 컴파일러 오류가 있으면 새 실행 파일은 생성되지 않은 것입니다. 지적된 소스 줄을 고치고 다시 컴파일하세요.

직사각형 도안을 RGB 픽셀에 대응시키기

이 단계에서는 엽서 생성기를 완성하고 실제 출력을 엽니다.

Workspace의 /home/labex/project/postcards에서 시작합니다. 제공된 도안과 미완성 생성기를 읽습니다.

cd /home/labex/project/postcards
cat art.h
cat card.c

#include "art.h"는 card.c를 컴파일할 때 준비된 정의를 사용할 수 있게 합니다. #define WIDTH 5와 #define HEIGHT 3은 크기에 이름을 붙입니다. pattern은 행이 3개이고 각 행에는 숫자 문자 5개가 있습니다. C는 보이는 문자 뒤에 값이 0인 종료 문자를 저장하므로, 각 문자열 행은 WIDTH + 1개 문자를 위한 공간을 둡니다. 반복문은 보이는 5개 위치만 사용합니다.

첫 행 "10000"은 팔레트 색 1로 시작하고 색 0인 픽셀 4개가 뒤따릅니다. 문자 '2'는 정수 2가 아닙니다. 문자 '0'을 빼면 제공된 숫자 문자를 팔레트 인덱스로 바꿀 수 있습니다. '2' - '0'은 2입니다. 도안에는 0부터 3까지의 숫자만 있으며 팔레트의 네 행에 대응합니다. 도안, 크기, 색을 유지하세요. 반복문에 WIDTH와 HEIGHT를 사용해, 같은 팔레트 인덱스 0부터 3을 사용하는 가로·세로 각각 1부터 16인 제공된 직사각형 도안도 다시 컴파일하면 처리할 수 있게 합니다.

미완성 생성기는 헤더만 쓰고 픽셀은 쓰지 않습니다. 파일 헤더만으로는 이미지가 아닙니다. y(행)를 순회하는 바깥 반복문과 x(열)를 순회하는 안쪽 반복문을 추가합니다. pattern[y][x]는 문자를 선택하고 palette[c][0..2]는 RGB 값을 선택합니다. printf의 %d 자리는 나열된 순서대로 정수 인수를 출력합니다.

다음 완전한 소스를 사용합니다. cat > card.c는 그 파일을 덮어씁니다. <<'EOF'는 here-document를 시작합니다. 셸은 EOF만 있는 줄을 만날 때까지 뒤의 줄을 그대로 복사합니다. 구분자를 따옴표로 감싸면 C 소스 안에서 셸 확장이 일어나지 않습니다. 마지막 EOF를 포함한 전체 블록을 복사하세요.

cat > card.c <<'EOF'
#include <stdio.h>
#include "art.h"

int main(void) {
    printf("P3\n%d %d\n255\n", WIDTH, HEIGHT);
    for (int y = 0; y < HEIGHT; y++) {
        for (int x = 0; x < WIDTH; x++) {
            int c = pattern[y][x] - '0';
            printf("%d %d %d\n", palette[c][0], palette[c][1], palette[c][2]);
        }
    }
    return 0;
}
EOF

컴파일하고 새로운 출력 파일을 생성합니다.

gcc -std=c11 -Wall -Wextra card.c -o card
./card > original.ppm

제공된 검사 도구는 파일을 디코딩할 뿐 엽서를 생성하거나 복구하지 않습니다. 파일 이름을 인수로 전달해 실행합니다.

python3 /home/labex/.local/share/pixel-postcards/inspect.py original.ppm

예상 출력:

5 x 3 pixels; 15 RGB triples; max value 255

이 숫자가 왜 유용할까요? 정사각형에서는 너비와 높이를 바꾼 실수를 놓칠 수 있습니다. 이 5열 3행 도안은 가로가 더 길어야 하며 어두운 띠가 왼쪽에 있어야 합니다. WIDTH는 안쪽 반복문, HEIGHT는 바깥 반복문을 제어합니다. 둘을 바꾸면 행의 범위를 벗어나거나 픽셀을 빠뜨릴 수 있습니다.

Postcard Preview로 전환합니다. Refresh files를 누르고 original.ppm을 선택한 뒤 Open postcard를 누릅니다. 5 × 3 상태를 확인하고 각 행을 art.h와 비교합니다. Download PPM을 누르면 실제 이미지 파일을 저장합니다. 브라우저가 PPM을 바로 보여 주지 않고 다운로드할 수 있으므로, 보려면 이 미리보기를 사용하세요. 실습 검사를 위해 Workspace로 돌아옵니다.

스크린샷에는 선택된 original.ppm, 저장된 크기 5 × 3, 이미지 아래의 Download PPM이 보입니다. 왼쪽의 어두운 띠와 세 행을 모두 비교하세요.

Postcard Preview의 직사각형 엽서

card.c, 실행 파일 card, art.h, original.ppm을 작업 공간에 남겨 둡니다.

미리보기에서 픽셀 개수 오류가 나오면 파일은 있지만 내용이 불완전한 것입니다. 두 반복문의 범위를 확인하고 card.c를 저장한 뒤 다시 컴파일해 original.ppm을 새로 생성합니다. 화면에서 확대되어도 저장된 크기는 변하지 않습니다. 다음 실습에서는 실제로 확대된 이미지 파일을 직접 만듭니다.

요약

작은 예제를 컴파일하고 색을 바꾼 뒤 직사각형 문자 배열을 RGB 세 성분에 대응시켰습니다. C 프로그램이 실제 P3 파일을 만들었으며 이를 디코딩하고 미리 보고 다운로드했습니다. 행을 먼저 지정하는 인덱싱과 표준 출력 리다이렉션도 연습했습니다.