FC2カウンター FPGAの部屋 手書き数字認識用畳み込みニューラルネットワーク回路の製作6(PYNQボードで動作確認)

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

FPGAの部屋

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

手書き数字認識用畳み込みニューラルネットワーク回路の製作6(PYNQボードで動作確認)

手書き数字認識用畳み込みニューラルネットワーク回路の製作5(SDK)”の続き。

前回は、カメラで撮影した画像の内の四角枠の領域の手書き数字を認識するPYNQボード用のVivado 2017.2プロジェクトで、SDKを使用してカメラ画像を表示した。今回は、mnist_conv_nn_test.c を作成して、手書き数字を認識してみよう。

まずは、SDK でベアメタル・アプリケーションソフトの mnist_conv_nn_test.c を作成した。
hand_draw_num_89_170706.png

PYNQボードにMT9D111カメラモジュールを搭載したシステムで、手書きの数字を撮影した。
hand_draw_num_78_170705.jpg

画像にピンクの四角枠が表示され、PYNQボードのシリアルインターフェイスに接続されたTera Term の k キーを押すと1ピクセル左に、j キーを押すと1ピクセル下に、k キーを押すと1ピクセル上に、l キーを押すと1ピクセル右に移動する。 r キーを押すと、0 ~ 10 までの値が表示され、最大値 (max_id) の値が表示される。 q キーでexit する。
更に、GPIOに接続されたLEDが、最大値 (max_id) の値を2進数で表示する。

さて、キー操作で、ピンクの四角枠を移動して、数字を指定する。
まずは、1 を認識してみよう。
hand_draw_num_79_170705.jpg

パソコンで立ち上げたTera Term の画面を示す。result[0] ~ result[9] はそれぞれの値の手書き数字の値を示す。その最大値 (max_id) は 1 なので、1 と判定された。なお、max_id はint で 2 の補数表示となっている。
hand_draw_num_68_170705.png

つぎは、2 を認識してみよう。
hand_draw_num_80_170705.jpg

パソコンで立ち上げたTera Term の画面を示す。 2 と認識されている。
hand_draw_num_69_170705.png

3 を認識してみよう。
hand_draw_num_81_170705.jpg

パソコンで立ち上げたTera Term の画面を示す。 3 と認識されている。
hand_draw_num_70_170705.png

4 を認識してみよう。
hand_draw_num_82_170705.jpg

パソコンで立ち上げたTera Term の画面を示す。 4 と認識されている。
hand_draw_num_71_170705.png

5 を認識してみよう。
hand_draw_num_83_170705.jpg

パソコンで立ち上げたTera Term の画面を示す。 5 と認識されている。
hand_draw_num_72_170705.png

6 を認識してみよう。
hand_draw_num_84_170705.jpg

パソコンで立ち上げたTera Term の画面を示す。 6 と認識されている。
hand_draw_num_73_170705.png

7 を認識してみよう。
hand_draw_num_85_170705.jpg

パソコンで立ち上げたTera Term の画面を示す。 7 と認識されている。
hand_draw_num_74_170705.png

8 を認識してみよう。
hand_draw_num_86_170705.jpg

パソコンで立ち上げたTera Term の画面を示す。 8 と認識されている。
hand_draw_num_75_170705.png

9 を認識してみよう。
hand_draw_num_87_170705.jpg

パソコンで立ち上げたTera Term の画面を示す。 9 と認識されている。
hand_draw_num_76_170705.png

0 を認識してみよう。
hand_draw_num_88_170705.jpg

パソコンで立ち上げたTera Term の画面を示す。 0 と認識されている。
hand_draw_num_77_170705.png

今のところ精度は 100 % だった。うまく行ったようだ。
手書き数字を認識する領域であるピンクの四角枠をキーで移動できるので、カメラの位置を細かく合わせる必要がなく、とってもやりやすい。

最後に mnist_conv_nn_test.c を貼っておく。

/* * mnist_conv_nn_test.c * *  Created on: 2017/07/03 *      Author: Masaaki */

#include <stdio.h>
#include <stdlib.h>
#include "xaxivdma.h"
#include "xil_io.h"
#include "xparameters.h"
#include "sleep.h"
#include "xgpio.h"

#include "xmnist_conv_nn.h"
#include "xsquare_frame_gen.h"

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

#define HORIZONTAL_PIXELS    800
#define VERTICAL_LINES        600
#define PIXEL_NUM_OF_BYTES    4

int max_int(int out[10]);

static XAxiVdma_DmaSetup Vdma0_WriteCfg;

void cam_i2c_init(volatile unsigned *mt9d111_i2c_axi_lites) {
    mt9d111_i2c_axi_lites[64] = 0x2// reset tx fifo ,address is 0x100, i2c_control_reg
    mt9d111_i2c_axi_lites[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 *mt9d111_i2c_axi_lites, unsigned int device_addr, unsigned int write_addr, unsigned int write_data){
    mt9d111_i2c_axi_lites[66] = 0x100 | (device_addr & 0xfe);   // Slave IIC Write Address, address is 0x108, i2c_tx_fifo
    mt9d111_i2c_axi_lites[66] = write_addr;
    mt9d111_i2c_axi_lites[66] = (write_data >> 8)|0xff;         // first data
    mt9d111_i2c_axi_lites[66] = 0x200 | (write_data & 0xff);        // second data
    cam_i2x_write_sync();
}

int main(){
    XMnist_conv_nn mcnn;
    XSquare_frame_gen sf_gen;
    int inbyte_in;
    int xval, yval;
    int i, res;
    int result[10];
    static XGpio GPIOInstance_Ptr;
    int XGpio_Status;
    int max_id;
    XAxiVdma_Config *XAxiVdma0_Config;
    XAxiVdma XAxiVdma0;
    int XAxiVdma0_Status;
    int result_disp = 0;

    // AXI VDMA Initialization sequence
    XAxiVdma0_Config = XAxiVdma_LookupConfig(XPAR_CAMERA_INTERFACE_AXI_VDMA_0_DEVICE_ID); // Look up the hardware configuration for a device instance
    if (XAxiVdma0_Config == NULL){
        fprintf(stderr, "No AXI VDMA found\n");
        return(-1);
    }

    XAxiVdma0_Status = XAxiVdma_CfgInitialize(&XAxiVdma0, XAxiVdma0_Config, XAxiVdma0_Config->BaseAddress); // Initialize the driver with hardware configuration
    if (XAxiVdma0_Status != XST_SUCCESS){
        fprintf(stderr, "XAxiVdma_CfgInitialize() failed\n");
        return(-1);
    }

    XAxiVdma_Reset(&XAxiVdma0, XAXIVDMA_WRITE);
    while(XAxiVdma_ResetNotDone(&XAxiVdma0, XAXIVDMA_WRITE)) ;

    XAxiVdma0_Status = XAxiVdma_SetFrmStore(&XAxiVdma0, NUMBER_OF_WRITE_FRAMES, XAXIVDMA_WRITE); // Set the number of frame store buffers to use.

    Vdma0_WriteCfg.VertSizeInput = VERTICAL_LINES;
    Vdma0_WriteCfg.HoriSizeInput = HORIZONTAL_PIXELS * PIXEL_NUM_OF_BYTES;
    Vdma0_WriteCfg.Stride = HORIZONTAL_PIXELS * PIXEL_NUM_OF_BYTES; // Indicates the number of address bytes between the first pixels of each video line.
    Vdma0_WriteCfg.FrameDelay = 0// Indicates the minimum number of frame buffers the Genlock slave is to be behind the locked master. This field is only used if the channel is enabled for Genlock Slave operations. This field has no meaning in other Genlock modes.
    Vdma0_WriteCfg.EnableCircularBuf = 1// Indicates frame buffer Circular mode or frame buffer Park mode.  1 = Circular Mode Engine continuously circles through frame buffers.
    Vdma0_WriteCfg.EnableSync = 0// Enables Genlock or Dynamic Genlock Synchronization. 0 = Genlock or Dynamic Genlock Synchronization disabled.
    Vdma0_WriteCfg.PointNum = 0// No Gen-Lock
    Vdma0_WriteCfg.EnableFrameCounter = 0// Endless transfers
    Vdma0_WriteCfg.FixedFrameStoreAddr = 0// We are not doing parking

    XAxiVdma0_Status = XAxiVdma_DmaConfig(&XAxiVdma0, XAXIVDMA_WRITE, &Vdma0_WriteCfg);
    if (XAxiVdma0_Status != XST_SUCCESS){
        fprintf(stderr, "XAxiVdma_DmaConfig() failed\n");
        return(-1);
    }

    // Frame buffer address set
    unsigned int frame_addr = (unsigned int)FRAME_BUFFER_ADDRESS;
    for (i=0; i<NUMBER_OF_WRITE_FRAMES; i++){
        Vdma0_WriteCfg.FrameStoreStartAddr[i] = frame_addr;
        //frame_addr += HORIZONTAL_PIXELS * PIXEL_NUM_OF_BYTES * VERTICAL_LINES;
    }

    XAxiVdma0_Status = XAxiVdma_DmaSetBufferAddr(&XAxiVdma0, XAXIVDMA_WRITE, Vdma0_WriteCfg.FrameStoreStartAddr);
    if (XAxiVdma0_Status != XST_SUCCESS){
        fprintf(stderr, "XAxiVdma_DmaSetBufferAddr() failed\n");
        return(-1);
    }

    // Mnist_conv_nn, Square_frame_gen Initialize
    XMnist_conv_nn_Initialize(&mcnn, 0);
    XSquare_frame_gen_Initialize(&sf_gen, 0);

    // square_frame_gen initialize
    XSquare_frame_gen_Set_x_pos(&sf_gen, HORIZONTAL_PIXELS/2);
    xval = HORIZONTAL_PIXELS/2;
    XSquare_frame_gen_Set_y_pos(&sf_gen, VERTICAL_LINES/2);
    yval = VERTICAL_LINES/2;
    XSquare_frame_gen_Set_width(&sf_gen, 28);
    XSquare_frame_gen_Set_height(&sf_gen, 28);
    XSquare_frame_gen_Set_off_on(&sf_gen, 1); // on

    // XSquare_frame_gen start
    XSquare_frame_gen_DisableAutoRestart(&sf_gen);
    while(!XSquare_frame_gen_IsIdle(&sf_gen)) ;
    XSquare_frame_gen_Start(&sf_gen);
    XSquare_frame_gen_EnableAutoRestart(&sf_gen);

    // mnist_conv_nn initialize
    XMnist_conv_nn_Set_addr_offset(&mcnn, HORIZONTAL_PIXELS/2);
    XMnist_conv_nn_Set_in_r(&mcnn, FRAME_BUFFER_ADDRESS+HORIZONTAL_PIXELS*(VERTICAL_LINES/2)*sizeof(int));

    // axis_switch_1, 1to2 ,Select M00_AXIS
    // Refer to http://marsee101.blog19.fc2.com/blog-entry-3177.html
    Xil_Out32((XPAR_CAMERA_INTERFACE_AXIS_SWITCH_1_BASEADDR+0x40), 0x80000000); // disable
    Xil_Out32((XPAR_CAMERA_INTERFACE_AXIS_SWITCH_1_BASEADDR+0x44), 0x0); // square_frame_gen enable
    Xil_Out32((XPAR_CAMERA_INTERFACE_AXIS_SWITCH_1_BASEADDR), 0x2); // Commit registers

    // axis_switch_0, 2to1, Select S00_AXIS
    // Refer to http://marsee101.blog19.fc2.com/blog-entry-3177.html
    Xil_Out32((XPAR_CAMERA_INTERFACE_AXIS_SWITCH_0_BASEADDR+0x40), 0x1);
    Xil_Out32((XPAR_CAMERA_INTERFACE_AXIS_SWITCH_0_BASEADDR), 0x2); // Commit registers

    // VDMA start
    XAxiVdma0_Status = XAxiVdma_DmaStart(&XAxiVdma0, XAXIVDMA_WRITE);
    if (XAxiVdma0_Status != XST_SUCCESS){
        fprintf(stderr, "XAxiVdma_DmaStart() failed\n");
        return(-1);
    }

    // mt9d111_inf_axis_0, axi_iic_0, bitmap_disp_cntrler_axi_master_0
    volatile unsigned int *bmdc_axi_lites;
    volatile unsigned int *mt9d111_axi_lites;
    volatile unsigned int *mt9d111_i2c_axi_lites;

    bmdc_axi_lites = (volatile unsigned *)XPAR_BITMAP_DISP_CNTRLER_AXI_MASTER_0_BASEADDR;
    mt9d111_axi_lites = (volatile unsigned *)XPAR_CAMERA_INTERFACE_MT9D111_INF_AXIS_0_BASEADDR;
    mt9d111_i2c_axi_lites = (volatile unsigned *)XPAR_CAMERA_INTERFACE_AXI_IIC_0_BASEADDR;

    bmdc_axi_lites[0] = (volatile unsigned int)FRAME_BUFFER_ADDRESS; // Bitmap Display Controller start
    mt9d111_axi_lites[0] = (volatile unsigned int)FRAME_BUFFER_ADDRESS; // Camera Interface start (Address is dummy)

    // CMOS Camera initialize, MT9D111
    cam_i2c_init(mt9d111_i2c_axi_lites);

    cam_i2c_write(mt9d111_i2c_axi_lites, 0xba, 0xf00x1);      // Changed regster map to IFP page 1
    cam_i2c_write(mt9d111_i2c_axi_lites, 0xba, 0x970x20);        // RGB Mode, RGB565

    mt9d111_axi_lites[1] = 0// One_shot_mode is disabled

    // XMnist_conv_nn start
    XMnist_conv_nn_DisableAutoRestart(&mcnn);
    while(!XMnist_conv_nn_IsIdle(&mcnn));
    XMnist_conv_nn_Start(&mcnn);
    XMnist_conv_nn_EnableAutoRestart(&mcnn);

    // AXI GPIO Initialization
    XGpio_Status = XGpio_Initialize(&GPIOInstance_Ptr,XPAR_AXI_GPIO_0_DEVICE_ID);
    if(XST_SUCCESS != XGpio_Status)
        print("GPIO INIT FAILED\n\r");
    // AXI GPIO Set the Direction(output setting)
    XGpio_SetDataDirection(&GPIOInstance_Ptr, 10);

    while(1){
        printf("mnist_conv_nn_test, <h> : left, <k> : up, <j> : down, <l> : right, <q> : exit\n");
        inbyte_in = inbyte();
        switch(inbyte_in) {
            case 'h' : // left
                if(xval > 0){
                    --xval;
                    XSquare_frame_gen_Set_x_pos(&sf_gen, xval);
                    XMnist_conv_nn_Set_addr_offset(&mcnn, xval);
                    printf("X_POS = %d, Y_POS = %d\n", xval, yval);
                }
                break;
            case 'l' : // right
                if(xval < HORIZONTAL_PIXELS-28){
                    xval++;
                    XSquare_frame_gen_Set_x_pos(&sf_gen, xval);
                    XMnist_conv_nn_Set_addr_offset(&mcnn, xval);
                    printf("X_POS = %d, Y_POS = %d\n", xval, yval);
                }
                break;
            case 'k' : // up
                if(yval > 0){
                    --yval;
                    XSquare_frame_gen_Set_y_pos(&sf_gen, yval);
                    XMnist_conv_nn_Set_in_r(&mcnn, FRAME_BUFFER_ADDRESS+HORIZONTAL_PIXELS*yval*sizeof(int));
                    printf("X_POS = %d, Y_POS = %d\n", xval, yval);
                }
                break;
            case 'j' : // down
                if(yval < VERTICAL_LINES-28){
                    yval++;
                    XSquare_frame_gen_Set_y_pos(&sf_gen, yval);
                    XMnist_conv_nn_Set_in_r(&mcnn, FRAME_BUFFER_ADDRESS+HORIZONTAL_PIXELS*yval*sizeof(int));
                    printf("X_POS = %d, Y_POS = %d\n", xval, yval);
                }
                break;
            case 'r' : // result check
                result_disp = 1;
                break;
            case 'q' : // exit
                return(0);
        }

        // mnist cnn result check
        for(i=0; i<5; i++){
            XMnist_conv_nn_Read_out_V_Words(&mcnn, i, &res, 1);
            result[i*2] = res & 0x0fff;
            if(result[i*2] & 0x800// minus
                result[i*2] = 0xfffff000 | result[i*2]; // Sign extension

            result[i*2+1] = (res & 0x0fff0000) >> 16;
            if(result[i*2+1] & 0x800// minus
                result[i*2+1] = 0xfffff000 | result[i*2+1]; // Sign extension
        }

        max_id = max_int(result);
        XGpio_DiscreteWrite(&GPIOInstance_Ptr, 1, max_id);

        if(result_disp){
            for(i=0; i<10; i++){
                printf("result[%d] = %x\n", i, result[i]);
            }
            printf("max_id = %d\n", max_id);
            result_disp = 0;
        }
    }
}

int max_int(int out[10]){
    int max_id;
    int max, i;

    for(i=0; i<10; i++){
        if(i == 0){
            max = out[0];
            max_id = 0;
        }else if(out[i]>max){
            max = out[i];
            max_id = i;
        }
    }
    return(max_id);
}

  1. 2017年07月06日 05:03 |
  2. DNN
  3. | トラックバック:0
  4. | コメント:0

コメント

コメントの投稿


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

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