프로그래밍/C++2009. 7. 16. 15:03

STL의 컨테이너로 List라는게 있다

List는 우리가 흔히 사용하는 연결리스트 같은것 이라고 생각하면 된다

우선 간단하게 리스트에서 출력하는 방법이다

for(itor=list1.begin(); itor != list1.end(); itor++)
  cout << *itor;

'프로그래밍 > C++' 카테고리의 다른 글

소수점 반올림  (0) 2011.03.18
C++ 디버깅시 TRACE 사용하기  (0) 2009.12.10
파일 입출력 및 링크드 리스트  (1) 2009.05.01
C/C++ 로깅 Facility  (0) 2009.04.25
Abstract Factory Pattern  (0) 2009.04.22
Posted by 마블(이환문)
프로그래밍/C++2009. 5. 1. 13:06

input.txt 파일을 읽어와서 output.csv 파일을 만드는 프로그램이다

여기에 추가적으로 input.txt 파일에 보면 각 날짜의 시간별로 온도, 습도, 강수량 등이 기록되어 있다

이를 날짜별으로 평균 온도, 최대 최소 온도, 표준편차 등을 구해서 Reprot.csv 파일로 만드는 프로그램이다

#include 
#include 
#include 
#include 

typedef struct Weather
{
	char day[11];
	char time[6];
	float temperature;
	float humidty;
	float rainfall;
	float insolation;
	float sunshine_duration;
	char windy[5];
	float wind_velocity;
	float max_wind_velocity;
	struct Weather *next;
}Weather;

typedef struct 
{
	Weather* head;
}Weather_h;

Weather_h* CreateNode(void);
void PrintList();
void CreateCSVfile(Weather_h *L, Weather *p);
void CreateReportFile(Weather *p);
float Standard_Deviation(Weather *p, Weather* nextNdoe, float avgTmp, bool end, int count);
void FreeNode(Weather_h *L, Weather *p);

void main()
{
	Weather_h* L;

	L = CreateNode();


	PrintList();

}

Weather_h* CreateNode(void)
{
	Weather_h* L;
	L = (Weather_h *)malloc(sizeof(Weather_h));
	L->head = NULL;
	return L;
}

void PrintList()
{
	Weather* p;
	Weather* newData;
	Weather_h* L;
	L = (Weather_h *)malloc(sizeof(Weather_h));
	L->head = NULL;

	FILE *fp;

	if((fp = fopen("input.txt", "r")) == NULL)
	{
		printf("File is not found!\n");
	}

	p = L->head;

	fseek(fp, 110,SEEK_SET);

	puts("=========================================================================");
	puts("계측시간 온도(℃) 습도(%) 강수량(mm) 일사량(w/m2) 일조시간(hour) 풍향 풍속(m/s) 최대풍속(m/s)");

	while(!feof(fp))
	{
		newData = (Weather *)malloc(sizeof(Weather));

		fscanf(fp, "%s %s %f %f %f %f %f %s %f %f ", newData->day, newData->time, &newData->temperature, &newData->humidty, &newData->rainfall, &newData->insolation, &newData->sunshine_duration, &newData->windy, &newData->wind_velocity, &newData->max_wind_velocity);

		if(L->head == NULL)
		{
			L->head = newData;
			p = newData;
			newData->next = NULL;

		}
		else
		{
			p->next = newData;
			newData->next = NULL;
		}

		while(p->next != NULL)
		{

			p = p->next;
		}
	}

	p = L->head;
	CreateCSVfile(L, p);
	CreateReportFile(p);
	FreeNode(L, p);
	
	puts("\ncvs 파일과 report.csv 파일을 성공적으로 만드셨습니다.");
	puts("=========================================================================");

	fclose(fp);


}



void CreateCSVfile(Weather_h* L, Weather* p)
{
	FILE *fp;
	Weather* newData;
	newData = p;

	if((fp = fopen("output.csv", "w")) == NULL)
	{
		printf("File is not found!\n");
	}

	fprintf(fp, "계측날짜, 시간,온도(℃), 습도(%), 강수량(mm), 일사량(w/m2), 일조시간(hour), 풍향, 풍속(m/s), 최대풍속(m/s) \n");
	while(newData->next != NULL)
	{
		fprintf(fp, "%s,", newData->day);
		fprintf(fp, " %s,", newData->time);
		fprintf(fp, " %.1f,", newData->temperature);
		fprintf(fp, " %.1f,", newData->humidty);
		fprintf(fp, " %.1f,", newData->rainfall);
		fprintf(fp, " %.1f,", newData->insolation);
		fprintf(fp, " %.1f,", newData->sunshine_duration);
		fprintf(fp, " %s,", newData->windy);
		fprintf(fp, " %.1f,", newData->wind_velocity);
		fprintf(fp, " %.1f\n", newData->max_wind_velocity);


		newData = newData->next;
	}

	fclose(fp);
}

void CreateReportFile(Weather* p)
{
	FILE *fp;
	Weather *nextNode, *temp, *startNode;
	float avgTem = 0;
	float sumTem = 0;
	int count=0;
	float Max = -1000;
	float Min = 1000;
	float sumHumidty = 0;
	float avgHumidty = 0;
	float sumRainfall = 0;
	float avgRainfall = 0;
	float sumSunshine_duration = 0;
	float avgSunshine_duration = 0;

	temp = p;
	startNode = p;
	nextNode = p->next;

	if((fp = fopen("Report.csv", "w")) == NULL)
	{
		printf("File is not found!\n");
	}

	fprintf(fp, "[일별기상통계표] 계측 지역:신촌 \n");
	fprintf(fp, "일자, ,기온(Temperature), , , 습도, 강수량, 일조시간\n");
	fprintf(fp, " , 평균, 표준편차, 최고, 최저, [%%], [mm], [hour]\n");


	while(p->next != NULL)
	{
		temp = startNode;

		if(strcmp(p->day, nextNode->day) == 0)
		{
			sumTem = sumTem + p->temperature;
			sumHumidty = sumHumidty + p->humidty;
			sumRainfall = sumRainfall + p->rainfall;
			sumSunshine_duration = sumSunshine_duration + p->sunshine_duration;

			count++;

			if(p->temperature > Max)
				Max = p->temperature;

			if(p->temperature < Min)
				Min = p->temperature;

			p = nextNode;
			nextNode = nextNode->next;


			if(p->next == NULL)
			{
				sumTem = sumTem + p->temperature;
				sumHumidty = sumHumidty + p->humidty;
				sumRainfall = sumRainfall + p->rainfall;
				sumSunshine_duration = sumSunshine_duration + p->sunshine_duration;
				count++;

				avgTem = sumTem / count;
				avgHumidty = sumHumidty / count;
				avgRainfall= sumRainfall / count;
				avgSunshine_duration = sumSunshine_duration / count;

				fprintf(fp, "%s, ", p->day);
				fprintf(fp, "%.1f, " , avgTem );
				fprintf(fp, "%.4f,", Standard_Deviation(temp, temp, avgTem, true, count));
				fprintf(fp, "%.1f, " , Max);
				fprintf(fp, "%.1f, " , Min);
				fprintf(fp, "%.1f, " , avgHumidty);
				fprintf(fp, "%.1f, " , avgRainfall);
				fprintf(fp, "%.1f\n " , avgSunshine_duration);

			}

		}

		else if(strcmp(p->day, nextNode->day) != 0)
		{
			sumTem = sumTem + p->temperature;
			sumHumidty = sumHumidty + p->humidty;
			sumRainfall = sumRainfall + p->rainfall;
			sumSunshine_duration = sumSunshine_duration + p->sunshine_duration;
			count++;

			avgTem = sumTem / count;
			avgHumidty = sumHumidty / count;
			avgRainfall= sumRainfall / count;
			avgSunshine_duration = sumSunshine_duration / count;

			if(p->temperature > Max)
				Max = p->temperature;

			fprintf(fp, "%s, ", p->day);
			fprintf(fp, "%.1f, " , avgTem);
			fprintf(fp, "%.4f,", Standard_Deviation(temp, nextNode, avgTem, false, count));
			fprintf(fp, "%.1f, " , Max);
			fprintf(fp, "%.1f, " , Min);
			fprintf(fp, "%.1f, " , avgHumidty);
			fprintf(fp, "%.1f, " , avgRainfall);
			fprintf(fp, "%.1f\n " , avgSunshine_duration);

			p = nextNode;
			nextNode = nextNode->next;
			startNode = p;
			avgTem = 0;
			sumTem = 0;
			sumRainfall = 0;
			sumHumidty = 0;
			sumSunshine_duration = 0;
			count = 0;
			Max = -1000;
			Min = 1000;

		}


	}



}

float Standard_Deviation(Weather* p, Weather* nextNode, float avgTmp, bool end, int count)
{
	Weather *temp = p;
	Weather *tempNext = nextNode;
	float sd = 0;
	float squaresum = 0;


	if(end == false)
	{
		while(strcmp(temp->day, tempNext->day) != 0)
		{
			squaresum = squaresum + (avgTmp - temp->temperature)*(avgTmp - temp->temperature);
			temp = temp->next;
		}
		squaresum = squaresum + (avgTmp - temp->temperature)*(avgTmp - temp->temperature);
		temp = temp->next;
	}
	else
	{
		while(temp->next != NULL)
		{
			squaresum = squaresum + (avgTmp - temp->temperature)*(avgTmp - temp->temperature);
			temp = temp->next;

		}
		squaresum = squaresum + (avgTmp - temp->temperature)*(avgTmp - temp->temperature);
		temp = temp->next;
	}
	sd = sqrt(squaresum) / count;
	return sd;	

}

void FreeNode(Weather_h *L, Weather *p)
{
	Weather *temp = L->head;


	if(L->head == NULL)
		return ;
	else
	{
		free(L);
		while(temp->next != NULL)
		{
			temp = p->next;
			free(p);
			p = temp;
		}
	}

}

'프로그래밍 > C++' 카테고리의 다른 글

C++ 디버깅시 TRACE 사용하기  (0) 2009.12.10
STL - List 사용하기  (0) 2009.07.16
C/C++ 로깅 Facility  (0) 2009.04.25
Abstract Factory Pattern  (0) 2009.04.22
PVOID  (0) 2009.04.22
Posted by 마블(이환문)

Java RMI (Remote Method Invocation)

 

1. RMI의 정의

- RMI란 원격 메서드 호출로 네트워크상에서 떨어져 있는 객체의 메서드를 투명하게 호출하는 것을 말한다.

 

2. RMI 의 개발 목적

① 안정된 원격 호출의 제공

② 서버에서 애플랫으로의 콜백 제공

③ 분산모델을 자바환경으로의 통합

④ 분산 객체와 비분산 객체의 명확한 구분

⑤ 안정된 분산 애플리케이션을 간편하게 만들수 있는 환경제공

⑥ 자바 런타임환경에의해 제공되는 안전성 유지

 

3. RMI 의 Interface 와 Class

- java.rmi 안에 정의 되어 있는 인터페이스들과 클래스들은 RMI System 의 상태(behavior)들을 기술하고 있다. 밑의 그림은 인터페이스들과 클래스들간의 관계를 나타낸 것이다.

 

 

 

 

 

 

 

 

 

 

 

 

4. RMI 시스템의 구조

- RMI 시스템은 stub/skeleton 래이어, remote reference 래이어, transport 래이어로 구성되어 있으며 각 래이어의 경계는 특별한 인터페이스와 프로토콜에 의해 정의되진다. 또한 각 래이어는 서로 독립적이며 따라서 다른 래이어에 영향을 주지 않고 한 래이어의 성질을 바꿀수 있다. 예들들어 설명하자면, 현재 transport 구현은 TCP를 바탕으로 하고 있으나 다른 래이어에 영향을 주지 않고 UDP 를 바탕으로한 transport 로의 교체가 가능한 것이다.

 

- 또 다른 테크닉으로 Dynamic stub loading 이 있다. 이는 remote interface를 구현하는 client-side stub 를 지원하기 위해 쓰인다. 예들들면 정확한 형의 stub 가 클라이언트에서 유효하지 않을경우 클라이언트가 자바에 내장된 연산자를 캐스팅과 타입채킹에 사용하도록 허용한다.

① stub/skeleton

- 클라이언트가 remote server의 객체를 호출할때 실재로 호출되는 부분으로 stub는 원 격 인터페이스의 구현으로 호출 요청을 remote reference layer를 통해 서버 객체로 보내는 역할을 담당한다. skeleton 은 그 반대로 생각하면 된다.

② remote reference layer

- 호출 정보를 분석해서 server 가 단일 객체인지 아니면 여러 사이트에 흩어져 있는 복수개의 객체인지를 판별후 호출을 실행한다. 또한 그 서버가 항상 실행상태인지 아니면 호출시에만 실행이되는지에 따른 참조방법을 추상화 한다. 따라서 상위 래이어에서는 이러한 차이점이 보이지 않게 된다.

③ transport layer

- 연결 setup, 연결 관리, 그리고 연결 감시등을 책임지는 래이어다. 각 래이어의 경계는 특별한 인터페이스와 프로토콜에 의해 정의되지며 각 래이어는 서로 독립적이며 따라서 다른 래이어에 영향을 주지 않고 한 래이어의 성질을 바꿀수 있다. 예들들어 설명하자면, 현재 transport 구현은 TCP를 바탕으로 하고 있으나 다른 래이어에 영향을 주지 않고 UDP 를 바탕으로한 transport 로의 교체가 가능한 것이다.

 5. RMI의 원리


 














6. RMI 순서

① 로컬 객체는 원격 객체의 메서드를 호출

② 스텁은 해당 메서드를 호출할 때 넘겨받은 인자들을 전달하기 위해, 원격지의 스켈레톤과 네트워크에서 통신하여, 해당 메서드를 호출

③ 원격지의 스켈레톤은 로켈 객체의 스텁으로부터 네트워크를 통하여 메서드 이름과 인자를 넘겨받는다.

④ 원격 객체에서 메서드가 실행되고 결과는 스켈레톤으로 넘겨진다.

⑤ 넘겨받은 스켈레톤이 결과를 로컬 컴퓨터의 스텁으로 넘겨준다.

⑥ 스텁은 스켈레톤에게 넘겨받은 결과를 원래의 로컬 객체에게 반환값으로 넘겨주게 된다.

 

7. RMI 프로그래밍 방법

 

















8. RMI 프로그래밍의 흐림

① 원격 인터페이스를 정의

② 원격객체를 프로그래밍

③ 원격객체의 클래스를 얻는다.

④ rmic 컴파일러로 스텁과 스켈레톤을 생성

⑤ 로컬객체를 프로그램화

⑥ 로컬객체를 컴파일

⑦ 자바보안정책 파일을 수정

⑧ RMI 레지스트리 서버를 실행

⑨ 원격 객체 클래스를 실행

⑩ 로컬 객체를 실행하여 RMI 기능을 이용


RPC(Remote prodecure call)

 

1. RPC의 정의

- 다른 네트워크의 컴퓨터에 있는 함수를 호출할 수 있도록 해주는 저 수준의 프로세스간 통신 방법

- 네트워크 프로그래밍을 함수 호출 레벨 수준으로 작성할 수 있게 해주는 API

- OSF DEC(Open Software Foundation Distributed Computing Environment)

① Microsoft SQL과 같은 RPC 활용 클라이언트/서버 모델 지원

② 클라이언트의 LPC가 네트워크 메시지로 변환되는 RPC 모델 지원

③ Lotus Notes에서 사용되는 것과 같은 형태의 데이터 공유 모델 지원

 

2. RPC의 유용성

① 서버의 CPU까지 공유할 수 있는 기능 제공

② 네트워크에서 이용 가능한 가장 강력한 CPU를 사용할 수 있도록 해준다

③ 네트워크에 연결된 컴퓨터들이 CPU를 편중되지 않게 고루 사용할 수 있다

④ Remote의 함수를 Local 함수와 동일하게 호출할 수 있다

⑤ 네트워크 transport나 protocol의 종류에 영향을 받지 않고 사용할 수 있다

 

3. RPC 메터니즘

- 서브/클라이언트 모델을 따르기 때문에, 데이터 통신을 위한 메커니즘은 단순하다고 할 수 있다. 클라이언트에서 서비스를 요청하면 서버에서 프로시져를 호출해서 처리하고 리턴값을 되돌려주는 방식이다.

 

4. RPC 데이터 흐름

① Client 데이터는 XDR 필터를 통과해서 인코딩된 후 전달된다.

② XDR 인코딩된 데이터는 네트워크를 가로질러서 원격지 호스트로 전달된다.

③ 데이터를 받은 서버는 XDR 필터를 통해서 디코딩한다.

④ 디코딩된 데이터를 처리하고 결과 데이터를 XDR 필터를 통해서 인코딩한다.

⑤ 인코딩된 XDR 데이터가 네트워크를 가로질러서 클라이언트에 전달된다.

⑥ 클라이언트는 XDR 디코딩을하고 결과를 처리한다.

 

5.RPC의 처리 과정

1) 작동순서

① 클라이언트의 작업 요청

② 입력 인수를 NDR(표준 네트워크 포맷)로 전환

③ 작업 요청 내용을 서버로 전송

④ 클라이언트의 작업 요청 내용 수신

⑤ NDR을 서버 함수가 이용할 수 있도록 전환

⑥ 서버 함수 호출

⑦ 작업 결과 반환

⑧ 출력 인수를 NDR로 전환

⑨ 작업 결과를 클라이언트로 전송

⑩ 서버로부터 전송된 작업 결과 수신

⑪ NDR의 작업 결과를 전환

⑫ 작업 결과를 받고, 하던 작업을 계속 수행

2) stub의 역할

- 주소공간에서 필요한 인수를 가져옴

- 인수들을 네트워크에 전송할 수 있는 형태(network transmission format)로 전환

- 작업을 요청 또는 반환하기 위해 RPC 라이브러리 호출

RMI와 RPC의 비교

- 프로그래머를 네트워크로부터 어느 정도 격리시켜준다는 공통점을 가지고 잇다. 그래서 RMI와 RPC 프로그래밍 모두 프로그래머가 네트워크에 대해 많은 신경을 쓰지 않고도 프로그램을 개발할 수 있도록 해준다. RMI와 RPC의 차이점은 RMI는 메소드 호출이 가능한 방식을 말하며, RPC는 단순히 다른 컴퓨터에 존재하는 프로그램의 프로시져 수행을 위한 것이다. RPC에 객체 지향이라는 옷을 입힌 것이 RMI라 볼 수 있다.

Posted by 마블(이환문)
프로그래밍/OpenGL2009. 4. 29. 12:21

1. Why is the use of Texture Mapping

- 적은 수의 폴리곤으로 효과적인 렌더링을 할 수 있다.

 

1.2. Steps of Texture Mapping

① 텍스처 오브젝트를 만들고, 오브젝트에 해당하는 텍스처를 명시해야 한다.

② 텍스처가 어떻게 각각의 픽셀과 연관되어 있는지에 고나해 나타내어야 한다.

③ 텍스처 매핑을 활성화 한다.

④ 텍스처와 기하학적 좌표가 제공된 장면을 렌더링 한다.

 

2. Example Texture Mapping

AUX_RGBImageRec *LoadBMPFile(char *filename)
{
	FILE *hFile = NULL
	if(!filename) 
		return NULL
	
	if(hFile = fopen(filename, "r"))
	{
		fclose(hFile);
		return auxDIBImageLoad(filename);
	// BMP 정보를 리턴해 준다
	}

	return NULL
}

void CreateTexture()
{
	AUX_RGBImageRec *texRec[1];	// RGB정보를 가지는 구조체 정의							// 텍스쳐매핑초기화
	if(texRec[0] = LoadBMPFile("img.bmp")) 
	{
		glGenTextures(1, &g_Texture[0]); 
		// 객체의 이름을 만들어 준다
		// 첫 번째 인자: 몇 개의 텍스쳐 텍스쳐 맵핑 소스를 생성할 것인지 지정
		// 두 번째 인자: 첫 번째 텍스쳐 맵핑 소스의 식별자의 주소값
		glBindTexture(GL_TEXTURE_2D, g_Texture[0]); 
		//앞으로 설정할 텍스쳐의 식별자를 지정
		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); 
		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);

		//GL_TEXTURE_MIN_FILTER: 맵핑 소스가 맵핑되어질 물체보다 클 경우
		//GL_TEXTURE_MAG_FILTER: 맵핑 소스가 맵핑되어질 물체보다 작을 경우
		//GL_NEAREST_MIPMAP_LINEAR   OpenGL   고화질
		//GL_NEAREST_MIPMAP_NEAREST  OpenGL   저화질. 권장.
		//GL_NEAREST_MIPMAP_LINEAR   OpenGL   고화질
		//GL_LINEAR_MIPMAP_NEAREST   Direct3D 저화질. 권장.
		//GL_LINEAR_MIPMAP_LINEAR    주로 배경(벽이나 지면의 재질감)의 설정
		//GL_LINEAR : 보간법
		//GL_NEAREST : 최근점법
		glTexImage2D(GL_TEXTURE_2D, 0, 3, texRec[0]->sizeX, texRec[0]->sizeY, 0, GL_RGB, GL_UNSIGNED_BYTE, texRec[0]->data);
		// 두 번째 인자 : 항상 0, 세 번째 인자: 색상 구성 요소의 개수 RGB 3		   개,여섯번째: 이미지 경계에 대한 픽셀 두께, 여덟 번째: 색상 구성 요		   소들이 갖는 값의 타입
	}

	if(texRec[0])		// 할당된 메모리를 해제
	{
		if(texRec[0]->data)
		{
			free(texRec[0]->data);
			free(texRec[0]);
		}

	}	

	glEnable(GL_TEXTURE_2D);		//텍스쳐 맵핑 사용함을 알림
	glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
}

void RenderScene()
{
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
	glLoadIdentity();

	gluLookAt(0, 0, 6,	0,0,0,	0,1,0);

	glBindTexture(GL_TEXTURE_2D, g_Texture[0]);
	glBegin(GL_QUADS);
		glTexCoord2f(0.0f, 1.0f);
		glVertex3f(-1, 1, 0);

		glTexCoord2f(0.0f, 0.0f);
		glVertex3f(-1, -1, 0);

		glTexCoord2f(1.0f, 0.0f);
		glVertex3f(1, -1, 0);

		glTexCoord2f(1.0f, 1.0f);
		glVertex3f(1, 1, 0);
	glEnd();

	SwapBuffers(g_hDC);
	
}

※ texel - 텍스처 데이터(컬러, 명도, 컬러와 알파 데이터)들의 사각형 배열 각각의 값
※ tessellation - 폴리곤의 각 꼬지점만을 명시해주면 자동으로 폴리곤을 오목한 부분이 없도록 쪼개서 연출해주는 기능

'프로그래밍 > OpenGL' 카테고리의 다른 글

옥트리(octree)  (0) 2009.04.25
OpenGL Viewing  (0) 2009.04.24
OpenGL 기초  (1) 2009.04.23
Picking 사용하기  (0) 2009.04.22
void glutIdleFunc(void (*func)(void))  (0) 2009.04.22
Posted by 마블(이환문)
프로그래밍/Multimedia2009. 4. 28. 18:07

1. DCT(이산여현변환)란?

- DCT를 이용하면 영상을 공간영역(Spatial Domain)으로부터 주파수 영역(Frequency Domain)으로 변환할 수 있으며 영상 데이터는 변화가 적으므로 낮은 주파수, 특히 0 주파수(DC) 성분이 큰 값을 갖게 되고 높은 주파수 성분은 상대적으로 매우 작은 값을 갖게 된다. 즉, 대부분의 정보가 낮은 주파수 쪽으로 몰리게 되므로 다음에 설명할 양자화 과정을 적절히 거치면 높은 압축률로 우수한 화질을 얻을 수 있다.

- 정보량이 많아 값이 낮은 주파수 성분만을 충실히 취하고 높은 주파수 성분은 값이 아주 작으므로 거의 무시해 버림으로써 전체 데이터양을 줄이는 원리인데, 높은 주파수 성분은 영상의 세밀한 해상도에 기여하기 때문에 이런 방법을 사용해 심하게 압축하면 해상도 저하라는 불이익을 감수해야 한다. DCT는 많은 곱하기와 더하기가 사용되므로 계산량이 상당히 많으나, 고속 알고리즘으로 인해 영상을 8*8 블럭으로 나눠 계산함으로써 계산 효율을 높일 수 있다.이 DCT를 용한 영상압축 방법은 성능과 실제 구현성이 좋아 정지 영상 뿐 아니라 동영상 압축에서도 폭 넓게 사용되고 있다.

 

1.1 DCT의 정의

- 두 개의 정수 값 i, j에 의해 표현된 함수(한 정지 영상)가 주어졌을 때 2D DCT 변환은 이 함수를 i와 j와 동일한 범위를 가지는 정수 u, v로 표현되는 새로운 함수로 변환된다.

i, u = 0,1,...,M-1 j, v = 0,1,...,N-1

- JPEG 영상 압축 표준에서 영상 블록은 차원 M = N = 8을 갖는 것으로 정의된다.

 

2. 2D DCT(2차원 이산 여현 변환)

i, j, u, v = 0,1,...,7

2.1 2D IDCT(2차원 역이산 여현 변환)

i, j, u, v = 0,1,...,7

3. 1D DCT

- f(i)가 시간 i에 따라 변화하는 신호를 표현.

- 시공간의 신호 f(i)를 주파수 공간의 신호 F(u)로 변환.

3.1 1D DCT의 예

 

예) f(i)가 (a)와 같이 주어졌을 경우에 F(u)를 구하시오

예) (d)는 임의의 입력 신호 f(i)에 대한 그 DCT 출력 F(u)를 나타내고 있다.

f(i)(i=0...7) : 85 -65 15 30 -56 35 90 60

F(u)(u=0...7) : 69 -49 74 11 16 117 44 -5

임을 보이시오.

 

3.2 1D IDCT

예) F(u)(u=0...7) : 69 -49 74 11 16 117 44 -5 값이 다음과 같을 때 f(i)를 구하시오

'프로그래밍 > Multimedia' 카테고리의 다른 글

디지털 오디오의 기초  (0) 2009.04.27
Posted by 마블(이환문)
프로그래밍/Multimedia2009. 4. 27. 13:55

1. 사운드의 디지털화

1.1 사운드란 무엇인가?

- 불빛과 같은 파동 현상을 말하기도 하고, 어떤 물리적인 장치의 동작에 의해 공기가 압축되고 팽창하는 분자 운동과 관련된다.

 

1.2 디지털화

- 아날로그 신호를 디지털화하기 위해서 sampling을 수행하여야 함.

- sampling : 균일한 시간 간격으로 값을 측정하는 방법.

- sampling frequency : sampling이 취해지는 비율.

- quantization : 진폭이나 전압의 sampling을 의미.

그림 1. 아날로그 신호의 예

1.3 Nyquist 이론

- 정확한 sampling을 위해서는 적어도 신호의 최대 주파수 성분의 두 배로 sampling이 이루어져야 한다.

- Nyquist 율과 동일한 주파수를 의미.

그림 4. 구형파 신호의 중첩

1.4 Aliasing

- 분석하고자 하는 신호를 sampling 주파수로 sampling하여 주파수 분석을 하였을 때 sampling 주파수의 절반 이상에 해당하는 주파수성분이 Nyquist 주파수 이하의 성분으로 겹쳐 나타나게 되는 현상

 

그림 7. 주파수의 1.5배 sampling

 

1.5 신호 대 잡음 비(SNR)

- 신호 대 잡음 비(SNR) : 정확한 신호와 잡음의 비율을 의미.

- SNR값은 dB을 단위로 하고 전압의 제곱에 대한 상용로그 값으로 정의됨.

 

P is average power, A is RMS amplitude

 

예제) 신호의 전압 이 잡음의 10배라면, SNR은 이다.

 

 

 

 

1.6 신호 대 잡음 비(SQNR)

- 양자화 잡음 : 원신호에 비교하여 양자화된 아날로그 신호의 오차분

- 샘플당 N 비트에 대한 디지털 신호의 범위는 에서

- 실제 아날로그 신호가 에서 범위에서 존재한다면, 각 양자화 단계는 이걸로 표시한다.

- SQNR은 단지 신호의 최대값으로 표현되고, 이것은 약 의 단계 로 대응.

- SQNR은 분모로 최대값 = 를 가진다

6.02N(dB)는 피크 신호 대 잡음비(PSQNR)을 나타내고 있다.

최악의 경우로 입력파가 구형파라면 아래와 같은 수식을 가진다

'프로그래밍 > Multimedia' 카테고리의 다른 글

DCT(Discrete Cosine Transform)  (0) 2009.04.28
Posted by 마블(이환문)
프로그래밍/C++2009. 4. 25. 20:03

회사에서나 개인적으로 S/W를 전문적으로 개발해 본 경험이 있는 분이라면 로깅 라이브러리는 거의 모든 프로젝트에 공통적으로 필요한 모듈이라는 걸 인정하실 것입니다.

로깅 라이브러리는 개발하는 중간에는 S/W 가 잘못 수행되는 부분은 없는지 디버깅하는 데 유용할 뿐만 아니라 개발이 완료되고 나서 시스템 운영자나 관리자가 해당 S/W를 운영하는 데 필수적인 정보를 제공하는 데 유용합니다. 여러분이 웬만한 규모 이상의 S/W를 개발한다면 항상 맞닥뜨려야 하는 문제라는 것이죠. 이렇게 거의 모든 S/W에서 필요한 라이브러리임에도 불구하고, 잘 설계된 로깅 라이브러리를 만든다는 건 쉽지 않은 일입니다.

이런 로깅 라이브러리를 설계/구현하는 긴 여정을 이번 글을 시작으로 해서 떠나 보도록 하겠습니다.

아~! 근데 본격적으로 여정을 떠나기 전에 한 가지 준비를 하고 떠나야 되겠습니다. 로깅을 위해 C/C++ 언어에서 제공하는 기본 메커니즘들을 확인하고 가는 것이죠. 이걸 확인하는 방법은 다양하겠지만, 저는 assert()를 통해 설명드리도록 하겠습니다.

assert() 란 함수의 본래 로직을 수행하기 전/후에 해당 로직이 가정하고 있는 특정 조건이 만족하는지를 체크해 보고 만족하지 않으면 실행을 중단해 버리는 특수한 함수로 개발 도중 디버깅 용도로 많이 쓰이는 방법입니다.

예를 들어, 여러분이 C 라이브러리 구현자라고 상상해 보시겠습니다. 그리고, memcpy() 와 같은 함수를 구현하는 책임이 있다고 상상해 보겠습니다. memcpy()는 다음과 같이 구현할 수 있을 것입니다.

/*
 * memcpy()는 pvSource 로부터 pvSink 로 sz 만큼을 복사함.
 * memcpy()는 pvSource 와 pvSink 가 NULL 이 아니라는 걸 가정하고 있음
 */
void*
memcpy(void* pvSink, void* pvSource, size_t sz)
{
  char* pSrc = (char*)pvSource;
  char* pSnk = (char*)pvSink;

  while (sz-- > 0)
    *pSnk++ = *pSrc++;

  return pvSink;
}

기본적인 아이디어는 위와 같이 구현할 수 있지만, memcpy 가 가정하고 있는 pvSource와 pvSink 가 NULL 이 아니라는 건 체크하지 않고 있기 때문에 어떤 개발자가 실수로 pvSink 나 pvSource에 NULL을 주게 되면 실행시에 심각한 문제가 발생할 수도 있습니다. 이런 문제를 피하기 위해 당연히 본래의 로직을 실행하기 전에 pvSource 와 pvSink의 NULL 여부를 체크하는 로직을 넣어야 할 것입니다.

/*
 * pvSource, pvSink가 NULL인지를 체크함
 */
void*
memcpy(void* pvSink, void* pvSource, size_t sz)
{
  char* pSrc = (char*)pvSource;
  char* pSnk = (char*)pvSink;

  /* pvSource 나 pvSink가 NULL 프로그램 수행을 중단 */
  if (pvSource == NULL || pvSink == NULL) {
    fprintf(stderr, "memcpy(): Bad args: pvSource or pvSink is NULL\n");
    abort();
  }

  while (sz-- > 0)
   *pSnk++ = *pSrc++;

  return pvSink;
}

어떤 분은 NULL 인지 체크한 후 에러 메시지만 출력하고 바로 리턴하는 게 아니라 abort()를 호출하는 걸 이상하게 생각하실 분도 있을 것 같습니다. 그건 pvSource 나 pvSink의 값으로 NULL이 넘어 왔다면 프로그램에 뭔가 심각한 버그가 있다는 걸 뜻하기 때문입니다. 뭔가 심각한 버그가 있는 채로 실행되는 것 보다는 그 상황이 알려지면 빨리 종료해 버리는 것이 문제를 해결하는 데 훨씬 유리하기 때문입니다. 그렇다 하더라도 이미 S/W가 출시된 상황에서 그런 일이 벌어진다면 고객으로부터 Claim이 올 것은 자명한 일입니다. 따라서 pvSource 나 pvSink 가 NULL 인지 체크하는 코드는 Production 시스템에 사용되는 것이 아니라 개발시에 사용되어야 그 의미가 살아날 수 있습니다. 즉, 다음과 같이 작성해야 할 것입니다.

/*
  * pvSource, pvSink가 NULL인지를 체크함
  */
void*
memcpy(void* pvSink, void* pvSource, size_t sz)
{
  char* pSrc = (char*)pvSource;
  char* pSnk = (char*)pvSink;

#ifdef DEBUG
  /* pvSource 나 pvSink가 NULL 프로그램 수행을 중단 */

  if (pvSource == NULL || pvSink == NULL) {
    fprintf(stderr, "memcpy(): Bad args: pvSource or pvSink is NULL\n");
    abort();
  }
#endif

  while (sz-- > 0)
   *pSnk++ = *pSrc++;

  return pvSink;
}

위와 같이 작성하고 개발시에는 DEBUG 를 정의해서 컴파일하고, 정식 릴리즈시에는 DEBUG를 정의하지 않고 컴파일하면 개발시에만 abort()가 적용되어 디버깅하는 데 도움을 얻을 수 있게 됩니다.

이렇게 뭔가 만족되어야 하는 조건을 검사하고, 그 조건이 만족하지 않을 경우 실행을 중단해버리는 함수가 바로 assert() 입니다. 다음은 assert() 를 적용해서 위 코드를 수정해 본 것입니다.

/*
  * pvSource, pvSink가 NULL인지를 체크함
  */
void*
my_memcpy(void* pvSink, void* pvSource, size_t sz)
{
  char* pSrc = (char*)pvSource;
  char* pSnk = (char*)pvSink;

  assert(pvSource != NULL && pvSink != NULL);

  while (sz-- > 0)
   *pSnk++ = *pSrc++;

  return pvSink;
}

char* pSource = "source memory content";
char pSink[32];

/* main() 함수 */
int
main(void)
{
  printf("Calling my_memcpy(%08x,%08x, %d)\n",
    pSink, pSource, strlen(pSource));
  my_memcpy(pSink, pSource, strlen(pSource));
  /* assert()가 실패하는 케이스 */
  printf("Calling my_memcpy(NULL,%08x, %d)\n", pSource,
    strlen(pSource));

  my_memcpy(NULL, pSource, strlen(pSource));
}

다음은 GCC(3.4.4)로 컴파일하고 실행해 본 결과입니다.

$ ./debug
Calling my_memcpy(00404020,0040302b, 21)
Calling my_memcpy(NULL,0040302b, 21)
debug: debug.c:15: main: Assertion `pvSource != NULL && pvSink != NULL' failed.
Aborted


위 결과에서 보시다시피 두 번째 my_memcpy() 실행 중에 assert()가 실패해서 실행이 취소되는 걸 확인할 수 있습니다.

assert()가 실패했을 때, 출력해주는 메시지를 살펴 보시기 바랍니다. 프로그래머가 특별히 입력인자로 주지도 않았는데 다음과 같은 부가적인 정보를 출력해서 디버깅이 용이하도록 해줍니다.

1. debug.c 라는 파일명을 출력해 주네요
2. 정확한 라인번호도 출력해 줍니다.
3. pvSource != NULL && pvSink != NULL 이라는 수식을 문자열로 출력해 줍니다.

너무 신기하지 않나요 ? 도대체 assert() 란 녀석이 어떤 마술을 부리길래 프로그래머가 주지도 않은 정보를 알아내서 저런 정보를 출력해 줄 수 있을까요 ? 전 처음에 이 assert() 란 녀석을 써보고서는 어찌나 신기했던지 어떻게 구현된지를 알아낼려고 한참을 헤멨습니다. 여러분도 궁금하시죠 ? 그럼 지금부터 하나 하나 그 비밀을 파헤쳐 보도록 하겠습니다.

assert() 란 녀석이 어떻게 구현되어 있는지를 알아내면 그 비밀을 알아낼 수 있을 것 같습니다. 우선 assert() 가 선언되어 있는 헤더 파일부터 뒤져보기로 하죠.

제가 지금 이 글을 쓰면서 쓰고 있는 컴파일러는 GCC 인데요, 그곳에서는 다음과 같이 assert()가 정의되어 있습니다.

/* 이해를 돕기 위해 간략하게 표현했습니다 */
extern void __assert_fail (__const char *__assertion,
                           __const char *__file,
                          
unsigned int __line,
                           __const char *__function)


#define assert(expr) \
 
((void) ((expr) ? 0 : (__assert_fail (#expr, __FILE__,  \
                                       __LINE__, __func__), 0)))


일단 이걸 보면 assert() 가 그냥 함수가 아니라 매크로 함수인 것을 알 수 있습니다. 그리고 내부적으로는 __assert_fail() 이라는 또 다른 함수를 #expr, __FILE__, __LINE__, __func__ 라는 인자를 가지고 호출하는 걸 확인할 수 있습니다. 도대체 이런 듣도 보도 못한 애들은 어느 별에서 온 걸까요 ? 참 신기하긴 합니다만 신기해 하는 것으로 그쳐서는 안되고 진정한 골수 엔지니어라면 얘네들이 어떤 별에서 왔는지 무슨 일이 있어도 알아내야 되겠지요.

우선 #expr 부터 살펴 보겠습니다. 다음 코드를 한 번 컴파일하고 실행시켜 보시겠어요 ?

#include <stdio.h>

#define STRINGIFY(x) #x

int
return10(void)
{
  return 10;
}

int
main(void)
{
  printf("%s = %d\n", STRINGIFY(10 + 100), 10 + 100);
  printf("%s = %d\n", STRINGIFY(strlen("printf")), strlen("printf"));
  printf("%s returned %d\n", STRINGIFY(return10()), return10());
}

컴파일한 후 실행시켜 보았더니 이런 결과가 나오네요.

$ gcc -o stringify stringify.c
$ ./stringify
10 + 100 = 110
strlen("printf") = 6
return10() returned 10

어~! STRINGIFY()를 호출하면 STRINGIFY로 넘겨준 인자가 그냥 그대로 문자열 리터럴이 되어 버리네요. 아~! 그렇구나. 매크로 함수로 넘겨준 인자에 '#'를 붙이면 전처리기가 그걸 문자열 리터럴로 바꿔주나 봅니다. 그것 참 쓸모 있을 것 같습니다.

다음은 __FILE__, __LINE__, __func__ 를 알아보도록 하겠습니다.

__FILE__ 매크로는 컴파일시에 파일명을 나타내는 문자열 리터럴로 치환되고 __LINE__ 은 소스 코드 라인번호로 치환됩니다. 그리고, __func_ 는 매크로가 아니라 컴파일러에 의해 자동으로 선언되는 변수로서 함수명을 나타내는 문자열을 가리키게 됩니다. __FILE__ 은 문자열 리터럴이고, __LINE__ 은 정수 리터럴이고, __func__ 는 자동으로 정의되는 변수라는 점에 주의해야 합니다. __func__ 와 비슷한 역할을 하는 녀석이 __FUNCTION__ 이라는 녀석입니다. 다음 코드를 컴파일해서 실행해 볼까요 ?

/* debug2.c */
#include <stdio.h>
#include <assert.h>

int
main(void)
{
  printf("%s() at %s::%05d\n", __FUNCTION__, __FILE__, __LINE__);
  printf("%s() at %s::%05d\n", __func__, __FILE__, __LINE__);
  assert(1 == 0);

  return 0;
}

Windows XP 에서 cygwin GCC(3.4.4)로 컴파일해서 실행해 봤더니 다음과 같은 결과가 나오는 군요.

$ ./debug2
main() at debug2.c::00009
main() at debug2.c::00010
assertion "1 == 0" failed: file "debug2.c", line 11
     38 [sig] debug2 5940 C:\cygwin\home\김윤수\work\debug2.exe: *** fatal error - called with threadlist_ix -1
Hangup

주의할 점은 __FUNCTION__ 은 비표준으로 컴파일러 벤더들이 자체적으로 제공하는 확장 매크로로 preprocessing 중에 문자열 리터럴로 치환되는 반면 __func__ 는 C99 표준에 포함된 것으로 컴파일러에 의해 자동적으로 정의되는 const char * 형의 변수라는 점입니다.

일단 GCC는 __FUNCTION__ 과 __func__ 둘 다를 지원하는 반면 Microsoft Visual C++ 2005의 cl은 __FUNCTION__ 만 지원하는 군요. 또 신기한 점은 GCC, G++은 __FUNCTION__ 을 __func__ 와 동일하게 처리한다는 점입니다. 위에서 컴파일해 본 debug2.c 에 대해 preprocessing 한 결과를 살펴 보면 매크로가 아니라 변수로 취급하는 걸 알 수 있습니다.

$ gcc -E debug2.c > debug2.pre.c
$ cat debug2.pre.c
......

int
main(void)
{
  printf("%s() at %s::%05d\n", __FUNCTION__, "debug2.c", 10);
  printf("%s() at %s::%05d\n", __func__, "debug2.c", 11);
  ((1 == 0) ? (void)0 : __assert("debug2.c", 12, "1 == 0"));

  return 0;
}

위에서 첫번째 명령에서 gcc 의 -E option은 preprocessing 한 결과를 표준출력으로 내보내라라는 옵션입니다. 위 명령들은 그걸 리다이렉션해서 debug2.pre.c 라는 파일로 저장해서 그 내용을 살펴본 것입니다. (각 컴파일러에서 preprocessing 만 하게 하는 옵션을 알아두면, 매크로 치환하면서 문제가 발생하는 경우 쉽게 디버깅할 수 있습니다. 꼭 알아 두시기를 권유합니다.)

__FUNCTION__, __func__ 이 매크로였다면 preprocessing 후에는 둘 다 __FILE__ 처럼 문자열 리터럴로 치환되어야 함에도 원래 이름대로 그대로 남아 있는 것은 전처리기가 처리하지 않는다는 뜻이 될 것입니다.

위에서 assert() 가 어떻게 정의되어 있는지 보여드렸을 때는 간략하게 하기 위해 복잡한 조건 컴파일을 보여드리지 않았는데요, 실제로는 다음과 같이 NDEBUG(No debugging 의 약자라고 보시면 됩니다)가 정의되어 있느냐에 따라 조건 컴파일이 되도록 정의되어 있습니다.

#ifdef NDEBUG /* required by ANSI standard */

#define assert(expr)      ((void)0)

#else

/* 아까 나온 부분. 이해를 돕기 위해 간략하게 표현했습니다 */

......
#define assert(expr) \
 
((void) ((expr) ? 0 : (__assert_fail (#expr, __FILE__,  \
                                       __LINE__, __func__), 0)))

#endif

즉, NDEBUG 가 정의되어 있으면(다르게 표현하면 디버깅 버전이 아니라는 뜻이겠지요), assert() 가 아무런 영향을 미치지 않고, 아무것도 정의되어 있지 않으면 assert()가 영향을 미치는 것이겠지요. NDEBUG는 ANSI 표준에 의해 정의된 매크로이기 때문에 대부분의 컴파일러가 지원할 것으로 보입니다. 앞에서 작성했던 debug2.c 를 다음과 같이 NDEBUG를 정의한 채로 컴파일한 후 실행하면 assert()가 아무런 영향을 미치지 않는 것을 확인할 수 있습니다.

$ gcc -o debug2 -DNDEBUG debug2.c
$ ./debug2
main() at debug2.c::00010
main() at debug2.c::00011


이렇게 디버깅 버전과 Production 버전에 따라 여러 가지 코드가 포함되고 포함되지 않게 하는 것은 실제 프로젝트에서 일상적으로 활용되는 방법입니다. 개발하는 동안에는 디버깅 버전으로 원래의 실행 로직외에 다양한 코드 또는 정보들이 실행 파일에 포함되게 함으로써 문제가 발생했을 때 신속하게 찾을 수 있게 도와주고, Production 버전에서는 성능을 개선하고 실행파일의 크기를 줄이기 위해 그런 부가적인 정보 또는 코드를 없애버리는 것이지요

마지막으로 한 가지 더 주의할 점을 알려 드리겠습니다. __FUNCTION__ 은 컴파일러 벤더들이 나름대로 정의한 확장 매크로이기 때문에 portability 가 떨어지고, __func__ 는 최신 C99 규격에 포함된 것이라 portability 가 약간 떨어진다는 것입니다. 물론 장기적으로는 __func__ 를 쓰는 것이 유리하겠지만(표준이니까 __func__ 를 지원하는 컴파일러가 많이 생길 것으로 기대할 수 있으므로), Microsoft Visual C++ 2005 버전도 지원하지 않는 걸 보니 __func__도 본격적으로 쓰일 수 있으려면 아직 갈 길이 먼 것 같습니다. 정리하자면 portability 가 중요하다면 __FUNCTION__ 이나 __func__ 를 전혀 쓰지 않거나 아니면 쓰더라도 컴파일러에서 __FUNCTION__ 이나 __func__이 지원되는 여부에 따라서 다르게 처리되도록 해야할 것입니다. 더 상세한 정보를 얻기 원하시면 다음 링크를 참고하시기 바랍니다.

__LINE__, __FUNCTION__, and __FILE__ macros
C, C++ 언어에서 디버깅시에 유용하게 쓸 수 있는 predefined macro 에 대한 설명
Pre-defined C/C++ Compiler Macros
다양한 C/C++ 컴파일러에서 정의된 표준 및 비표준 predefined macro 목록을 잘 정의해 둔 곳

이번 글에서는 다음과 같은 사항을 기억하시면 될 것입니다.

  1. 매크로 함수 정의에서 인자 x 에 대해 #x 라고 쓰면 x 가 문자열 리터럴로 바뀝니다.
  2. __FUNCTION__ 또는 __func__ 는 현재 함수명을 출력하는 데 사용할 수 있습니다. 단, __FUNCTION__, __func__ 모두 portability 측면에서 약간 문제가 있으므로 주의해서 사용해야 합니다.
  3. __FILE__ 은 현재 파일명을 출력하는 데 사용할 수 있습니다.
  4. __LINE__ 은 현재 라인번호를 출력하는 데 사용할 수 있습니다.
  5. 조건부 컴파일을 이용해서 디비깅용 빌드와 Production 빌드를 구분하는 방법을 사용할 수 있어야 합니다.
  6. 컴파일러 옵션 중 preprocessing 만 하는 옵션을 알아두면 매크로 함수를 디비겅하는데 유용하게 사용될 수 있습니다.

자~ 그럼 이상으로 해서 로깅 라이브러리를 설계/구현하기 위한 여정에 필수적인 도구들을 다 챙긴 것 같습니다

출처: 김윤수의 이상계를 꿈꾸며
url : http://yesarang.tistory.com/74

'프로그래밍 > C++' 카테고리의 다른 글

C++ 디버깅시 TRACE 사용하기  (0) 2009.12.10
STL - List 사용하기  (0) 2009.07.16
파일 입출력 및 링크드 리스트  (1) 2009.05.01
Abstract Factory Pattern  (0) 2009.04.22
PVOID  (0) 2009.04.22
Posted by 마블(이환문)
프로그래밍/OpenGL2009. 4. 25. 20:03

Octree

정의

옥트리는 삼차원 공간에서 오브젝트를 표현하기 위한 자료구조이다. 이는 자동적으로 그것들을 계층화하여 그룹화하고, 공간 내의 빈 부분에 대한 표현을 피한다.

첫 번째 옥트리 노드는 루트 셀인데, 아래의 그림에서 보이듯이 이는 여덟 개의 연속되는 요소들의 배열이다. 이들 각 요소들은 여덟 개의 연속된 요소들 중 다른 하나를 가리킬 수 있다. 여기에서 각각은 여덟 개의 연속되는 요소들 중 다른 하나를 가리키는 식인데, 이는 특정한 최대 숫자의 레벨에 도달할 때까지 계속된다. 마지막 레벨은 단말 레벨이며, 여기에서 단말 요소나 복셀(voxel)이 저장된다.

 
복셀 검색 원리

위의 그림은 자료 구조가 메모리(왼쪽)와 그것의 삼차원 공간 표현(오른쪽) 사이에서 어떻게 대응되는지를 보여준다. 트리는 여덟 개의 복셀과 다섯 개의 레벨을 가지고 있다. 각 레벨은 서로 다른 색으로 표현된다. 옥트리의 마지막 레벨에 저장되는 여덟 개의 레벨은 분홍색으로 표현된다. 위의 그림의 표는 마지막 셀에 있는 다섯번 째 복셀(index 4)의 좌표를 보여준다. 그것의 좌표는 : 31, 30, 30이며, 후에 오른쪽에서 이진 형식으로 보여진다. 마지막으로 각 좌표 비트와 연관된 각 열도 옥트리의 레벨과 관련이 있다. 각 열의 비트를 합치면, 관련 레벨을 위한 셀의 각 요소에 대한 (테이블의 마지막 열에 나온) 인덱스를 획득하게 된다. 그리고 이것은 트리를 순회하고 검색된 복셀을 찾는 데 사용된다. 이것이 옥트리에서 주어진 복셀을 검색하는 원리이다.

(역주 :

각 열을 합칠 때, x를 0비트, y를 1비트, z를 2비트로 취급하면, 1 + 2 + 4 = 7, 0 + 0 + 4 = 4가 된다. 위의 표에서 열이 나타내는 숫자 0, 1, 2, 3, 4는 복셀의 인덱스를 의미하고 있다.

그런데 표가 이상하다. 비트의 합이 0 ~ 7까지 순서대로 나오는게 맞을텐데 다 7이고 4번째 열만 4다

)

이러한 구조의 주요 이점은 복셀 좌표가 저장되지 않고, 순회되는 요소들의 위치에 의해 암묵적으로 내포된다는 것이다. 이것은 좌표가 저장하기에는 크고 성가신 고해상도 복셀 공간에서 매우 편리하다. 3D 그리드도 복셀 위치를 내포하는 좌표를 가지고 있지만, 대부분의 볼륨이 비어있는 경우에도 볼륨 내의 모든 복셀을 표현해야 한다는 심각한 단점을 가지고 있다.

메모리 점유

위의 단말 레벨에서 모든 셀의 요소들이 셀을 가리키고 있다면, 그 옥트리를 "꽉찬 옥트리(full octree)"라고 부른다. n개의 레벨을 가지고 있는 꽉찬 옥트리는 해상도 2n x 2n x 2n의 정규 3D 그리드와 같은 복셀 개수를 포함한다(예를 들어 꽉찬 옥트리가 5레벨이라면 32 x 32 x 32 해상도를 가진 3D 그리드와 같다). 이런 극단적인 경우에 옥트리는 관련 3D 그리드보다 14% 많은 방(역주 : 메모리공간인듯)을 차지한다. 그러나 복셀이 볼륨 대신에 서피스를 표현한다면, 대부분의 공간은 비어있으며 그것은 옥트리 내에 표현되지 않는다. 이런 경우에 옥트리는 매우 경제적이며, 적은 메모리 소비만으로 고해상도 복셀 공간을 만들 수 있게 해 준다

'프로그래밍 > OpenGL' 카테고리의 다른 글

Texture Mapping  (0) 2009.04.29
OpenGL Viewing  (0) 2009.04.24
OpenGL 기초  (1) 2009.04.23
Picking 사용하기  (0) 2009.04.22
void glutIdleFunc(void (*func)(void))  (0) 2009.04.22
Posted by 마블(이환문)
일반2009. 4. 24. 14:32

오피스 파워포인트 2007 에서는 듀얼로 창이 뜨지 않습니다

위 파일을 C:\Program Files\Microsoft Office\Office12 이 폴더에 덮어 씌우면 듀얼 모니터에서

각 화면에 파워포인트를 띄워놓고 작업 할 수 있습니다

혹시 모르니 백업은 필수!!!!!

'일반' 카테고리의 다른 글

청첩장 샘플 입니다.  (0) 2014.12.08
유닉스 test 명령어 사용법  (0) 2014.06.11
토토브라우져 검색어 패치(4월 24일 현재 잘됨)  (0) 2009.04.24
Posted by 마블(이환문)
일반2009. 4. 24. 14:19
토토브라우져 검색어 패치 입니다

파일을 다운 받으신후 C:\Program Files\ToToBrowser 폴더에 덮어씌우기 하시면 됩니다

환경설정에 가셔서 검색방법 설정에서 일반 폴더 검색으로 설정을 변경하시고 사용하시면 됩니다

'일반' 카테고리의 다른 글

청첩장 샘플 입니다.  (0) 2014.12.08
유닉스 test 명령어 사용법  (0) 2014.06.11
파워포인트 2007 듀얼 창으로 쓰기  (0) 2009.04.24
Posted by 마블(이환문)