48578


<http://marina.sys.wakayama-u.ac.jp/~tokoi/?date=20140725>  
<http://13mzawa2.hateblo.jp/entry/2016/08/04/210552>

上記のページを参考に，OpenCVで読み込んだカメラ画像をOpenGLを使って描画しようとしています．OpenGLによる描画自体は成功するのですが，取得される画像サイズと同じ大きさのウィンドウを用意しているのにも関わらず，左下4分の1の領域にしか描画されません．  
OpenCVでそのまま描画するのと比較すると，OpenGLは4分の1にリサイズして左下に貼り付けているような形になっています．  
これをOpenGLで描画してもウィンドウいっぱいに表示されるようにしたいのですが，何が原因かわからず行き詰まっています．

OpenGLで描画したウィンドウをMacbook
PROに接続したディスプレイに移動させると，ウィンドウいっぱいに表示されたりするので，OpenGLのウィンドウの設定などではなく，描画の設定の問題なのだと思うのですが．．．（フラグメントシェーダとかでしょうか？）

よろしくお願いします．

PC: Macbook PRO  
OS: macOS 10.13.2  
Xcode: 9.4  
OpenCV: 3.3  
OpenGL: 4.1  
GLFW: 3.3  
GLSL: 4.1 (ページのコードのままなので，使用しているのは3.3だと思います)


CC BY-SA 4.0
2018-09-07T08:25:58.993
48180
2018-09-20T04:33:52.547
13125
question
xcode macos opencv opengl
OpenCVで画像を表示しようとしても左下4分の1に領域しか描画されない


Mac Proのディスプレイはたぶん2倍retinaですね。  
そうすると、Windowサイズを縦横100x100のサイズで取得すると、200ピクセルx200ピクセルでウィンドウを作成することになるからそういう動作になると思います。

対策は以下のURLが参考になるんじゃないかと思います。  
<https://qiita.com/sazameki/items/e60848466f7016e48b14>

NSViewのconvertSizeToBackingメソッドを使って、ViewPortに与えるサイズを変換してあげるということでしょうか。

何か参考になれば。

■追記  
こちらでも軽くサンプルプログラムを作成して実行してみましたが、うまく動作しました。  
テストプログラムでは、独自のシェーダーを使用せずに固定のパイプラインを使用しました。

試しに、以下のコードでglfwのバージョンを取得してみてください。

[code]

    int major,minor,rev;
    glfwGetVersion(&major, &minor, &rev);
    
[/code]

このコードは、glfwInitを呼び出す前に実行する必要があるみたいです。

glfwは3.0からretina対応したみたいなので、3以降を使われていたら問題ないと思うのですが、質問に記載のglfw3.3というものが見当たらなかったです。  
最新でもglfw3.2.1のようです。  
<http://www.glfw.org/changelog.html>

こちらで試したglfwのバージョンは3.2.1になります。

■再追記(2018/09/19)  
こちらも3.3.0で動作確認してみましたが、やはりRetinaディスプレイでもうまく表示されました。  
以下のコードです。  
一度以下のコードで試してもらうことはできますか？

[code]

    #pragma clang diagnostic push
    #pragma clang diagnostic ignored "-Wdocumentation"
    
    #include <iostream>
    //#include <gl/glew.h>
    #include <GLFW/glfw3.h>
    
    int main(int argc, const char * argv[])
    {
        int major,minor,rev;
        glfwGetVersion(&major, &minor, &rev);
    
        printf("%d.%d.%d", major, minor, rev);
    
    
        // GLFW初期化
        if (glfwInit() == GL_FALSE)
        {
            return -1;
        }
    
        GLFWwindow *cameraWindow = glfwCreateWindow(640, 480, "Captured Image", NULL, NULL);
    
        // バージョン3.2指定
        glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
        glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 2);
        glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);
        glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
    
    
        glfwMakeContextCurrent(cameraWindow);
        int w, h;
        glfwGetFramebufferSize(cameraWindow, &w, &h);
        glViewport(0, 0, w, h);
    
        glfwSwapInterval(1);
    
        // フレームループ
        while (glfwWindowShouldClose(cameraWindow) == GL_FALSE)
        {
            // バッファのクリア
            glClearColor(0.2f, 0.2f, 0.2f, 0.0f);
            glClear(GL_COLOR_BUFFER_BIT);
    
            // 色指定
            glColor4f(1.0, 0.0, 0.0, 1.0);
    
            // 3つの頂点座標をGPUに転送
            glBegin(GL_TRIANGLES);
            glVertex2f(   0,  0.5);
            glVertex2f(-0.5, -0.5);
            glVertex2f( 0.5, -0.5);
            glEnd();
    
            // ダブルバッファのスワップ
            glfwSwapBuffers(cameraWindow);
            glfwPollEvents();
    
            glfwGetFramebufferSize(cameraWindow, &w, &h);
    
        }
    
        // GLFWの終了処理
        glfwTerminate();
    
        return 0;
    }
    
[/code]


CC BY-SA 4.0
2018-09-07T08:55:12.407
48182
2018-09-19T09:48:38.527
2018-09-19T09:48:38.527
17014
17014
48180
answer


上の回答が長くなってきているので、新しく回答を作成させていただきました。

GLImage.hを以下のように修正してください。  
これで行けるはずです。  
こちらでも同様の環境を作って、以下のコードでうまく表示できることを確認しました。

画面上に表示している板にちゃんとテクスチャUV座標を指定して描画させています。

[code]

    //
    //  OpenGLHeader.h
    //  PerceptualProjection
    //
    //  Created by Ryo Akiyama on 2018/09/06.
    //  Copyright © 2018年 Ryo Akiyama. All rights reserved.
    //
    
    /********************************************************
     OpenGL Image with OpenCV
     GLFWでOpenCVのcv::Matを背景描画するためのクラス
    
     How to Use:
     1. メインループに入る前にGLImageを生成
     2. 描画したいGLFWwindowを与えてGLImageを初期化
     3. メインループ内で次の様に書く(ex. mainWindowの背景にframeImgを描画)
    
     // Change Current Window
     glfwMakeContextCurrent(mainWindow);
     // Clear Buffer Bits
     glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
     // Draw Image
     glImg.draw(frameImg);      //  <- Only rendering
     // Clear Depth Bits (so you can overwride CG on frameImg)
     glClear(GL_DEPTH_BUFFER_BIT);
     // Draw your CG
     // End Draw
     glfwSwapBuffers(mainWindow);
    
     Change 20160119:
     ・コンストラクタで初期化できるようにした
     ・コメント大幅追加
     ・GLSLをインライン化して外部ファイルを不要にした
    
     *********************************************************/
    
    #pragma once
    #pragma clang diagnostic push
    #pragma clang diagnostic ignored "-Wdocumentation"
    
    //#pragma comment(lib, "opengl32.lib")
    //#pragma comment(lib, "glew32.lib")
    //#pragma comment(lib, "glfw3.lib")
    #include <stdio.h>
    #include <stdlib.h>
    #include <iostream>
    
    #include <GL/glew.h>
    #include <GLFW/glfw3.h>
    #include "Shader.h"
    #include <opencv2/opencv.hpp>
    
    class GLImage
    {
    private:
        GLFWwindow *imgWindow;
        GLuint vao;     // 頂点配列オブジェクト
        GLuint vbo;     // 頂点バッファオブジェクト
        GLuint vbo_tex;     // TextureCoordinate ////テクスチャUV座標送信用のバッファー
        GLuint image;   // テクスチャオブジェクト
        GLuint imageLoc;// オブジェクトの場所
        Shader s;       // シェーダ
        // バーテックスシェーダ
        const char *vertexSource =
        "#version 330 core \n"
        "layout(location = 0) in vec4 pv;\n"
        "layout(location = 1) in vec2 st;\n"////テクスチャUV座標を受け取る
        "out vec2 txcoord;\n"////FragmentShaderに出力するテクスチャUV変数名の定義
        "void main(void)\n"
        "{\n"
        "    gl_Position = pv;\n"
        "    txcoord = st;\n"////FragmentShaderにテクスチャUV座標を送信する
        "}\n";
        // フラグメントシェーダ
        const char *fragmentSource =
        "#version 330 core \n"
        "uniform sampler2DRect image;\n"
        "in vec2 txcoord;\n"////VertexShaderからテクスチャUV座標を受け取る
        "out vec4 fc;\n"
        "void main(void)\n"
        "{\n"
        "    fc = texture(image, txcoord);\n"////テクスチャのUV座標をベースに色を決定
    //  "    fc = vec4(1.0, 0.0, 0.0, 1.0);\n"////テストで画面を真っ赤に
        "}\n";
    
    //    const char *fragmentSource =
    //    "#version 330 core \n"
    //    "uniform sampler2DRect image;\n"
    //    "void main(void)\n"
    //    "{\n"
    //    "    vec4 fc = texture(image, gl_FragCoord.xy);\n"
    //    "}\n";
        int vertices;
    
    public:
        GLImage()
        {
        }
        GLImage(GLFWwindow *window)
        {
            init(window);
        }
        void init(GLFWwindow *window)
        {
            int w, h;
            glfwMakeContextCurrent(window);
            glfwGetWindowSize(window, &w, &h);
            imgWindow = window;
    
    
            glewExperimental = GL_TRUE;
            glewInit();
    
    //        int width, height;
    //        glfwGetFramebufferSize( imgWindow, &width, &height);
    //        glViewport(0, 0, width/2, height/2);
    
            // 頂点配列オブジェクト
            glGenVertexArrays(1, &vao);
            glBindVertexArray(vao);
    
    //        glViewport(0, 0, width/2, height/2);
    
            // 頂点バッファオブジェクト
            glGenBuffers(1, &vbo);
            glBindBuffer(GL_ARRAY_BUFFER, vbo);
    
    
    
    //        glViewport(0, 0, width/2, height/2);
    
            // [-1, 1] の正方形
            static const GLfloat position[][2] =
            {
                { -1.0f, -1.0f },
                { 1.0f, -1.0f },
                { 1.0f, 1.0f },
                { -1.0f, 1.0f }
            };
            vertices = sizeof(position) / sizeof (position[0]);
            glBufferData(GL_ARRAY_BUFFER, sizeof(position), position, GL_STATIC_DRAW);
            glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 0, 0);
            glEnableVertexAttribArray(0);
    
            ////テクスチャUV座標をシェーダーに渡す。　ここから
            glGenBuffers(1, &vbo_tex);
            glBindBuffer(GL_ARRAY_BUFFER, vbo_tex);
    
            static const GLfloat texcoord[][2] =
            {
                { 0.0f * w, 0.0f * h },
                { 1.0f * w, 0.0f * h },
                { 1.0f * w, 1.0f * h },
                { 0.0f * w, 1.0f * h }
            };
    
    
            glBufferData(GL_ARRAY_BUFFER, sizeof(texcoord), texcoord, GL_STATIC_DRAW);
            glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 0, 0);
            glEnableVertexAttribArray(1);
            ////テクスチャUV座標をシェーダーに渡す。　ここまで
    
            // テクスチャ
            glGenTextures(1, &image);
            glBindTexture(GL_TEXTURE_RECTANGLE, image);
            glTexImage2D(GL_TEXTURE_RECTANGLE, 0, GL_RGB, w, h, 0, GL_BGR, GL_UNSIGNED_BYTE, NULL);
            glTexParameteri(GL_TEXTURE_RECTANGLE, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
            glTexParameteri(GL_TEXTURE_RECTANGLE, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
            glTexParameteri(GL_TEXTURE_RECTANGLE, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER);
            glTexParameteri(GL_TEXTURE_RECTANGLE, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER);
    
            // シェーダのロード
            s.initInlineGLSL(vertexSource, fragmentSource);
            imageLoc = glGetUniformLocation(s.program, "image");
        }
        void draw(cv::Mat frame)
        {
            glfwMakeContextCurrent(imgWindow);
            glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    
            // 切り出した画像をテクスチャに転送する
            cv::flip(frame, frame, 0);
            glBindTexture(GL_TEXTURE_RECTANGLE, image);
            glTexSubImage2D(GL_TEXTURE_RECTANGLE, 0, 0, 0, frame.cols, frame.rows, GL_BGR, GL_UNSIGNED_BYTE, frame.data);
    //        glTexImage2D(GL_TEXTURE_RECTANGLE, 0, GL_RGB, frame.cols, frame.rows, 0, GL_BGR, GL_UNSIGNED_BYTE, frame.data);
    //        glViewport(0, 0, frame.cols, frame.rows);
    //        GLint originalViewport[4];
    //        std::cout<<"viewport: "<<std::endl;
    //        glGetIntegerv(GL_VIEWPORT, originalViewport);
    
    
            // シェーダプログラムの使用開始
            s.enable();
    
            // uniform サンプラの指定
            glUniform1i(imageLoc, 0);
    //        glViewport(0, 0, 100, 150);
            // テクスチャユニットとテクスチャの指定
            glActiveTexture(GL_TEXTURE0);
            glBindTexture(GL_TEXTURE_RECTANGLE, image);
    //        glViewport(0, 0, 100, 200);
            // 描画に使う頂点配列オブジェクトの指定
            glBindVertexArray(vao);
    
    //        int w, h;
    //        glfwGetFramebufferSize( imgWindow, &w, &h );
    //        glViewport(0, 0, w, h);
            // 図形の描画
            // ここでviewportが効いてる
    //        glViewport(0, 0, 2000, 2000);
            glDrawArrays(GL_TRIANGLE_FAN, 0, vertices);
    
    //        glfwGetFramebufferSize( imgWindow, &w, &h );
    //        glViewport(0, 0, w, h);
            // 頂点配列オブジェクトの指定解除
            glBindVertexArray(0);
    
            // シェーダプログラムの使用終了
            s.disable();
        }
    };
    
[/code]


CC BY-SA 4.0
2018-09-20T04:10:09.617
48578
2018-09-20T04:33:52.547
2018-09-20T04:33:52.547
17014
17014
48180
answer