[자바] 이미지 프로세싱 - 1

2023. 4. 13. 19:27프로그래밍

728x90

PGM,PBM 이미지 파일 읽어 보기

문득, 나도 이미지 프로세싱에 대해서 관심이 많았다는 생각을 하고여러 가지 교재들을 어둠의 경로로 알아 보았다. 사실 히스토그램이라던지, 보간법 같은 것들에 대해서 용어에 대해서 들어는 보았지만, 여전히그게 정말 무엇을 하는 것인지 정확하게 파악하고 있지는 않았다.

 

근데 찾아보다가, 참 좋은 사이트가 눈에 띄였고, C/C++ 로 코딩되어 있는 내용이 있어서 그냥 눈으로 따라가다가 그럼 자바로 한번 만들어 볼까 하는 생각이들어서 그렇게 해보기로 하였다.

사이트는 http://oneday0012.tistory.com/13 이고, 거의 강의가 완성되어 있는 것으로 보인다.

 

그냥 편한하게 따라가보아도 무방할 정도로 쉽게 쓰여져 있어서

 

이것 저것 건너뛰고 파일 읽어 보는 프로그램을 우선 만들어 보자

참고로 사이트에 용어 정리가 되어 있으나, 요약 하자면 다음과 같이 PGM,PBM을 정의 할 수 있다고 한다.

•PBM(Portable BitMap) : 흑백영상

•PGM(Portable GrayMap) : 그레이영상

•PPM(Portalble PixMap) : 컬러영상
 

그리고 이미지는 사이트에 있는 이미지 파일을 그대로 써보기로 한다.

 

사이트에서 툴로 사용하는 XnView를 사용해서 위의 이미지를 변환하여사용한다.

그리고 XnView의 설치가 귀찮은 관계로 그냥 포터블 버전을 사용하기로했다.

 

 

그리고 아래와 같이 저장한다.

 

사이트에서도 헥사뷰어로 확인 한 모습이 있다.

근데 헤더만 ASCII로 되어 있는 모양새이다.

그리고 읽어 들이는 부분은 그냥 C/C++ 코드를 그대로 차용한 모습으로가져가면 되겠다.

근데, 구조체는 사실 모든 사람이 알다시피 자바에서는 없기 때문에클래스를 사용하도록 한다. 내부에 아래 정도 클래스를 만들면 될 듯하다.

class PGMImage
{
	byte M, N; //매직 넘버
	int width;
	int height;
	int max;
	byte pixels[][];
};
 

자바에서는 char가 byte 정도로표한 하면 된다. 왜냐하면 자바에서 char는 유니코드를받아 줄 수 있도록 16비트이기 때문이다.

이 파일을 읽어 들이고 다시 써보면 제대로 파일을 읽고 썼는 지 여부는 금방 알 수 있을 것이다.

소스 코드에 따라서 파일에서 읽는 부분을 구현해 보면,

boolean fnReadPGM(final String fileNm, PGMImage pgmImg)
{
	RandomAccessFile fp = null;
	File file = new File(fileNm);

	if(!file.exists()){
	System.err.println("fnReadPGM file doesn’t exist\n");
	return false;
	}

	try
	{
		// binary mode
		fp = new RandomAccessFile(file, "r");
		//read magic number
		pgmImg.M = fp.readByte();
		pgmImg.N = fp.readByte();

		if(pgmImg.M != 'P' || pgmImg.N != '5'){
			
			System.err.printf("Not a PGM header : %c%c\n", pgmImg.M, pgmImg.N);
			
			if(fp != null) try {fp.close();} catch (IOException e) {}
			
			return false;
		}
	}
	catch(Exception e)
	{
		e.printStackTrace();
		System.err.printf("Exception occurred while access file: %s\n", fileNm);
		return false;
	}

	//read width height, image intensity
	try {
		//skip carrage return
		fp.readLine();
		String wh[] = fp.readLine().split(" ");
		pgmImg.width = Integer.valueOf(wh[0]);
		pgmImg.height = Integer.valueOf(wh[1]);
		pgmImg.max = Integer.valueOf(fp.readLine());
	} catch (IOException e1) {
		e1.printStackTrace();
	}

	if(pgmImg.max != 255){
		System.err.println("Not valid image intensity.\n");
		if(fp != null) try {fp.close(); } catch (IOException e) {}
		return false;
	}

	// memory allocation of the image
	pgmImg.pixels = new byte[pgmImg.height][pgmImg.width];

	//for(int i=0; i<pgmImg.height; i++){
	// pgmImg.pixels[i] = new byte[pgmImg.width];
	//}

	try
	{
		// <-- load pixel values of the image
		for(int i=0; i<pgmImg.height; i++){
			for(int j=0; j<pgmImg.width; j++){
				pgmImg.pixels[i][j] = fp.readByte();
			}
		}
	}
	catch(Exception e)
	{
		e.printStackTrace();
		return false;
	}
	finally
	{
		if(fp != null) try {fp.close(); } catch (IOException e) {}
		fp = null;
	}

	return true;
}
 

 

비슷한 형태로 구현해 보기 위해서 그나마 비슷한 RandomAccessFile 클래스를 사용해서 구현해 보았다.

그리고파일에쓰는부분은다음과같이구현하였다.파일에서읽는부분의반대이므로,코드를보는것이오히려더 나을 듯… 별 내용이 없다.

boolean fnWritePGM(final String fileNm, final PGMImage pgmImg)
{
	RandomAccessFile fp = null;
	File file = new File(fileNm);

	try
	{
		fp = new RandomAccessFile(file, "rw"); // binary mode
		//write magic number
		fp.writeByte(pgmImg.M);
		fp.writeByte(pgmImg.N);
	}
	catch(Exception e)
	{
		e.printStackTrace();
		System.err.printf("Exception occurred while creating random access file: %s\n", fileNm);
		return false;
	}

	try {
		fp.write('\n');
		fp.writeBytes(pgmImg.width+" "+pgmImg.height);
		fp.write('\n');
		fp.writeBytes(String.valueOf(pgmImg.max));
		fp.write('\n');
	} catch (IOException e1) {
		e1.printStackTrace();
	}

	try
	{
		int tmp;

		for(int i=0; i<pgmImg.height; i++){
			for(int j=0; j<pgmImg.width; j++){
				tmp = pgmImg.pixels[i][j];

				if(tmp > 255){
					tmp = 255;
				}
				fp.write(tmp);
			}
		}
	}
	catch(Exception e)
	{
		e.printStackTrace();
		return false;
	}
	finally
	{
		if(fp != null) try {fp.close(); } catch (IOException e) {}
		fp = null;
	}
	return true;	
}
 

그럼 위의 두개의 메서드를 사용해서 위에서 생성한 파일을 읽어들였다가 다시 다른 이름으로 써보도록 하는 메인메서드를 만들어 보기로 한다.

 

ImageProcessingTest test = new ImageProcessingTest();
PGMImage pgmImg = new PGMImage();
String fileNm = ".\\sample1.pgm";
String fileNm = ".\\sample2.pgm";

test.fnReadPGM(fileNm, pgmImg);
test.fnWritePGM(fileNm2, pgmImg);
 

똑 같은 파일을 생성 하여 보았다.

 

오늘은 여기 까지...

 

이상.