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

# はじめに
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で渡してやる。

```python3

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でテクスチャに変換するが，この場合上下反転する必要がなかった。


```python3
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()

```