FC2カウンター FPGAの部屋 カメラ画像をDisplayPortに出力する2(vflip_dma_write2 の1)
FC2ブログ

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

FPGAの部屋

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

カメラ画像をDisplayPortに出力する2(vflip_dma_write2 の1)

カメラ画像をDisplayPortに出力する1(構想編)”の続き。

前回は、カメラ画像をUltra96 のDisplayPort へ出力するための回路の構想を練った。今回は、その内のvflip_dma_write2 について書いてみよう。
以前作成した vflip_dma_write はSVGA 解像度の垂直のみが反転したカメラ画像を正常に表示できるようにDMA する回路だった。今回のvflip_dma_write2 はXGA解像度やHD解像度の真ん中にSVGA のカメラ画像を表示するDMA である。

vflip_dma_write2.h を示す。

// vflip_dma_write2.h
// 2019/01/28 by marsee
//

#ifndef __VFLIP_DMA_WRITE2_H__
#define __VFLIP_DMA_WRITE2_H__

#include "ap_axi_sdata.h"
#include "hls_video.h"

#define SVGA_WIDTH 800
#define SVGA_HEIGHT 600
#define SVGA_START_OFFSET 0
#define XGA_WIDTH 1024
#define XGA_HEIGHT 768
#define XGA_START_OFFSET (((XGA_HEIGHT-SVGA_HEIGHT)/2)*XGA_WIDTH+(XGA_WIDTH-SVGA_WIDTH)/2)
#define XGA_STRIDE (XGA_WIDHT-SVGA_WIDTH)
#define HD_WIDTH 1920
#define HD_HEIGHT 1080
#define HD_START_OFFSET (((HD_HEIGHT-SVGA_HEIGHT)/2)*HD_WIDTH+(HD_WIDTH-SVGA_WIDTH)/2)
#define HD_STRIDE (HD_WIDHT-SVGA_WIDTH)

#define RESO_SVGA 0
#define RESO_XGA 1
#define RESO_HD  2

typedef hls::stream<ap_axiu<32,1,1,1> > AXI_STREAM;
typedef ap_axiu<32,1,1,1> AP_AXIU32;
typedef hls::Scalar<3, unsigned char> RGB_PIXEL;
typedef hls::Mat<HD_HEIGHT, HD_WIDTH, HLS_8UC3> RGB_IMAGE;
typedef hls::Mat<HD_HEIGHT, HD_WIDTH, HLS_8UC1> GRAY_IMAGE;

#endif


vflip_dma_write2.cpp を示す。

// vflip_dma_write2.cpp
// 2019/01/28 by marsee
//

#include <ap_int.h>
#include <hls_stream.h>
#include <ap_axi_sdata.h>

#include "vflip_dma_write2.h"

int vflip_dma_write2(AXI_STREAM & ins,
    volatile ap_int<32> *fb0, volatile ap_int<32> *fb1, volatile ap_int<32> *fb2,
    ap_uint<32> &resolution, volatile ap_uint<2> &active_frame){
#pragma HLS INTERFACE s_axilite port=resolution
#pragma HLS INTERFACE ap_none port=active_frame
#pragma HLS INTERFACE m_axi depth=480000 port=fb0 offset=slave
#pragma HLS INTERFACE m_axi depth=480000 port=fb1 offset=slave
#pragma HLS INTERFACE m_axi depth=480000 port=fb2 offset=slave
#pragma HLS INTERFACE axis register both port=ins
#pragma HLS INTERFACE s_axilite port=return

    AP_AXIU32 pix;
    int max_fb_chk;
    int max_width, max_height;
    int start_offset;
    int stride;

    active_frame = 0;
    switch((int)resolution){
        case 0: // SVGA
            start_offset = SVGA_START_OFFSET;
            max_width = SVGA_WIDTH;
            max_height = SVGA_HEIGHT;
            break;
        case 1: // XGA
            start_offset = XGA_START_OFFSET;
            max_width = XGA_WIDTH;
            max_height = XGA_HEIGHT;
            break;
        default: // HD
            start_offset = HD_START_OFFSET;
            max_width = HD_WIDTH;
            max_height = HD_HEIGHT;
            break;
    }

    LOOP_WAIT0: do { // user が 1になった時にフレームがスタートする
#pragma HLS LOOP_TRIPCOUNT min=1 max=1 avg=1
        ins >> pix;
    } while(pix.user == 0);

    LOOP_Y0: for (int y=SVGA_HEIGHT-1; y>=0; y--){ // vflip
        LOOP_X0: for (int x=0; x<SVGA_WIDTH; x++){
#pragma HLS PIPELINE II=1
            if (!(x==0 && y==0))    // 最初の入力はすでに入力されている
                ins >> pix;    // AXI4-Stream からの入力

            fb0[start_offset + (y*max_width)+x] = pix.data;
        }
    }

    active_frame = 1;
    LOOP_WAIT1: do { // user が 1になった時にフレームがスタートする
#pragma HLS LOOP_TRIPCOUNT min=1 max=1 avg=1
        ins >> pix;
    } while(pix.user == 0);

    LOOP_Y1: for (int y=SVGA_HEIGHT-1; y>=0; y--){ // vflip
        LOOP_X1: for (int x=0; x<SVGA_WIDTH; x++){
#pragma HLS PIPELINE II=1
            if (!(x==0 && y==0))    // 最初の入力はすでに入力されている
                ins >> pix;    // AXI4-Stream からの入力

            fb1[start_offset + (y*max_width)+x] = pix.data;
        }
    }

    active_frame = 2;
    LOOP_WAIT2: do { // user が 1になった時にフレームがスタートする
#pragma HLS LOOP_TRIPCOUNT min=1 max=1 avg=1
        ins >> pix;
    } while(pix.user == 0);

    LOOP_Y2: for (int y=SVGA_HEIGHT-1; y>=0; y--){ // vflip
        LOOP_X2: for (int x=0; x<SVGA_WIDTH; x++){
#pragma HLS PIPELINE II=1
            if (!(x==0 && y==0))    // 最初の入力はすでに入力されている
                ins >> pix;    // AXI4-Stream からの入力

            fb2[start_offset + (y*max_width)+x] = pix.data;
        }
    }
end:
    return(0);
}


vflip_dma_write2_tb.cpp を示す。

// vflip_dma_write2_tb.cpp
// 2019/01/28 by marsee
//

#include <ap_int.h>
#include <hls_stream.h>
#include <iostream>
#include <fstream>
#include "hls_opencv.h"

#include "vflip_dma_write2.h"

int vflip_dma_write2(AXI_STREAM & ins,
    volatile ap_int<32> *fb0, volatile ap_int<32> *fb1, volatile ap_int<32> *fb2,
    ap_uint<32> &resolution, volatile ap_uint<2> &active_frame);
    
int vflip_dmaw(AXI_STREAM &ins, int width, int height, ap_uint<32> resolution,
    char *output_file0, char *output_file1, char *output_file2);

#define NUM_FRAME_BUFFER 3

int main()
{
    using namespace cv;

    AXI_STREAM ins0, ins1;
    AP_AXIU32 pix;
    ap_uint<32> resolution;

    // OpenCV で 画像を読み込む
    Mat src = imread("bmp_file0_vflip.bmp");
    AXI_STREAM src_axi, dst_axi;

    // Mat フォーマットから AXI4 Stream へ変換、3画面分
    for(int i=0; i<NUM_FRAME_BUFFER; i++){
        cvMat2AXIvideo(src, src_axi);
        for (int y=0; y<SVGA_HEIGHT; y++){
            for (int x=0; x<SVGA_WIDTH; x++){
                src_axi >> pix;
                ins0 << pix;
                ins1 << pix;
            }
        }
    }
    char output_file0[] = "xga_result0.bmp";
    char output_file1[] = "xga_result1.bmp";
    char output_file2[] = "xga_result2.bmp";
    resolution = RESO_XGA;
    vflip_dmaw(ins0, XGA_WIDTH, XGA_HEIGHT, resolution, output_file0, output_file1, output_file2);
    
    char output_file3[] = "hd_result0.bmp";
    char output_file4[] = "hd_result1.bmp";
    char output_file5[] = "hd_result2.bmp";
    resolution = RESO_HD;
    vflip_dmaw(ins1, HD_WIDTH, HD_HEIGHT, resolution, output_file3, output_file4, output_file5);

    return(0);
}

int vflip_dmaw(AXI_STREAM &ins, int width, int height, ap_uint<32> resolution,
    char *output_file0, char *output_file1, char *output_file2){
    
    using namespace cv;

    ap_uint<2> active_frame;
    ap_uint<32> *frame_buffer;

    // frame buffer をアロケートする、3倍の領域を取ってそれを3つに分ける
    if ((frame_buffer =(ap_uint<32> *)malloc(NUM_FRAME_BUFFER * sizeof(ap_int<32>) * (width * height))) == NULL){
        fprintf(stderr, "Can't allocate frame_buffer0 ~ 2\n");
        exit(1);
    }
    for(int i=0; i<NUM_FRAME_BUFFER; i++){ // 0 clear
        for (int y=0; y<height; y++){
            for (int x=0; x<width; x++){
                frame_buffer[i*(width * height)+y*width+x] = 0;
            }
        }
    }

    vflip_dma_write2(ins, (volatile ap_int<32> *)frame_buffer,
        (volatile ap_int<32> *)&frame_buffer[width * height],
        (volatile ap_int<32> *)&frame_buffer[2 * (width * height)],
        resolution, active_frame);

    // AXI4 Stream から Mat フォーマットへ変換
    // dst は宣言時にサイズとカラー・フォーマットを定義する必要がある
    Mat dst[3];
    for(int i=0; i<NUM_FRAME_BUFFER; i++){
        dst[i] = Mat(height, width, CV_8UC3);
    }

    // dst[i] にframe_bufferから画像データをロード
    for(int i=0; i<NUM_FRAME_BUFFER; i++){
        Mat_<Vec3b> dst_vec3b = Mat_<Vec3b>(dst[i]);
        for(int y=0; y<dst[i].rows; y++){
            for(int x=0; x<dst[i].cols; x++){
                Vec3b pixel;
                int rgb = frame_buffer[i*(width * height)+y*width+x];
                pixel[0] = (rgb & 0xff); // blue
                pixel[1] = (rgb & 0xff00) >> 8; // green
                pixel[2] = (rgb & 0xff0000) >> 16; // red
                dst_vec3b(y,x) = pixel;
            }
        }
    }

    // DMAされたデータをBMPフィルに書き込む
    char output_file[100];
    for (int i=0; i<NUM_FRAME_BUFFER; i++){
        switch (i){
            case 0:
                strcpy(output_file, output_file0);
                break;
            case 1:
                strcpy(output_file, output_file1);
                break;
            case 2:
                strcpy(output_file, output_file2);
                break;
        }
        // Mat フォーマットからファイルに書き込み
        imwrite(output_file, dst[i]);
    }

    free(frame_buffer);
    
    return(0);
}


Vivado HLS 2018.3 で vflip_dma_write2 プロジェクトを作成した。
cam_displayport_2_190130.png
  1. 2019年01月30日 03:42 |
  2. Ultra96
  3. | トラックバック:0
  4. | コメント:0

コメント

コメントの投稿


管理者にだけ表示を許可する

トラックバック URL
http://marsee101.blog.fc2.com/tb.php/4426-a1a6e61b
この記事にトラックバックする(FC2ブログユーザー)