본문 바로가기
대학교 과제 및 프로젝트/선형대수학

[C언어] 선형대수학 이미지 압축(image compression)

by davidlee_ 2022. 4. 4.
반응형

대학교 1학년 2학기, 선형대수학 과제였던 image compression 과제에 대해 기록하고자 한다.
(추후 이 강의를 듣거나 관련 과제를 하는 후배들에게 조금이나마 도움을 주기 위해)

처음 이 문제를 접했을 때 파이썬의 open cv 라이브러리를 쓰면 금방 해결할 수 있을 것이라고 생각했다.
이 문장을 보기 전까진..

무조건 C언어로만 코드를 짜야한다는 당부사항이었다.
과제를 계속 미뤄두고 있었던 터라 과제 제출 하루 전에 코드 작성을 시작했다. 다행히도 교수님께서 프로젝트 설명 영상을 찍어주셔서 문제에 대한 이해는 어렵지 않게 할 수 있었다. 하지만 이해한 내용을 코드로 작성하는 것은 전혀 다른 문제였다.

문제의 전체적인 맥락은 Haar wavelet transform을 이용해서 image compression을 하는 것이 주된 목적이었다. 계산에 필요한 함수들이 많았는데 이를 모두 구현해야 했다.
먼저 계산에 필요한 kronecker함수, identity함수, normalize함수, transpose함수 그리고 기본적인 행렬 계산 함수들을 구현했다.

 

kronecker함수

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
float** kronecker(float** A, int A_m, int A_n, float** B, int B_m, int B_n) {
    int stRow, stCol;
    float** S = malloc(sizeof(float** A_m * B_m);
    for (int x = 0; x < A_m * B_m; x++) {
        S[x] = malloc(sizeof(float* A_n * B_n);
    }
 
    for (int i = 0; i < A_m; i++) {
        for (int j = 0; j < A_n; j++) {
            stRow = i * B_m;
            stCol = j * B_n;
            for (int k = 0; k < B_m; k++) {
                for (int l = 0; l < B_n; l++) {
                    S[stRow + k][stCol + l] = A[i][j] * B[k][l];
                }
            }
        }
    }
 
    return S;
 
}
cs

크로네커 곱 함수이다. 인자값으로 행렬, 행렬의 크기를 각각 넘겨주면 크로네커 곱에 의해 생성된 행렬 S를 반환해주게 된다.

 

identity함수

1
2
3
4
5
6
7
8
9
10
11
float** identity(int size) {
    float** A = malloc(sizeof(float** size);
    for (int i = 0; i < size; i++) {
        A[i] = malloc(sizeof(float** size);
        memset(A[i], 0sizeof(float* size);
    }
    for (int i = 0; i < size; i++) {
        A[i][i] = 1;
    }
    return A;
}
cs

단위행렬 생성 함수이다. 행렬의 크기를 인자값으로 넘겨주면 단위행렬 A를 반환한다.

 

normalize함수

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
float** normalize(float** Haar, int n) {
    float** Haar_nor;
    Haar_nor = (float**)malloc(sizeof(float** n);
    for (int i = 0; i < n; i++) {
        Haar_nor[i] = (float*)malloc(sizeof(float* n);
    }
 
    float* sum = (float*)malloc(sizeof(float* n);
    for (int col = 0; col < n; col++) {
        sum[col] = 0;
        for (int row = 0; row < n; row++) {
            sum[col] += Haar[row][col] * Haar[row][col];
        }
    }
    for (int i = 0; i < n; i++) {
        sum[i] = sqrt(sum[i]);
    }
    for (int col = 0; col < n; col++) {
        for (int row = 0; row < n; row++) {
            Haar_nor[row][col] = Haar[row][col] / sum[col];
        }
    }
    return Haar_nor;
}
cs

정규화 함수이다. 인자값으로 정규화할 행렬과 행렬의 크기를 넘겨주면 정규화된 행렬을 반환해주게 된다. 필자는 변환할 행렬이 정방행렬이었기 때문에 n값 하나만 넘겨주었다.

 

transpose함수

1
2
3
4
5
6
7
8
9
10
11
12
float** transposeMat(float** A, int m, int n) {
    float** B = malloc(sizeof(float** n);
 
    for (int x = 0; x < n; x++) {
        B[x] = malloc(sizeof(float* m);
        for (int y = 0; y < m; y++) {
            B[x][y] = A[y][x];
        }
    }
 
    return B;
}
cs

전치행렬로 변환해주는 함수이다. 인자값으로 행렬과 행렬의 크기를 넘겨주면 전치된 행렬 B가 반환된다.

 

multiply함수

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
float** multiplyMat(float** A, float** B, int n) {
    float** mutiMat;
    mutiMat = (float**)malloc(sizeof(float** n);
    for (int i = 0; i < n; i++) {
        mutiMat[i] = (float*)malloc(sizeof(float* n);
    }
    for (int k = 0; k < n; k++) {
        for (int j = 0; j < n; j++) {
            mutiMat[k][j] = 0;
            for (int i = 0; i < n; i++) {
                mutiMat[k][j] += A[k][i] * B[i][j];
            }
        }
    }
    return mutiMat;
}
 
cs

행렬의 곱셈 함수이다. 인자값으로 두 행렬 A,B 행렬의 크기 n을 넘겨주면 두 행렬의 곱인 mutiMat을 반환해준다.

필자는 크기가 같은 두 정방행렬만 곱셈하면 되었기에 n값 하나만 인자로 넘겨주었다.

 

 

위 함수들은 직접 정의한 함수들이다.

밤새면서 작성했던 함수들이라 주석이 없을뿐더러 체계적이지 않은 변수명 때문에 이해하는데 조금 걸릴 수도 있다.

교수님께서 이미지 파일을 불러오는 코드 등 전체적인 틀은 짜주셔서, 정의한 함수를 사용하여 변환 과정만 수행하면 되었기 때문에 함수들을 짜는 것 외에는 큰 어려움은 없었다. (물론 다양한 오류들로 인해 애는 먹었다.)

 

image compression 결과물들

픽셀값을 다르게 하여 결과물을 출력했다.

제일 왼쪽 상단 이미지부터 차례대로 8,16,64,128,256, 원본 이미지 순이다.

과제의 조건에서 grayscale 이미지가 입력 조건이라고 명시되었기에 필자는 24bit grayscale 이미지를 사용하였다. (사진은 image compression에서 사용되는 국룰 예시인 lena이미지를 사용하였다.)

 

위와 같은 결과값 출력을 위해 디버깅만 3시간을 했던 것 같다.

검은 화면만 보일 때도 있었고, lena의 모습이 잘려서 보이기도 했다. 하지만 다행히 결과값이 성공적으로 출력되었고, 보고서를 조금 급하게 썼지만 기한 내에 제출할 수 있었다.

 

몇 가지 핵심 코드들은 일부러 공개하지 않았다. (복사 붙여 넣기만 한다면 실력은 늘지 않기 때문이다.) 하지만 아마 저 위의 함수들을 잘 활용한다면 큰 어려움 없이 이미지 압축을 할 수 있을 것이다.

 

image compression의 원리를 조금 더 자세히 알고 싶다면 이 영상을 추천한다.

MIT - image compression

반응형

댓글