언어/C

C언어 영상처리 과제 - compression, Quantization

이게될까 2024. 6. 5. 15:39
728x90
728x90

C언어 문자열 다루는게 기억이 안나서 막 짰는데 그것 때문에 코드가 많이 지저분해 진 것 같은데,,,

#define _CRT_SECURE_NO_WARNINGS
#include<math.h>
#include<stdio.h>
#include<stdlib.h>
#include <string.h>
#include<windows.h>

void DPCMEncoder(double*, int*, int, BITMAPFILEHEADER, BITMAPINFOHEADER);
void write_matrix_to_file(const char* filename, int* matrix, int height, int width);
void makeOutFile(BITMAPFILEHEADER bmpFile, BITMAPINFOHEADER bmpInfo, char* fileName, double* y);


int main() {
	BITMAPFILEHEADER bmpFile, bmpFile2;
	BITMAPINFOHEADER bmpInfo, bmpInfo2;
	FILE* inputFile = NULL, * inputFile2 = NULL;
	inputFile = fopen("AICenterY.bmp", "rb"); // 원본 영상

	fread(&bmpFile, sizeof(BITMAPFILEHEADER), 1, inputFile);
	fread(&bmpInfo, sizeof(BITMAPINFOHEADER), 1, inputFile);

	int width = bmpInfo.biWidth, height = bmpInfo.biHeight, size = bmpInfo.biSizeImage, bitCnt = bmpInfo.biBitCount, stride = (((bitCnt / 8) * width) + 3) / 4 * 4, max;

	double* y;
	y = (double*)calloc(width * height, sizeof(double));

	unsigned char* inputImg = NULL;
	inputImg = (unsigned char*)calloc(size, sizeof(unsigned char));

	fread(inputImg, sizeof(unsigned char), size, inputFile);

	// 값 받아오기
	for (int j = 0; j < height; j++) {
		for (int i = 0; i < width; i++) {
			y[j * width + i] = inputImg[j * stride + 3 * i];
		}
	}

	// 저장 공간, q값 정하기
	int* prsy, q = 20;
	prsy = (int*)calloc(width * height, sizeof(int));

	// 인코딩 시작
	DPCMEncoder(y, prsy, q, bmpFile, bmpInfo);

	return 0;
}



void DPCMEncoder(double* org, int* prsy, int q, BITMAPFILEHEADER bmpFile, BITMAPINFOHEADER bmpInfo) {
	int width = bmpInfo.biWidth, height = bmpInfo.biHeight, size = bmpInfo.biSizeImage;
	double* pe, * rs;
	pe = (double*)calloc(width * height, sizeof(double));
	rs = (double*)calloc(width * height, sizeof(double));
	for (int j = 0; j < height; j++) {
		for (int i = 0; i < width; i++) {
			// 여긴 시작이라 128을 빼줄 곳
			if (i == 0) {
				pe[j * width + i] = org[j * width + i] - 128;
				// q로 나누기
				prsy[j * width + i] = (int)(pe[j * width + i] / q);
				// bit 범위 넘어가는 곳은 제한해주기
				if (prsy[j * width + i] < -3) prsy[j * width + i] = -4;
				else if (prsy[j * width + i] > 2)prsy[j * width + i] = 3;
				rs[j * width + i] = 128 + prsy[j * width + i] * q;
			}
			// 여기는 시작 부분이 아니라 직전 값으로 더한다.
			else {
				pe[j * width + i] = org[j * width + i] - rs[j * width + i - 1];
				// q로 나누기
				prsy[j * width + i] = (int)(pe[j * width + i] / q);
				//bit 범위 넘어가는 곳은 제한해주기
				if (prsy[j * width + i] < -3) prsy[j * width + i] = -4;
				else if (prsy[j * width + i] > 2)prsy[j * width + i] = 3;

				rs[j * width + i] = rs[j * width + i - 1] + prsy[j * width + i] * q;
			}

		}
	}
	// 사진 출력
	makeOutFile(bmpFile, bmpInfo, "reconEncY.bmp", rs);


	// 양자화 값 출력
	write_matrix_to_file("bitstream.txt", prsy, height, width);


	free(rs);
	free(pe);
}

void write_matrix_to_file(const char* filename, int* matrix, int height, int width) {
	FILE* file = fopen(filename, "w");
	if (file == NULL) {
		perror("파일 열기 실패");
		return;
	}
	
	// 값에 따라 000 ~ 111 txt 파일에 작성하기
	for (int j = 0; j < height; j++) {
		for (int i = 0; i < width; i++) {
			if (matrix[j * width + i] == -4) fprintf(file, "%s","000");
			if (matrix[j * width + i] == -3) fprintf(file, "%s", "001");
			if (matrix[j * width + i] == -2) fprintf(file, "%s", "010");
			if (matrix[j * width + i] == -1) fprintf(file, "%s", "011");
			if (matrix[j * width + i] == 0) fprintf(file, "%s", "100");
			if (matrix[j * width + i] == 1) fprintf(file, "%s", "101");
			if (matrix[j * width + i] == 2) fprintf(file, "%s", "110");
			if (matrix[j * width + i] == 3) fprintf(file, "%s", "111");
		}
	}

	fclose(file);
}

// 사진 만들어서 내보내기
void makeOutFile(BITMAPFILEHEADER bmpFile, BITMAPINFOHEADER bmpInfo, char* fileName, double* y) {
	int width = bmpInfo.biWidth, height = bmpInfo.biHeight, size = bmpInfo.biSizeImage, bitCnt = bmpInfo.biBitCount, stride = (((bitCnt / 8) * width) + 3) / 4 * 4;
	unsigned char* outputImg = NULL;
	outputImg = (unsigned char*)calloc(size, sizeof(unsigned char));
	// Y값 넘기기
	for (int j = 0; j < height; j++) {
		for (int i = 0; i < width; i++) {
			for (int k = 0; k < 3; k++) outputImg[j * stride + 3 * i + k] = (unsigned char)(y[j * width + i] > 255 ? 255 : (y[j * width + i] < 0 ? 0 : y[j * width + i]));
		}
	}
	// 파일 만드기
	FILE* outputFile = fopen(fileName, "wb");
	fwrite(&bmpFile, sizeof(BITMAPFILEHEADER), 1, outputFile);
	fwrite(&bmpInfo, sizeof(BITMAPINFOHEADER), 1, outputFile);
	fwrite(outputImg, sizeof(unsigned char), size, outputFile);

	// 변수 제거 
	fclose(outputFile);
	free(outputImg);
}

인코더는 단순합니다. 사진 받아서 이전 값에서 빼주기 -> 양자화(q로 나누기) -> 양자화된 것 보내기 

#define _CRT_SECURE_NO_WARNINGS
#include<math.h>
#include<stdio.h>
#include<stdlib.h>
#include <string.h>
#include<windows.h>

void DPCMDecoder(int* prsy, int q, BITMAPFILEHEADER bmpFile, BITMAPINFOHEADER bmpInfo);
void read_matrix_from_file(const char* filename, int* matrix, int rows, int cols);
void makeOutFile(BITMAPFILEHEADER bmpFile, BITMAPINFOHEADER bmpInfo, char* fileName, double* y);


int main() {
	BITMAPFILEHEADER bmpFile ;
	BITMAPINFOHEADER bmpInfo;
	FILE* inputFile = NULL;
	inputFile = fopen("AICenterY.bmp", "rb"); // 원본 영상

	fread(&bmpFile, sizeof(BITMAPFILEHEADER), 1, inputFile);
	fread(&bmpInfo, sizeof(BITMAPINFOHEADER), 1, inputFile);

	int width = bmpInfo.biWidth, height = bmpInfo.biHeight, size = bmpInfo.biSizeImage, bitCnt = bmpInfo.biBitCount, stride = (((bitCnt / 8) * width) + 3) / 4 * 4, max;


	// q = 20, 저장할 공간 만들기
	int* prsy, q = 20;
	prsy = (int*)calloc(width * height, sizeof(int));

	// 디코더 시작
	DPCMDecoder(prsy, q, bmpFile, bmpInfo);

	free(prsy);


	return 0;
}


void DPCMDecoder(int* prsy, int q, BITMAPFILEHEADER bmpFile, BITMAPINFOHEADER bmpInfo) {
	int width = bmpInfo.biWidth, height = bmpInfo.biHeight, size = bmpInfo.biSizeImage;
	double* rs;

	rs = (double*)calloc(width * height, sizeof(double));
	// prsy에 값 받아오기
	read_matrix_from_file("bitstream.txt", prsy, height, width);

	for (int j = 0; j < height; j++) {
		for (int i = 0; i < width; i++) {
			if (i == 0) {
				// 0위치는 128에서 더하기
				rs[j * width + i] = 128 + prsy[j * width + i] * q;
			}
			else {
				// 나머지 위치는 직전 값에서 더하기
				rs[j * width + i] = rs[j * width + i - 1] + prsy[j * width + i] * q;

			}

		}
	}

	// 파일 만들기
	makeOutFile(bmpFile, bmpInfo, "reconDecY.bmp", rs);
	free(rs);

}

void read_matrix_from_file(const char* filename, int* matrix, int height, int width) {
	FILE* file = fopen(filename, "r");
	if (file == NULL) {
		perror("파일 열기 실패");
		return;
	}
	int a[3];

	// 파일 읽기 시작
	for (int j = 0; j < height; j++) {
		for (int i = 0; i < width; i++) {
			for (int k = 0; k < 3; k++) {
				// 값 한개씩 읽어서 저장하기
				a[k]=fgetc(file);
			}
			
			if (a[0] == '0') { // 0
				if (a[1] == '0') { // 00
					if (a[2] == '0') { //000
						matrix[j * width + i] = -4;
					}
					else { //001
						matrix[j * width + i] = -3;
					}
				}
				else { // 01
					if (a[2] == '0') { //010
						matrix[j * width + i] = -2;
					}
					else { //011
						matrix[j * width + i] = -1;
					}
				}
			}
			else { //1
				if (a[1] == '0') { // 10
					if (a[2] == '0') { //100
						matrix[j * width + i] = 0;
					}
					else { //101
						matrix[j * width + i] = 1;
					}
				}
				else {// 11
					if (a[2] == '0') {//110
						matrix[j * width + i] = 2;
					}
					else { // 111
						matrix[j * width + i] = 3;
					}
				}
			}
			
		}
	}

	fclose(file);
}

// 사진 만들어서 내보내기
void makeOutFile(BITMAPFILEHEADER bmpFile, BITMAPINFOHEADER bmpInfo, char* fileName, double* y) {
	int width = bmpInfo.biWidth, height = bmpInfo.biHeight, size = bmpInfo.biSizeImage, bitCnt = bmpInfo.biBitCount, stride = (((bitCnt / 8) * width) + 3) / 4 * 4;
	unsigned char* outputImg = NULL;
	outputImg = (unsigned char*)calloc(size, sizeof(unsigned char));
	// Y값 넘기기
	for (int j = 0; j < height; j++) {
		for (int i = 0; i < width; i++) {
			for (int k = 0; k < 3; k++) outputImg[j * stride + 3 * i + k] = (unsigned char)(y[j * width + i] > 255 ? 255 : (y[j * width + i] < 0 ? 0 : y[j * width + i]));
		}
	}
	// 파일 만드기
	FILE* outputFile = fopen(fileName, "wb");

	// 파일 작성
	fwrite(&bmpFile, sizeof(BITMAPFILEHEADER), 1, outputFile);
	fwrite(&bmpInfo, sizeof(BITMAPINFOHEADER), 1, outputFile);
	fwrite(outputImg, sizeof(unsigned char), size, outputFile);

	// 변수 제거 
	fclose(outputFile);
	free(outputImg);
}

디코더는 더 단순하네요

양자화된 것 받아서 복구하기

 

메모장에는 대충 이렇게 되어 있습니다.

101100100100100100101011100100101100100100011100100100100100100101011100100100100001100100011100100100011100100100110010100111010011111001100111100000111101000110110001101111000100111010011111010011111010011111010011111011010111011010111100001111100001111100001111100001111011001111101001110101001110100010111100010100100100100100100100100100100100100110100101100010100100100100100101011100100101100100011100100100100011011100100100011101100101100101100011101101100101100011011101100011100011100100100100111011010101101011100100101011100100100100100100101100011100101100100100100011100100100100110100100100011011101101100100010100100100100100101101100100100100100011011100011100100100101101100011101101100100100100100100100101010110011100110100011100100011100100100100100010110100100100100010110100100100100101100010100011011010101110100101010011100100100100100101100100100110100100100011100100100101011101100100100100100100100100100100100100100100100100011100100100100011100100100100110100100110011011100100101101100100100011100100100100100100100100100101100011011100100101011100100100100010101100100101100100011100010100100101100100100100100011100100101100100100011100100101100100100100100101100101100010110011100100100100011101100011100100100100101100100011101100100100100011100100100100100100100011100110101100100100100100011100100100100100100100100100100100100100100100100101100100100011011100100100101101011011100100100110100100011101100100100100100101010100100011101110100011011100101100100010100110100100101011100011100010100100101100100100100100100100100101100011011100101100100100100100100100100100100011101101100011001100100011100100100011100100100110001110111000100111010011111010011111010011111011001110111000101111000101111000101111000101111000100111001100111001100111010100111001011111010011111010011111010011111011010111001100111011010100101100011100101100100100100100100100100101101100011100100100101100100011011101100100100100100100100010100100100100100100100101101011100100110101100011100011100101100100100011100100100100101011100100100100100100101011100100100100100101100100100100100100100011100100100101100100100100100100011100100101101100010100011100100100101100101110100100100100011010011100100100100110100100100011010101111011100101100101100011011011111011100101100100100100101011011100100100010101101011101011011100100110

한줄로 된 0과 1의 연속입니다.

저기서 3개씩 받아와서 읽습니다.

 

인코더 사진

q가 크다보니 손상이 크네요...

3비트밖에 안해줘서 양자화 범위가 -4 ~ 3밖에 안되는 것도 이 사진에 문제가 되겠네요

디코더 사진

둘의 psnr 은 99.99입니다!

728x90