FC2カウンター FPGAの部屋 ステレオカメラによる画像解析

FPGAやCPLDの話題やFPGA用のツールの話題などです。 マニアックです。 日記も書きます。

FPGAの部屋

FPGAの部屋の有用と思われるコンテンツのまとめサイトを作りました。Xilinx ISEの初心者の方には、FPGAリテラシーおよびチュートリアルのページをお勧めいたします。

OpenCV 2.4.10 の stereo_calib.cpp を自分のカメラ画像でやってみた2

”OpenCV 2.4.10 の stereo_calib.cpp を自分のカメラ画像でやってみた1”の続き。

前回は自分で作ったチェスボードを使ってステレオカメラのキャリブレーションをしようとしたら、チェスボードの左目カメラ画像と右目カメラ画像のペアを認識してくれなかった。今回は、チェスボードを変更してみたら、ペアを認識してくれたが、ステレオカメラのキャリブレーションがまだおかしい。

さて、いろいろとチェスボードの位置や大きさを変更して、stereo_calib を実行したが、上手くペアを認識してくれない。それではチェスボードがおかしいのだろうか?ということで、背景だけの画像ファイルにOpenCV のサンプルのチェスボードだけをコピー&ペーストしたものを混ぜてみたら、見事それはペアと認識されているようだった。

やはり、チェスボードのパターンが重要のようだった。ちなみに、stereo_calib のオプションで -w がチェスボードのコマの横の数、-h が縦の数を指定できるので、指定していたのだが、やはりペアと認識してくれなかった。

OpenCV のサンプルで使用されているチェスボードを作ることにした。
stereo_calib_62_160311.png

それで、チェスボードの画像を撮影した。取りあえず、3ペア撮影した。その内の最初のペアを下に示す。
stereo_calib_63_160311.jpg

グレースケール、640 x 480 ピクセルに変換した。
stereo_calib_64_160311.jpg

./stereo_calib コマンドを実行した。
3ペアを認識してくれた。。。
stereo_calib_65_160311.png

しかし、rectified ウインドウは真っ黒だった。
stereo_calib_66_160311.png

OpenCVのサンプルだと、左目カメラ画像と右目カメラ画像が表示されていた。
stereo_calib_7_160220.png

./stereo_calib コマンドが終了すると、extrinsics.yml と intrinsics.yml ができていた。
stereo_calib_67_160311.png

次に、stereo_match.cpp を使用して、2つのキャリブレーション・ファイルを入力して、左目カメラ画像と右目カメラ画像を補正してみた。(参考URLは、”OpenCV 2.4.10 の stereo_match.cpp をやってみた”と”OpenCV 2.4.10 の stereo_match.cpp をやってみた2”)

./stereo_match -i intrinsics.yml -e extrinsics.yml left0.jpg right0.jpg
を実行した。
stereo_calib_68_160312.png

結果を下に示す。やはりおかしい。
stereo_calib_69_160312.jpg

./stereo_match -i intrinsics.yml -e extrinsics.yml left1.jpg right1.jpg
も実行してみたが、やはりおかしい。
stereo_calib_70_160312.jpg
  1. 2016年03月11日 05:23 |
  2. ステレオカメラによる画像解析
  3. | トラックバック:0
  4. | コメント:0

OpenCV 2.4.10 の stereo_calib.cpp を自分のカメラ画像でやってみた1

OpenCV 2.4.10 の stereo_match.cpp を自分のカメラ画像でやってみた1”の続きなのだが、とりあえずはstereo_calib.cpp をやる必要があるので、タイトルを変更した。

前回は、自分のカメラでカメラ画像を取得できたが、チェスボードの升目の境界辺りがおかしかった。そのバグは修正できたので、もう一度、カメラ画像を取得して stereo_calib.cpp をやってみよう。

チェスボードの升目の境界辺りのバグは、”ZYBO_0_2のビットマップ・ディスプレイ・コントローラのバグフィックス”で修正された。
更に、黒いドットも”ZYBO_0_2のビットマップ・ディスプレイ・コントローラのバグフィックス2”で修正された。

カメラ画像は StereoCam_Alt_Disp で表示させてから、RL_capture_bmp を使用して、BMPファイルに保存する。(”左目カメラ画像と右目カメラ画像をBMPファイルに変換するアプリケーションを作成した”参照)
stereo_calib_47_160309.jpg

3つのチェスボードの画像を保存した。
stereo_calib_48_160309.jpg

stereo_calib_49_160309.jpg

stereo_calib_50_160309.jpg

BMPファイルは800 ピクセル x 600 ラインの大きさなので、640 ピクセル x 480 ラインに縮小して、convert_calibf コマンドで、グレースケールに変換してから、JPGファイルとして保存する。(”左目カメラ画像、右目カメラ画像のBMPファイルをVGAサイズ+グレースケールに変換”参照)
stereo_calib_51_160309.png

stereo_calib_52_160309.png

最初の1ペアのみ表示する。
stereo_calib_53_160309.jpg

JPGファイルだけをWork ディレクトリに移動し、Work ディレクトリに移動した。
stereo_calib_54_160309.png

Work ディレクトリ
stereo_calib_55_160309.png

stereo_calib.xml は 3ペアのみにした。
stereo_calib_56_160309.png

./stereo_calib を実行したところ、1つもペアとして認識してくれなかった。
stereo_calib_57_160309.png

libdc1394 error: Failed to initialize libdc1394
...0 pairs have been successfully detected.
Error: too little pairs to run the calibration



そこで、OpenCV のサンプル用の left01.jpg と right01.jpg を追加して stereo_calib をやってみることにした。
stereo_calib_58_160309.png

stereo_calib.xml にも追加した。
stereo_calib_59_160309.png

結果は1ペアだけ認識できた。
stereo_calib_60_160309.png

つまり、OpenCV のサンプル用の left01.jpg と right01.jpg だけ認識できたということだ。

私の撮った画像と何処が違うのか?と探したのだが、今のところ、私の撮った画像では持ち手が無いので、チェスボードに持ち手がかぶっているが、OpenCV のサンプル用の left01.jpg と right01.jpg は持ち手がかぶっていない。その違いかな?
下に、OpenCV のサンプル用の left01.jpg と right01.jpg を引用する。
stereo_calib_61_160309.jpg

チェスボードを手で持たないようにしてやってみよう。
  1. 2016年03月08日 05:23 |
  2. ステレオカメラによる画像解析
  3. | トラックバック:0
  4. | コメント:3

OpenCV 2.4.10 の stereo_match.cpp を自分のカメラ画像でやってみた1

左目カメラ画像と右目カメラ画像をBMPファイルに変換するアプリケーションを作成した”の RL_capture_bmp アプリケーションを使用して、自分のカメラの画像を右目カメラ画像、左目カメラ画像共に1ペア撮影した。

left0.jpg と right0.jpg だ。
stereo_calib_38_160229.png

stereo_calib_39_160229.png

left0.bmp を表示してみた。
stereo_calib_40_160229.jpg

チェスボードの升目の境界辺りを拡大してみよう。こっちは問題ない。
stereo_calib_42_160229.jpg

どうも前から、カメラ画像を大きな画面で表示させた時におかしいとは思っていたのだが、1ピクセル分、となりのピクセルと入れ替わっているように見える。つまり、64ビット幅のデータパスで32ビット分、上位下位を間違っている感じだ。

カメラ画像をディスプレイに表示したものがおかしく、BMPファイルは正常に見える。
右目カメラ画像は、ビットマップ・ディスプレイ・コントローラからの出力をHDMI経由で送付されているので、マスの境界がおかしい感じだ。rigth0.jpg を示す。
stereo_calib_41_160229.jpg

チェスボードの升目の境界辺りを拡大してみよう。やはり境界がおかしいようだ。
stereo_calib_43_160229.jpg

ちなみに、所々にあるドットは以前から分かっているバグである。
  1. 2016年03月01日 03:38 |
  2. ステレオカメラによる画像解析
  3. | トラックバック:0
  4. | コメント:0

左目カメラ画像、右目カメラ画像のBMPファイルをVGAサイズ+グレースケールに変換

左目カメラ画像と右目カメラ画像をBMPファイルに変換するアプリケーションを作成した”で作った左目カメラ画像と右目カメラ画像のSVGAサイズのBMPファイルをVGAサイズに変更して、グレースケールにも変更する。

変換するには、Linux のコマンドの convert を使用して、シェルスクリプトのコマンドを作製する。

主に参考にさせていただいたWebページは、”bashで始めるシェルスクリプト基礎の基礎”でした。

convert_calibf という名前でシェルスクリプトを作成した。

chmod +x convert_calibf で実行権限を与えた。
stereo_calib_29_160226.png

./convert_calibf
で実行したが、convert コマンドが無いので、imagemagick と graphicsmagick-imagemagick-compat を apt-get install でインストールしろと言われた。
stereo_calib_30_160226.png

最初に
sudo apt-get install imagemagick
を実行した。
stereo_calib_31_160226.png

次に
sudo apt-get install graphicsmagick-imagemagick-compat
を実行した。
stereo_calib_32_160226.png

convert -gemonetory 640x480 -depth 8 -type GrayScale left_0.bmp left0.jpg

コマンドを実行したところ、left0.jpg ができた。
stereo_calib_33_160226.png

left0.jpg ができていて、白黒になっているのが分かる。
stereo_calib_34_160226.png

left_0.bmp、left0.jpg を並べてみた。画像が800 x 600 から 640 x 480 に縮小されている。
stereo_calib_35_160226.jpg

left_?.bmp を left?.bmp に名前を変更して、
./convert_calibf
を起動したところ、left?.jpg、right?.jpg ができた。
stereo_calib_36_160226.png

stereo_calib_37_160226.png

最後に convert_calib を貼っておく。

#!/bin/sh

for file in *.bmp
do
    convert -geometry 640x480 -depth 8 -type GrayScale $file ${file%bmp}jpg
done

  1. 2016年02月27日 03:58 |
  2. ステレオカメラによる画像解析
  3. | トラックバック:0
  4. | コメント:0

左目カメラ画像と右目カメラ画像をBMPファイルに変換するアプリケーションを作成した

OpenCV 2.4.10 の stereo_match.cpp をやってみた”と”OpenCV 2.4.10 の stereo_match.cpp をやってみた2”でやってみたOpenCVのステレオカメラのキャリブレーションを自分の環境でもやってみたくなって、左目カメラ画像と右目カメラ画像をBMPファイルに変換するアプリケーションを作成した。

2016/02/26:修正 左目カメラ画像の名前が left_0.bmp になってしまって、”_”が余計だったので、left0.bmp になるようにアプリケーションを修正しました。)

左目カメラ画像と右目カメラ画像をBMPファイルに変換するアプリケーションは、RL_capture_bmp.cpp という名前にした。
引数で左目カメラ画像と右目カメラ画像の名前を指定できる。デフォルトではそれぞれ、left, right という名前で、その後に番号とBMPファイルの拡張子が付く。(left00.bmp, right00.bmp left01.bmp, right01.bmp)
2016/03/22: -n オプションを追加)

// -l : left bmp file name
// -r : right bmp file name
// -n : Start File Number
// -h : help


stereo_calib_24_160225.png

g++ でコンパイルを行った。
g++ RL_capture_bmp.cpp -o RL_capture_bmp

./RL_capture_bmp で起動した。
コマンドは2つで w で左目カメラ画像と右目カメラ画像をBMPファイルに保存する。q で Exit する。
stereo_calib_25_160225.png

left00.bmp, right00.bmp left01.bmp, right01.bmp left02.bmp, right02.bmp ができているのが分かる。
stereo_calib_26_160225.png

left00.bmp を表示してみた。
stereo_calib_27_160225.jpg

right00.bmp を表示してみた。
stereo_calib_28_160225.jpg

やはり、細かいドットが出ているがステレオカメラのキャリブレーションは大丈夫だろうか?ダメならば、ここをデバックする必要があるかも知れない?

最後に、RL_capture_bmp.cpp を貼っておく。

//
// RL_capture_bmp.cpp
// 2016/02/24
// 2016/03/22 : -n : Start File Number
//
// This software converts the left and right of the camera image to BMP file.
// -l : left bmp file name
// -r : right bmp file name
// -n : Start File Number
// -h : help
//

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <assert.h>
#include <sys/mman.h>
#include <fcntl.h>
#include <string.h>

#include "bmpheader.h"

#define PIXEL_NUM_OF_BYTES    4
#define NUMBER_OF_WRITE_FRAMES    3 // Note: If not at least 3 or more, the image is not displayed in succession.

#define XGA_HORIZONTAL_PIXELS   1024
#define XGA_VERTICAL_LINES       768
#define XGA_ALL_DISP_ADDRESS    (XGA_HORIZONTAL_PIXELS * XGA_VERTICAL_LINES * PIXEL_NUM_OF_BYTES)
#define XGA_3_PICTURES          (XGA_ALL_DISP_ADDRESS * NUMBER_OF_WRITE_FRAMES) 

#define SVGA_HORIZONTAL_PIXELS  800
#define SVGA_VERTICAL_LINES     600
#define SVGA_ALL_DISP_ADDRESS   (SVGA_HORIZONTAL_PIXELS * SVGA_VERTICAL_LINES * PIXEL_NUM_OF_BYTES)
#define SVGA_3_PICTURES         (SVGA_ALL_DISP_ADDRESS * NUMBER_OF_WRITE_FRAMES)

int WriteBMPfile(FILE *fbmp, volatile unsigned int *frame_buffer, BMP24FORMAT **bmp_data);

int main(int argc, char *argv[]){
    int opt;
    int c, help_flag=0;
    char left_bmp_fn[256] = "left";
    char right_bmp_fn[256] = "right";
    unsigned char  attr[1024];
    unsigned long  phys_addr;
    volatile unsigned int *Leye_addr, *Reye_addr;
    int i, j;
    int file_no = 0;
    FILE *fbmp;
    BMP24FORMAT **bmp_data; // 24 bits Date of BMP files (SVGA_HORIZONTAL_PIXELS * SVGA_VERTICAL_LINES)

    while ((opt=getopt(argc, argv, "l:r:n:h")) != -1){
        switch (opt){
            case 'l':
                strcpy(left_bmp_fn, optarg);
                break;
            case 'r':
                strcpy(right_bmp_fn, optarg);
                break;
            case 'n':
                file_no = atoi(optarg);
                break;
            case 'h':
                help_flag = 1;
                break;
        }
    }
   
    if (help_flag == 1){ // help
        printf("Usage : RL_capture_bmp [-l <left bmp file name>] [-r <right bmp file name>] [-n <Start File Number>] [-h]\n");
    }

    // udmabuf0
    int fdf = open("/dev/udmabuf0", O_RDWR | O_SYNC); // frame_buffer, The chache is disabled. 
    if (fdf == -1){
        fprintf(stderr, "/dev/udmabuf0 open error\n");
        exit(-1);
    }
    volatile unsigned *frame_buffer = (volatile unsigned *)mmap(NULL, SVGA_3_PICTURES+XGA_3_PICTURES+SVGA_ALL_DISP_ADDRESS, PROT_READ|PROT_WRITE, MAP_SHARED, fdf, 0);
    if (!frame_buffer){
        fprintf(stderr, "frame_buffer mmap error\n");
        exit(-1);
    }

    // phys_addr of udmabuf0
    int fdp = open("/sys/devices/virtual/udmabuf/udmabuf0/phys_addr", O_RDONLY);
    if (fdp == -1){
        fprintf(stderr, "/sys/devices/virtual/udmabuf/udmabuf0/phys_addr open error\n");
        exit(-1);
    }
    read(fdp, attr, 1024);
    sscanf((const char *)attr, "%lx", &phys_addr);  
    close(fdp);
    printf("phys_addr = %x\n", (unsigned int)phys_addr);

    // allocated the memory for bmp file
    if ((bmp_data=(BMP24FORMAT **)malloc(sizeof(BMP24FORMAT *)*SVGA_VERTICAL_LINES)) == NULL){
        fprintf(stderr, "Can not allocate memory of the first dimension of SVGA_VERTICAL_LINES of bmp_data\n");
        exit(1);
    }
    for (i=0; i<SVGA_VERTICAL_LINES; i++){
        if ((bmp_data[i]=(BMP24FORMAT *)malloc(sizeof(BMP24FORMAT) * SVGA_HORIZONTAL_PIXELS)) == NULL){
            fprintf(stderr, "Can not allocate %d th memory of the first dimension of bmp_data\n", i);
            exit(1);
        }
    }
   
    // assigned the left and right eys's frame buffer
    Leye_addr = frame_buffer; // The Left Camera Image
    Reye_addr = (volatile unsigned int *)((unsigned)frame_buffer+SVGA_3_PICTURES+0x8); // The Right Camera Image

    char lbmp_file[256];
    char rbmp_file[256];

    // w - writed the left and right eye's bmp files.  q - exit.
    c = getc(stdin);
    while(c != 'q'){
        switch ((char)c) {
            case 'w' : // w - writed the left and right eye's bmp files.
                // writed the left and right eys's frame buffer
                sprintf(lbmp_file, "left%d.bmp", file_no);
                if ((fbmp=fopen(lbmp_file, "wb")) == NULL){
                    fprintf(stderr, "Cannot open %s in binary mode\n", lbmp_file);
                    exit(1);
                }
                WriteBMPfile(fbmp, Leye_addr, bmp_data);
                fclose(fbmp);

                sprintf(rbmp_file, "right%d.bmp", file_no);
                if ((fbmp=fopen(rbmp_file, "wb")) == NULL){
                    fprintf(stderr, "Cannot open %s in binary mode\n", rbmp_file);
                    exit(1);
                }
                WriteBMPfile(fbmp, Reye_addr, bmp_data);
                fclose(fbmp);
                
                printf("file No. = %d\n", file_no);

                file_no++;
                break;
        }
        c = getc(stdin);
    }

    for(i=0; i<SVGA_VERTICAL_LINES; i++){
        free(bmp_data[i]);
    }
    free(bmp_data);
    munmap((void *)frame_buffer, (SVGA_3_PICTURES+XGA_3_PICTURES+SVGA_ALL_DISP_ADDRESS));
    close(fdf);
    
    return(0);
}

int WriteBMPfile(FILE *fbmp, volatile unsigned *frame_buffer, BMP24FORMAT **bmp_data){
    BITMAPFILEHEADER bmpfh; // file header for a bmp file
    BITMAPINFOHEADER bmpih; // INFO header for BMP file

    // Copy the camera color data of the bmp_data (data of BMP when its starts from lower left)
    for (int i=0; i<SVGA_VERTICAL_LINES; i++){
        for (int j=0; j<SVGA_HORIZONTAL_PIXELS; j++){
            bmp_data[(SVGA_VERTICAL_LINES-1)-i][j].red = (frame_buffer[i*SVGA_HORIZONTAL_PIXELS+j]>>16)&0xff;
            bmp_data[(SVGA_VERTICAL_LINES-1)-i][j].green = (frame_buffer[i*SVGA_HORIZONTAL_PIXELS+j]>>8)&0xff;
            bmp_data[(SVGA_VERTICAL_LINES-1)-i][j].blue = (frame_buffer[i*SVGA_HORIZONTAL_PIXELS+j])&0xff;
        }
    }

    // Assign a value to the file header of the BMP file
    bmpfh.bfType = 0x4d42;
    bmpfh.bfSize = SVGA_HORIZONTAL_PIXELS*SVGA_VERTICAL_LINES*3+54;
    bmpfh.bfReserved1 = 0;
    bmpfh.bfReserved2 = 0;
    bmpfh.bfOffBits = 0x36;

    // Assign a value to the INFO header of the BMP file
    bmpih.biSize = 0x28;
    bmpih.biWidth = SVGA_HORIZONTAL_PIXELS;
    bmpih.biHeight = SVGA_VERTICAL_LINES;
    bmpih.biPlanes = 0x1;
    bmpih.biBitCount = 24;
    bmpih.biCompression = 0;
    bmpih.biSizeImage = 0;
    bmpih.biXPixPerMeter = 3779;
    bmpih.biYPixPerMeter = 3779;
    bmpih.biClrUsed = 0;
    bmpih.biClrImporant = 0;

    // Writing of BMP file header
    fwrite(&bmpfh.bfType, sizeof(char), 2, fbmp);
    fwrite(&bmpfh.bfSize, sizeof(long), 1, fbmp);
    fwrite(&bmpfh.bfReserved1, sizeof(short), 1, fbmp);
    fwrite(&bmpfh.bfReserved2, sizeof(short), 1, fbmp);
    fwrite(&bmpfh.bfOffBits, sizeof(long), 1, fbmp);

    // Writing of BMP INFO header
    fwrite(&bmpih, sizeof(BITMAPINFOHEADER), 1, fbmp);

    // Writing of lbmp_data anr rbmp_data
    for (int i=0; i<SVGA_VERTICAL_LINES; i++) {
        for (int j=0; j<SVGA_HORIZONTAL_PIXELS; j++) {
            fputc((int)bmp_data[i][j].blue, fbmp);
            fputc((int)bmp_data[i][j].green, fbmp);
            fputc((int)bmp_data[i][j].red, fbmp);
        }
    }
}

  1. 2016年02月25日 05:22 |
  2. ステレオカメラによる画像解析
  3. | トラックバック:0
  4. | コメント:0

ステレオカメラによる距離測定テスト14(ステレオカメラ・キャリブレーション方法の参考になるWebサイト)

ステレオカメラによる距離測定テスト13(チェッカーボードの撮影結果)”の続きです。

前回は、チェッカーボード(チェスボード)をカメラで撮影して、歪を確認した。今回は、ステレオカメラやカメラのキャリブレーション方法についての参考になるWebページを書いておこうと思います。

ステレオカメラの歪み補正&平行化
ここからのリンクで、OpenCV のカメラキャリブレーションがあるので、これもやってみようと思っています。
但し、これを行うためには、画像をファイルに変換する必要があるので、画像保存ソフトウェアを作る必要があります。

OpenCV Stereo Camera Calibration/Image Rectification
Stereo Camera Calibration by Detecting Chessboard Corners (Project SAHE)のビデオへのリンクがありました。
これ見てて気がついたのですが、OpenCVのC++ のサンプルに、stereo_calib.cpp がありました。
StereoCamTest_83_160219.png

最初にこれを見てみて、やってみようと思っています。但し、これをやるにも画像を画像ファイルにするソフトウェアが必要ですね。

Camera calibration With OpenCV
Theory も載っているので、勉強になりますね。最も、”詳解 OpenCV ―コンピュータビジョンライブラリを使った画像処理・認識”の”第11章 カメラモデルとキャリブレーション”にカメラ単体のキャリブレーション方法とステレオカメラのキャリブレーション方法のやり方が詳しく載ってますね。これを読むのがお勧めです。

OpenCVでステレオ画像処理
PointGreyのGigEカメラ BlackFly2台でステレオカメラにしているようです。

OpenCVでステレオ画像処理(その2)
ステレオ対応点探索の前に、左右の画像を平行化を行っています。チェスボード画像やサンプルコードがあります。

OpenCVでステレオ画像処理(その3)
キャリブレーションボードを使ったいわゆる普通のステレオキャリブレーションをOpenCVでやる方法が書いてあります。今から stereo_calib.cpp をやってみるのに、とってもためになりそうです。

カメラキャリブレーションと3次元再構成
カメラのキャリブレーション関連の関数の説明があります。ステレオカメラのキャリブレーション関連の関数の説明もあります。
  1. 2016年02月19日 05:20 |
  2. ステレオカメラによる画像解析
  3. | トラックバック:0
  4. | コメント:0

ステレオカメラによる距離測定テスト13(チェッカーボードの撮影結果)

ステレオカメラによる距離測定テスト12(焦点距離の計算)”の続き。

前回の結果はおかしいというご指摘を頂いたので、すでにチェッカーボードを自作してあったので、これでカメラの歪をみようと思う。
なお、カメラの性能やステレオカメラのキャリブレーション方法のことはよく知らないので、どの本が良いか、コメント欄にアドバイスしていただくととっても嬉しい。よろしくお願いします。
今持っている本は、”詳解 OpenCV――コンピュータビジョンライブラリを使った画像処理・認識”と”デジタル画像処理”だ。

さて、チェッカーボードを左カメラで撮影するのだが、右手でチェッカーボードを持って、左手でスマフォのカメラで撮影できない。
確か、ビットマップ・ディスプレイ・コントローラに静止画機能を付けていたと思って、HDLソースコードを見たが付いていない。もしかしてカメラ・インターフェース?と思ってみると、こっちに付いていた。

// mt9d111_axi_lite_slave.v 
// mt9d111_inf_axi_master のAXI Lite Slave モジュール。Frame Buffer のスタートアドレス・レジスタを持つ。
//
// 2014/11/08 : one_shot_reg を実装。
// オフセット0番地: フレーム・バッファの先頭アドレス(fb_start_address)
// オフセット4番地: 0 ビット目が 0 の時動画、0 ビット目に 1 の時に、ワンショットで取得した1フレームのカメラ画像を表示(one_shot_reg)
//            1 ビット目に 1 を Write した時に、ワンショットで1フレームの画像をフレーム・バッファに保存


つまり、オフセット0x4 に 0 を書くと連続画像キャプチャ、1 を書くと画像が止まる。
これを、StereoCam_Alt_Disp.c に追加した。
s キーを押すと、画像連続キャプチャ、一時停止を繰り返す。
StereoCamTest_81_160217.png

このアプリケーションで撮影したチェッカーボードを下に示す。
StereoCamTest_82_160217.jpg

樽型歪が出ているのが分かる。

修正した StereoCam_Alt_Disp.c を貼っておく。

//
// StereoCam_Alt_Disp.c
// 2016/02/01 by marsee 
//
// 2016/02/17 : 's' commannd : Camera Start and Stop Command, but left camera only.
//

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <assert.h>
#include <sys/mman.h>
#include <fcntl.h>

#define PIXEL_NUM_OF_BYTES    4
#define NUMBER_OF_WRITE_FRAMES    3 // Note: If not at least 3 or more, the image is not displayed in succession.

#define XGA_HORIZONTAL_PIXELS    1024
#define XGA_VERTICAL_LINES        768
#define XGA_ALL_DISP_ADDRESS    (XGA_HORIZONTAL_PIXELS*XGA_VERTICAL_LINES*PIXEL_NUM_OF_BYTES)

#define SVGA_HORIZONTAL_PIXELS    800
#define SVGA_VERTICAL_LINES        600
#define SVGA_ALL_DISP_ADDRESS    (SVGA_HORIZONTAL_PIXELS*SVGA_VERTICAL_LINES*PIXEL_NUM_OF_BYTES)

void cam_i2c_init(volatile unsigned *caminf_axi_iic) {
    caminf_axi_iic[64] = 0x2// reset tx fifo ,address is 0x100, i2c_control_reg
    caminf_axi_iic[64] = 0x1// enable i2c
}

void cam_i2x_write_sync(void) {
    // unsigned c;

    // c = *cam_i2c_rx_fifo;
    // while ((c & 0x84) != 0x80)
        // c = *cam_i2c_rx_fifo; // No Bus Busy and TX_FIFO_Empty = 1
    usleep(1000);
}

void cam_i2c_write(volatile unsigned *caminf_axi_iic, unsigned int device_addr, unsigned int write_addr, unsigned int write_data){
    caminf_axi_iic[66] = 0x100 | (device_addr & 0xfe);    // Slave IIC Write Address, address is 0x108, i2c_tx_fifo
    caminf_axi_iic[66] = write_addr;
    caminf_axi_iic[66] = (write_data >> 8)|0xff;            // first data
    caminf_axi_iic[66] = 0x200 | (write_data & 0xff);        // second data
    cam_i2x_write_sync();
}

int main()
{
    int fd0, fd1, fd2, fd3, fd4, fd5, fd6, fd7, fd9, fd10;
    volatile unsigned *bmdc_axi_lites0;
    volatile unsigned *caminf_axi_vdma_0, *dviin_axi_vdma_0;
    volatile unsigned *caminf_axis_switch_0, *caminf_axis_switch_1;
    volatile unsigned *caminf_mt9d111_inf_axis_0;
    volatile unsigned *caminf_axi_iic;
    volatile unsigned *caminf_lap_filter_axis_0;
    volatile unsigned *frame_buffer;
    unsigned char  attr[1024];
    unsigned long  phys_addr;
    char c;
    int laps_cntrl;

    // Bitmap Display Controller 0 AXI4 Lite Slave (UIO6)
    fd6 = open("/dev/uio6", O_RDWR); // bitmap_display_controller 0 axi4 lite
    if (fd6 < 1){
        fprintf(stderr, "/dev/uio6 (bitmap_disp_cntrler_axi_master_0) open errorn");
        exit(-1);
    }
    bmdc_axi_lites0 = (volatile unsigned *)mmap(NULL, 0x10000, PROT_READ|PROT_WRITE, MAP_SHARED, fd6, 0);
    if (!bmdc_axi_lites0){
        fprintf(stderr, "bmdc_axi_lites0 mmap errorn");
        exit(-1);
    }
    
    // caminf_axi_vdma_0 (UIO1)
    fd1 = open("/dev/uio1", O_RDWR); // caminf_axi_vdma_0 interface AXI4 Lite Slave
    if (fd1 < 1){
        fprintf(stderr, "/dev/uio1 (caminf_axi_vdma_0) open errorn");
        exit(-1);
    }
    caminf_axi_vdma_0 = (volatile unsigned *)mmap(NULL, 0x10000, PROT_READ|PROT_WRITE, MAP_SHARED, fd1, 0);
    if (!caminf_axi_vdma_0){
        fprintf(stderr, "caminf_axi_vdma_0 mmap errorn");
        exit(-1);
    }
    
    // dviin_axi_vdma_0 (UIO7)
    fd7 = open("/dev/uio7", O_RDWR); // dviin_axi_vdma_0 interface AXI4 Lite Slave
    if (fd7 < 1){
        fprintf(stderr, "/dev/uio7 (dviin_axi_vdma_0) open errorn");
        exit(-1);
    }
    dviin_axi_vdma_0 = (volatile unsigned *)mmap(NULL, 0x10000, PROT_READ|PROT_WRITE, MAP_SHARED, fd7, 0);
    if (!dviin_axi_vdma_0){
        fprintf(stderr, "dviin_axi_vdma_0 mmap errorn");
        exit(-1);
    }
    
    // mt9d111 i2c AXI4 Lite Slave (UIO0)
    fd0 = open("/dev/uio0", O_RDWR); // mt9d111 i2c AXI4 Lite Slave
    if (fd0 < 1){
        fprintf(stderr, "/dev/uio0 (caminf_axi_iic) open errorn");
        exit(-1);
    }
    caminf_axi_iic = (volatile unsigned *)mmap(NULL, 0x10000, PROT_READ|PROT_WRITE, MAP_SHARED, fd0, 0);
    if (!caminf_axi_iic){
        fprintf(stderr, "caminf_axi_iic mmap errorn");
        exit(-1);
    }

    // mt9d111 inf axis AXI4 Lite Slave (UIO5)
    fd5 = open("/dev/uio5", O_RDWR); // mt9d111 inf axis AXI4 Lite Slave
    if (fd5 < 1){
        fprintf(stderr, "/dev/uio5 (caminf_mt9d111_inf_axis_0) open errorn");
        exit(-1);
    }
    caminf_mt9d111_inf_axis_0 = (volatile unsigned *)mmap(NULL, 0x10000, PROT_READ|PROT_WRITE, MAP_SHARED, fd5, 0);
    if (!caminf_mt9d111_inf_axis_0){
        fprintf(stderr, "caminf_mt9d111_inf_axis_0 mmap errorn");
        exit(-1);
    }

    // caminf_axis_switch_0 (UIO2)
    fd2 = open("/dev/uio2", O_RDWR); // caminf_axis_switch_0 interface AXI4 Lite Slave
    if (fd2 < 1){
        fprintf(stderr, "/dev/uio2 (caminf_axis_switch_0) open errorn");
        exit(-1);
    }
    caminf_axis_switch_0 = (volatile unsigned *)mmap(NULL, 0x10000, PROT_READ|PROT_WRITE, MAP_SHARED, fd2, 0);
    if (!caminf_axis_switch_0){
        fprintf(stderr, "caminf_axis_switch_0 mmap errorn");
        exit(-1);
    }
    
    // caminf_axis_switch_1 (UIO3)
    fd3 = open("/dev/uio3", O_RDWR); // caminf_axis_switch_1 interface AXI4 Lite Slave
    if (fd3 < 1){
        fprintf(stderr, "/dev/uio3 (caminf_axis_switch_1) open errorn");
        exit(-1);
    }
    caminf_axis_switch_1 = (volatile unsigned *)mmap(NULL, 0x10000, PROT_READ|PROT_WRITE, MAP_SHARED, fd3, 0);
    if (!caminf_axis_switch_1){
        fprintf(stderr, "caminf_axis_switch_1 mmap errorn");
        exit(-1);
    }
    
    // caminf_lap_filter_axis_0 (UIO4)
    fd4 = open("/dev/uio4", O_RDWR); // caminf_lap_filter_axis_0 interface AXI4 Lite Slave
    if (fd4 < 1){
        fprintf(stderr, "/dev/uio4 (caminf_lap_filter_axis_0) open errorn");
        exit(-1);
    }
    caminf_lap_filter_axis_0 = (volatile unsigned *)mmap(NULL, 0x10000, PROT_READ|PROT_WRITE, MAP_SHARED, fd4, 0);
    if (!caminf_lap_filter_axis_0){
        fprintf(stderr, "caminf_lap_filter_axis_0 mmap errorn");
        exit(-1);
    }
    
    // udmabuf0
    fd9 = open("/dev/udmabuf0", O_RDWR | O_SYNC); // frame_buffer, The chache is disabled. 
    if (fd9 == -1){
        fprintf(stderr, "/dev/udmabuf0 open errorn");
        exit(-1);
    }
    frame_buffer = (volatile unsigned *)mmap(NULL, (XGA_ALL_DISP_ADDRESS*NUMBER_OF_WRITE_FRAMES)+(SVGA_ALL_DISP_ADDRESS*NUMBER_OF_WRITE_FRAMES), PROT_READ|PROT_WRITE, MAP_SHARED, fd9, 0);
    if (!frame_buffer){
        fprintf(stderr, "frame_buffer mmap errorn");
        exit(-1);
    }

    // caminf_axis_switch_1, 1to2 ,Select M00_AXIS
    // Refer to http://marsee101.blog19.fc2.com/blog-entry-3177.html
    caminf_axis_switch_1[16] = 0x0// 0x40 = 0
    caminf_axis_switch_1[17] = 0x80000000// 0x44 = 0x80000000, disable
    caminf_axis_switch_1[0] = 0x2// Comit registers
    
    // caminf_axis_switch_0, 2to1, Select S00_AXIS
    // Refer to http://marsee101.blog19.fc2.com/blog-entry-3177.html
    caminf_axis_switch_0[16] = 0x0// 0x40 = 0;
    caminf_axis_switch_0[0] = 0x2// Comit registers
    
    // phys_addr of udmabuf0
    fd10 = open("/sys/devices/virtual/udmabuf/udmabuf0/phys_addr", O_RDONLY);
    if (fd10 == -1){
        fprintf(stderr, "/sys/devices/virtual/udmabuf/udmabuf0/phys_addr open errorn");
        exit(-1);
    }
    read(fd10, attr, 1024);
    sscanf(attr, "%lx", &phys_addr);  
    close(fd10);
    printf("phys_addr = %xn", (unsigned)phys_addr);
    
    // AXI VDMA Initialization sequence (caminf_axi_vdma_0)
    caminf_axi_vdma_0[12] = 0x4// S2MM_VDMACR (S2MM VDMA Control Register  Offset 30h) is 0x4 
    while ((caminf_axi_vdma_0[12] & 0x4) == 0x4) ; // Reset is progress
    caminf_axi_vdma_0[12] = 0x4// S2MM_VDMACR (S2MM VDMA Control Register  Offset 30h) is 0x4 
    while ((caminf_axi_vdma_0[12] & 0x4) == 0x4) ; // Reset is progress
    caminf_axi_vdma_0[18] = NUMBER_OF_WRITE_FRAMES; // S2MM_FRMSTORE (0x48) register
    caminf_axi_vdma_0[12] = 0x00010002// S2MM_VDMACR(IRQFrameCount = 0x1, Circular_Park = 1)
    caminf_axi_vdma_0[41] = SVGA_HORIZONTAL_PIXELS*PIXEL_NUM_OF_BYTES; // S2MM Horizontal Size Register(S2MM_HSIZE)0xc80 = 3200dec = 800 x 4
    caminf_axi_vdma_0[42] = SVGA_HORIZONTAL_PIXELS*PIXEL_NUM_OF_BYTES; // S2MM Frame Delay and Stride Register(S2MM_FRMDLY_STRIDE)0xc80 = 3200dec = 800 x 4
    caminf_axi_vdma_0[43] = (unsigned)phys_addr; // S2MM Start Address (1 to 16) Start Address 1
    caminf_axi_vdma_0[44] = (unsigned)phys_addr+SVGA_ALL_DISP_ADDRESS; // S2MM Start Address (1 to 16) Start Address 2
    caminf_axi_vdma_0[45] = (unsigned)phys_addr+2*SVGA_ALL_DISP_ADDRESS; // S2MM Start Address (1 to 16) Start Address 3
    caminf_axi_vdma_0[12] = 0x00010003// S2MM_VDMACR(IRQFrameCount = 0x1, Circular_Park = 1, Run/stop = 1)
    while((caminf_axi_vdma_0[13] & 0x1) == 0x1) ; // Halt? (S2MM_VDMASR 0x34)
    caminf_axi_vdma_0[40] = SVGA_VERTICAL_LINES; // S2MM Vertical Size (S2MM_VSIZE  Offset 0xA0) 0x258 = 600dec

    // AXI VDMA Initialization sequence (dviin_axi_vdma_0)
    dviin_axi_vdma_0[12] = 0x4// S2MM_VDMACR (S2MM VDMA Control Register  Offset 30h) is 0x4 
    while ((dviin_axi_vdma_0[12] & 0x4) == 0x4) ; // Reset is progress
    dviin_axi_vdma_0[12] = 0x4// S2MM_VDMACR (S2MM VDMA Control Register  Offset 30h) is 0x4 
    while ((dviin_axi_vdma_0[12] & 0x4) == 0x4) ; // Reset is progress
    dviin_axi_vdma_0[18] = NUMBER_OF_WRITE_FRAMES; // S2MM_FRMSTORE (0x48) register
    dviin_axi_vdma_0[12] = 0x00010002// S2MM_VDMACR(IRQFrameCount = 0x1, Circular_Park = 1)
    dviin_axi_vdma_0[41] = XGA_HORIZONTAL_PIXELS*PIXEL_NUM_OF_BYTES; // S2MM Horizontal Size Register(S2MM_HSIZE)0xc80 = 3200dec = 800 x 4
    dviin_axi_vdma_0[42] = XGA_HORIZONTAL_PIXELS*PIXEL_NUM_OF_BYTES; // S2MM Frame Delay and Stride Register(S2MM_FRMDLY_STRIDE)0xc80 = 3200dec = 800 x 4
    dviin_axi_vdma_0[43] = (unsigned)phys_addr+3*SVGA_ALL_DISP_ADDRESS; // S2MM Start Address (1 to 16) Start Address 1
    dviin_axi_vdma_0[44] = (unsigned)phys_addr+3*SVGA_ALL_DISP_ADDRESS+XGA_ALL_DISP_ADDRESS; // S2MM Start Address (1 to 16) Start Address 2
    dviin_axi_vdma_0[45] = (unsigned)phys_addr+3*SVGA_ALL_DISP_ADDRESS+2*XGA_ALL_DISP_ADDRESS; // S2MM Start Address (1 to 16) Start Address 3
    dviin_axi_vdma_0[12] = 0x00010003// S2MM_VDMACR(IRQFrameCount = 0x1, Circular_Park = 1, Run/stop = 1)
    while((dviin_axi_vdma_0[13] & 0x1) == 0x1) ; // Halt? (S2MM_VDMASR 0x34)
    dviin_axi_vdma_0[40] = XGA_VERTICAL_LINES; // S2MM Vertical Size (S2MM_VSIZE  Offset 0xA0) 0x258 = 600dec

    // CMOS Camera initialize, MT9D111
    cam_i2c_init(caminf_axi_iic);
    
    cam_i2c_write(caminf_axi_iic, 0xba, 0xf00x1);        // Changed regster map to IFP page 1
    cam_i2c_write(caminf_axi_iic, 0xba, 0x970x20);    // RGB Mode, RGB565

    caminf_mt9d111_inf_axis_0[1] = 0;
    
    // Camera Base Address Setting
    caminf_mt9d111_inf_axis_0[0] = (unsigned)phys_addr+3*SVGA_ALL_DISP_ADDRESS;; // Camera Interface start (Address is dummy)

    // bitmap display controller settings
    bmdc_axi_lites0[0] = (unsigned)phys_addr; // Bitmap Display Controller 0 start, My Camera Image
    c = getc(stdin);
    while(c != 'q'){
        switch ((char)c) {
            case '1' :
                bmdc_axi_lites0[0] = (unsigned)phys_addr; // Bitmap Display Controller 0 start, My Camera Image
                break;
            case '2' :
                bmdc_axi_lites0[0] = (unsigned)phys_addr+3*SVGA_ALL_DISP_ADDRESS+0x8// Another one ZYBO Camera Image
                break;
            case 's' :
                if (caminf_mt9d111_inf_axis_0[1] == 0)
                    caminf_mt9d111_inf_axis_0[1] = 1// left camera is stopped.
                else
                    caminf_mt9d111_inf_axis_0[1] = 0// left camere is started. 
                break;
            case 'l' : // laplacian filter
                // caminf_axis_switch_1, 1to2 ,Select M01_AXIS
                // Refer to http://marsee101.blog19.fc2.com/blog-entry-3177.html
                caminf_axis_switch_1[16] = 0x80000000// 0x40 = 0x80000000; disable
                caminf_axis_switch_1[17] = 0// 0x44 = 0;
                caminf_axis_switch_1[0] = 0x2// 0x0 = 2; Commit registers
    
                // laplacian filter AXIS Start
                laps_cntrl = caminf_lap_filter_axis_0[0] & 0x80// Auto Restart bit
                caminf_lap_filter_axis_0[0] = laps_cntrl | 0x01// Start bit set
                caminf_lap_filter_axis_0[0] = 0x80// Auto Restart bit set
    
                // caminf_axis_switch_0, 2to1, Select S01_AXIS
                // Refer to http://marsee101.blog19.fc2.com/blog-entry-3177.html
                caminf_axis_switch_0[16] = 0x1// 0x40 = 0x1;
                caminf_axis_switch_0[0] = 0x2// 0x0 = 2; Commit registers
                break;
            case 'c' : // camera image
                // caminf_axis_switch_1, 1to2 ,Select M01_AXIS
                // Refer to http://marsee101.blog19.fc2.com/blog-entry-3177.html
                caminf_axis_switch_1[16] = 0// 0x44 = 0;
                caminf_axis_switch_1[17] = 0x80000000// 0x40 = 0x80000000; disable
                caminf_axis_switch_1[0] = 0x2// 0x0 = 2; Commit registers
    
                // laplacian filter AXIS Start
                caminf_lap_filter_axis_0[0] = 0x00// Auto Restart Disable
    
                // caminf_axis_switch_0, 2to1, Select S01_AXIS
                // Refer to http://marsee101.blog19.fc2.com/blog-entry-3177.html
                caminf_axis_switch_0[16] = 0x0// 0x40 = 0x0;
                caminf_axis_switch_0[0] = 0x2// 0x0 = 2; Commit registers
                break;
        }
        c = getc(stdin);
    }

    munmap((void *)bmdc_axi_lites0, 0x10000);
    munmap((void *)caminf_axi_vdma_0, 0x10000);
    munmap((void *)dviin_axi_vdma_0, 0x10000);
    munmap((void *)caminf_axi_iic, 0x10000);
    munmap((void *)caminf_mt9d111_inf_axis_0, 0x10000);
    munmap((void *)caminf_axis_switch_0, 0x10000);
    munmap((void *)caminf_axis_switch_1, 0x10000);
    munmap((void *)caminf_lap_filter_axis_0, 0x10000);
    munmap((void *)frame_buffer, (XGA_ALL_DISP_ADDRESS*NUMBER_OF_WRITE_FRAMES)+(SVGA_ALL_DISP_ADDRESS*NUMBER_OF_WRITE_FRAMES));
    
    close(fd0);
    close(fd1);
    close(fd2);
    close(fd3);
    close(fd4);
    close(fd5);
    close(fd6);
    close(fd7);
    close(fd9);
    close(fd10);
    
    return(0);
}

  1. 2016年02月17日 06:40 |
  2. ステレオカメラによる画像解析
  3. | トラックバック:0
  4. | コメント:0
»