IT/Graphics

[OpenGL] OpenCV 이미지 OpenGL 배경으로 사용하기

버건디 팩토리 2021. 8. 3. 03:00
반응형

OpenCV 이미지를 OpenGL 배경으로

많은 프로젝트를 찾아봤지만 texture를 이용하여 OpenCV 이미지를 OpenGL 배경에 넣는 레퍼런스 프로젝트는 많이 보지 못한 것 같다.

 


그래서 간단하게 이를 작성하여 남겨보려고 한다!!

Code

전체 코드는 맨 아래에 있으니 참고를 부탁한다.

OpenCV part

이미지 불러오기

현재 디렉토리에 이미지를 붙여넣고, opencv 함수를 통해 이를 전역변수 Mat에 불러온다.

Mat image;

int main(int argc, char **argv) {
	...
	image = imread("img_0000.png", IMREAD_COLOR);
    cvtColor(colorImage, colorImage, CV_RGB2BGR);
	...
}

해당 코드를 통해 Mat 형식으로 이미지를 불러올 수 있다. 이때 나는 CV_8CU3의 8비트 컬러 이미지를 사용했다.
또한 OpenCV는 BGR, OpenGL은 RGB를 사용하므로 cvtColor 함수를 통해 바꿔준다.

이미지 주소값 저장

Mat 함수의 data는 image.data로 불러올 수 있지만, 조금 더 명확한 선언을 위해 전역변수 pColor에 data의 주소값을 저장하는 함수를 선언한다

uchar * pColor;

void setImage(uchar *image_addr) {
	pColor = image_addr;
}

OpenCV에서 OpenGL로 설정할 data 선언이 완료되었다. 해당 이미지는 텍스쳐로 OpenGL의 background image texture로 입력될 것이다.

OpenGL part

main

해당 프로젝트의 main code이다.
전역변수에는 texture, 이미지 shape, 이미지 저장 주소를 선언한다.
아주 기본적인 background texture를 위한 코드이므로, 별도의 resize 함수를 사용하지 않고 draw 시 PERSPECTIVE, MODEL의 좌표를 모두 선언하려고 한다.

GLuint colorTexture;
int w, h;
unsigned char *pColor;

int main(int argc, char **argv) {

	// 이미지를 불러온다
	colorImage = imread("img/img_0000.png", IMREAD_COLOR);
	cvtColor(colorImage, colorImage, CV_RGB2BGR);
	
    	// 이미지의 data 주소를 전역변수로 저장한다
	SetImage(colorImage.data);

	// 이미지의 크기를 저장한다
	w = colorImage.cols;
	h = colorImage.rows;

	cout << "width : " << w << " height : " << h << endl;

	glutInit(&argc, argv);
	glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGBA);

	glutInitWindowSize(w, h);
	glutInitWindowPosition(w / 2, h / 2);
	glutCreateWindow("Image Background");

	// OpenGL 기본 설정
	init();
	
	// OpenGL 내 background 및 model draw
	glutDisplayFunc(draw);

	glutMainLoop();

	return EXIT_SUCCESS;
}

init

OpenGL의 기본 설정 함수이다.
나는 아주 기본적인 background만 보여줄 것이니, DEPTH_TEST 등은 선언하지 않고 texture와 SCISSOR에 관해서만 선언하였다.

void init() {
	cout << "init func called\n";
	glClearColor(0.f, 0.f, 0.f, 1.f);
    
	glEnable(GL_SCISSOR_TEST);

	glEnable(GL_TEXTURE_2D);
	glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
	// GL_REPLACE : 물체의 원래 색상을 무시하고 texture로 덮어씌운다

	glFrontFace(GL_CW);

	//RGB Init
	glGenTextures(1, &colorTexture);
	glBindTexture(GL_TEXTURE_2D, colorTexture);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
	glBindTexture(GL_TEXTURE_2D, 0);
}


사용 방법에 대해서는 아래 draw 파트에서 설명하려고 한다. 또한 texture 설정에 관련된 부분은 다른 블로그들에 매우 잘 정리되어 있으니 참고 부탁한다!

draw

OpenGL 창에 그림을 그리는 함수이다.
background에 OpenCV 이미지를 그리는 부분, 모델을 그리는 부분 두 함수로 나눠 실행한다.
draw_background는 전체 화면에 꽉 차야하므로 소실점이 없는 gluOrtho2D를 사용한다.


draw_model은 model을 그릴 때 소실점을 표현하기 위해 gluPerspective를 따로 사용하게 설정한다.


또한 이는 SCISSOR_TEST를 통해 구현이 가능하다!

void draw() {
	cout << "draw func called\n";
	draw_background();
	draw_model();

	glFlush();
	glutSwapBuffers();
}

glFlush, glutSwapBuffers 함수에 대한 설명까지 넣으면 너무 중구난방이 될 것 같아
이것도 다른 블로그를 참고 부탁한다

draw_background

Img를 OpenGL view의 background로 사용하는 함수이다.
OpenCV, OpenGL은 좌표계가 다르다.


따라서 이를 맞추기 위해 scale 함수가 필요하다.

glScalef(1, -1, -1);
glTranslatef(0, -h, 0);

위 두 식을 통해 y축을 내리고, h만큼 올려줘, OpenGL 좌표계를 OpenCV 좌표계로 변경한다.
추후 이를 통해 OpenGL 좌표계를 OpenCV 좌표계로 바꿔, AR 프로젝트 또한 진행이 가능하다!

 

texture는 아래와 같이 glTexImage2D 함수에서 pColor를 불러오며 텍스쳐를 배경에 입힐 수 있다.
이 부분은 레퍼런스 코드가 굉장히 많으므로 간단하게 주석으로 처리하겠다!!

void draw_background() {

	glViewport(0, 0, w, h);

	glClearColor(0.f, 0.f, 0.f, 0.f);
	glClear(GL_COLOR_BUFFER_BIT);

	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();

	glPushMatrix();
	gluOrtho2D(0.0, w, 0.0, h);

	// OpenCV 좌표계와 동일하게 바꿔준다
	glScalef(1, -1, -1);
	glTranslatef(0, -h, 0);
	
	// img texture를 orthogonal하게 선언한다
	glBindTexture(GL_TEXTURE_2D, colorTexture);
	// img가 저장되어 있는 pColor 주소를 받아 texture로 활용한다
	glTexImage2D(GL_TEXTURE_2D, 0, 3, w, h, 0, GL_RGB, GL_UNSIGNED_BYTE, pColor);
	glBegin(GL_TRIANGLE_FAN);
	// texture와 OpenGL 좌표계를 연결한다.
	glTexCoord2f(0.0f, 0.0f);  glVertex3f(0, 0, 0);
	glTexCoord2f(1.0f, 0.0f);  glVertex3f(w, 0, 0);
	glTexCoord2f(1.0f, 1.0f);  glVertex3f(w, h, 0);
	glTexCoord2f(0.0f, 1.0f);  glVertex3f(0, h, 0);
	glEnd();
	glBindTexture(GL_TEXTURE_2D, 0);

	glMatrixMode(GL_PROJECTION);
	glPopMatrix();
}

이를 통해 OpenGL 창에 이미지를 꽉 차게 선언할 수 있다!!

draw_model

특별할 것 없이 solidTeapot을 그리는 코드이다.

gluPerspective는 내 카메라 파라미터 기준으로 작성되었지만, 간단하게 표현이 가능하다. 아래와 같이 작성할 필요가 없으니 참고하자!

float fx = 520.f;

void draw_model() {

	glMatrixMode(GL_PROJECTION);
	glPushMatrix();
	glLoadIdentity();
	gluPerspective(2.0f * (180 / 3.141592) * atan2(h, 2.f * fx), ((float)w * fx) / ((float)h * fx), 1.f, 200.f);

	glMatrixMode(GL_MODELVIEW);
	glPushMatrix();
	glLoadIdentity();

	glTranslatef(0.f, 0.f, -5.f);
	glRotatef(-45, 0.f, 1.f, 0.f);
	glRotatef(30, 1.f, 0.f, -1.f);
	glColor3f(1.f, 0.f, 1.f);
	glutSolidTeapot(1.f);

	glPopMatrix();

	glMatrixMode(GL_PROJECTION);
	glPopMatrix();
}

 

아래 그림을 출력하는 정말 간단한 함수이다!

 

Result

실행 결과

최종적으로 아래와 같이 OpenCV img를 배경으로 하는 프로젝트를 만들 수 있다.

 

다음 프로젝트

위 프로젝트에서 glutIdleFunc 함수를 통해 동영상을 OpenGL 함수로 불러오는 예제를 작성하려고 한다!!

전체 Code

#include <GL/freeglut.h>
#include "opencv2/opencv.hpp" 
#include <iostream>  

using namespace cv;
using namespace std;

GLuint colorTexture;
int w, h;
unsigned char *pColor;

float fx = 520.f;

void init();
void draw();
void draw_background();
void draw_model();

void setImage(uchar *image_addr) {
	pColor = image_addr;
}

int main(int argc, char **argv) {

	Mat colorImage = imread("img/img_0000.png", 1);

	cvtColor(colorImage, colorImage, CV_RGB2BGR);

	setImage(colorImage.data);

	w = colorImage.cols;
	h = colorImage.rows;

	cout << "width : " << w << " height : " << h << endl;

	glutInit(&argc, argv);
	glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGBA);

	glutInitWindowSize(w, h);
	glutInitWindowPosition(w / 2, h / 2);
	glutCreateWindow("Image Background");

	init();

	glutDisplayFunc(draw);
	//glutIdleFunc(proc);

	glutMainLoop();

	return EXIT_SUCCESS;
}

void init() {

	printf("init func called\n");
	glClearColor(0.f, 0.f, 0.f, 1.f);

	glEnable(GL_TEXTURE_2D);
	glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
	//GL_REPLACE : polygon의 원래 색상은 무시하고 texture로 덮음

	glEnable(GL_SCISSOR_TEST);

	glFrontFace(GL_CW);

	//RGB Init
	glGenTextures(1, &colorTexture);
	glBindTexture(GL_TEXTURE_2D, colorTexture);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
	glBindTexture(GL_TEXTURE_2D, 0);
}

void draw() {
	cout << "draw func called\n";
	draw_background();
	draw_model();

	glFlush();
	glutSwapBuffers();
}

void draw_background() {

	glScissor(0, 0, w, h);			// w x h 만큼 따로 그린다
	glViewport(0, 0, w, h);

	glClearColor(0.f, 0.f, 0.f, 0.f);
	glClear(GL_COLOR_BUFFER_BIT);

	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();

	glPushMatrix();
	gluOrtho2D(0.0, w, 0.0, h);

	// OpenCV 좌표계와 동일하게 바꿔준다
	glScalef(1, -1, -1);
	glTranslatef(0, -h, 0);

	// img texture를 orthogonal하게 선언한다
	glBindTexture(GL_TEXTURE_2D, colorTexture);
	// img가 저장되어 있는 pColor 주소를 받아 texture로 활용한다
	glTexImage2D(GL_TEXTURE_2D, 0, 3, w, h, 0, GL_RGB, GL_UNSIGNED_BYTE, pColor);
	glBegin(GL_TRIANGLE_FAN);
	// texture와 OpenGL 좌표계를 연결한다.
	glTexCoord2f(0.0f, 0.0f);  glVertex3f(0, 0, 0);
	glTexCoord2f(1.0f, 0.0f);  glVertex3f(w, 0, 0);
	glTexCoord2f(1.0f, 1.0f);  glVertex3f(w, h, 0);
	glTexCoord2f(0.0f, 1.0f);  glVertex3f(0, h, 0);
	glEnd();
	glBindTexture(GL_TEXTURE_2D, 0);

	glMatrixMode(GL_PROJECTION);
	glPopMatrix();
}

void draw_model() {

	glScissor(0, 0, w, h);
	glViewport(0, 0, w, h);

	glMatrixMode(GL_PROJECTION);
	glPushMatrix();
	glLoadIdentity();
	gluPerspective(45, (float)w / (float)h, 1.f, 200.f);

	glMatrixMode(GL_MODELVIEW);
	glPushMatrix();
	glLoadIdentity();

	glTranslatef(0.f, 0.f, -5.f);
	glRotatef(-45, 0.f, 1.f, 0.f);
	glRotatef(30, 1.f, 0.f, -1.f);
	glColor3f(1.f, 0.f, 1.f);
	glutSolidTeapot(1.f);

	glPopMatrix();

	glMatrixMode(GL_PROJECTION);
	glPopMatrix();
}
반응형