Edited at

PythonでOpenCVの画像をOpenGLで表示する

More than 1 year has passed since last update.


はじめに

OpenCVは非常に便利な画像処理のライブラリであるが,画像の描画はあまり得意ではないようである。また,imshowで表示すると,waitKey(1)が引数に関係なく15ms程度は少なくともかかるようで,60fps程度が上限になってしまうといえる。

http://13mzawa2.hateblo.jp/entry/2015/12/28/180021

3Dモデルを表示する場合や,高速なフレームレートで表示したい場合はOpenGLを使うのが良いみたいである。

Pythonでのコードがあまり見つからなかったのでメモしておく。


OpenCVの画像をOpenGLで表示

OpenCVの画像をOpenGLで表示するためには,glDrawPixelsでnumpy配列をOpenGLに渡すのが最もてっ取り早い。

https://qiita.com/smygw72/items/90bfe55a9eae5989d4bf

cap.read()で読み込んだnumpy配列をOpenGLの座標に合わせて上下反転し,BGRをRGBに変換してからglDrawPixelsで渡してやる。


from OpenGL.GL import *
from OpenGL.GLU import *
from OpenGL.GLUT import *
import cv2
import numpy as np

# USB camera setup
cap = cv2.VideoCapture(0)
if cap.isOpened() is False:
raise("IO Error")
cap.set(cv2.CAP_PROP_FPS, 30)
cap.set(cv2.CAP_PROP_FRAME_WIDTH, 720)
cap.set(cv2.CAP_PROP_FRAME_HEIGHT, 480)

widowWidth = 720
windowHeight = 480

def draw():
ret, img = cap.read() #read camera image
#img = cv2.imread('image.png') # if use the image file
img = cv2.flip(img, 0)
img= cv2.cvtColor(img,cv2.COLOR_BGR2RGB) #BGR-->RGB

glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
glColor3f(1.0, 1.0, 1.0)

glDrawPixels(img.shape[1], img.shape[0], GL_RGB,GL_UNSIGNED_BYTE, img)

glFlush();
glutSwapBuffers()

def init():
glClearColor(0.7, 0.7, 0.7, 0.7)

def idle():
glutPostRedisplay()

def reshape(w, h):
glViewport(0, 0, w, h)
glLoadIdentity()
#Make the display area proportional to the size of the view
glOrtho(-w / widowWidth, w / widowWidth, -h / windowHeight, h / windowHeight, -1.0, 1.0)

def keyboard(key, x, y):
# convert byte to str
key = key.decode('utf-8')
# press q to exit
if key == 'q':
print('exit')
sys.exit()

if __name__ == "__main__":

glutInitWindowPosition(0, 0);
glutInitWindowSize(widowWidth, windowHeight);
glutInit(sys.argv)
glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE )
glutCreateWindow("Display")
glutDisplayFunc(draw)
glutReshapeFunc(reshape)
glutKeyboardFunc(keyboard)
init()
glutIdleFunc(idle)
glutMainLoop()

しかし,上記のコードで表示して処理時間を測ってみると(カメラ読み込みではカメラのフレームレートに依存するためpng画像の読み込みで)あまり早くない。

調べてみると,glDrawPixelsは遅いらしい。テクスチャを貼り付ける形で表示すると早いらしいので,下記のコードのようにするのがおすすめである。画像はglTexImage2Dでテクスチャに変換するが,この場合上下反転する必要がなかった。

from OpenGL.GL import *

from OpenGL.GLU import *
from OpenGL.GLUT import *
import cv2
import numpy as np
import time

start = time.time()

# USB camera setup
cap = cv2.VideoCapture(0)
if cap.isOpened() is False:
raise("IO Error")
cap.set(cv2.CAP_PROP_FPS, 30)
cap.set(cv2.CAP_PROP_FRAME_WIDTH, 720)
cap.set(cv2.CAP_PROP_FRAME_HEIGHT, 480)

widowWidth = 720
windowHeight = 480

def draw():
# Paste into texture to draw at high speed
ret, img = cap.read() #read camera image
#img = cv2.imread('image.png') # if use the image file
img= cv2.cvtColor(img,cv2.COLOR_BGR2RGB) #BGR-->RGB
h, w = img.shape[:2]
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, w, h, 0, GL_RGB, GL_UNSIGNED_BYTE, img)

glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
glColor3f(1.0, 1.0, 1.0)

# Enable texture map
glEnable(GL_TEXTURE_2D)
# Set texture map method
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR)
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR)

# draw square
glBegin(GL_QUADS)
glTexCoord2d(0.0, 1.0)
glVertex3d(-1.0, -1.0, 0.0)
glTexCoord2d(1.0, 1.0)
glVertex3d( 1.0, -1.0, 0.0)
glTexCoord2d(1.0, 0.0)
glVertex3d( 1.0, 1.0, 0.0)
glTexCoord2d(0.0, 0.0)
glVertex3d(-1.0, 1.0, 0.0)
glEnd()

glFlush();
glutSwapBuffers()

def init():
glClearColor(0.7, 0.7, 0.7, 0.7)

def idle():
glutPostRedisplay()

def reshape(w, h):
glViewport(0, 0, w, h)
glLoadIdentity()
#Make the display area proportional to the size of the view
glOrtho(-w / widowWidth, w / widowWidth, -h / windowHeight, h / windowHeight, -1.0, 1.0)

def keyboard(key, x, y):
# convert byte to str
key = key.decode('utf-8')
# press q to exit
if key == 'q':
print('exit')
sys.exit()

if __name__ == "__main__":

glutInitWindowPosition(0, 0);
glutInitWindowSize(widowWidth, windowHeight);
glutInit(sys.argv)
glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE )
glutCreateWindow("Display")
glutDisplayFunc(draw)
glutReshapeFunc(reshape)
glutKeyboardFunc(keyboard)
init()
glutIdleFunc(idle)
glutMainLoop()