请选择 进入手机版 | 继续访问电脑版
[X]关闭
1

S04-CH10 自适应二值化MT9V034摄像头硬件实现

摘要: 本章结为大家介绍了使用二值化IP来处理MT9V034采集的图像,这为大家使用HLS进行摄像头算法的移植提供了一个思路,能节省大量的设计时间,十分的方便。

软件版本:VIVADO2017.4

操作系统:WIN10 64bit

硬件平台:适用米联客 ZYNQ系列开发板

米联客(MSXBO)论坛:www.osrc.cn答疑解惑专栏开通,欢迎大家给我提问!!

10.1 概述

      本章结为大家介绍了使用二值化IP来处理MT9V034采集的图像,这为大家使用HLS进行摄像头算法的移植提供了一个思路,能节省大量的设计时间,十分的方便。

10.2 MT9V034驱动修改

      MT9V034是一款直接输出灰度的相机,避免了RGB到灰度的转换操作,在前面的两个章节中,由于我们没有做任何的图像处理操作,只是为了显示,因此我们直接在IP中将灰度信号进行了拼接,以便于后级的显示操作。但在实际的项目工程当中,灰度信号要进行一定的处理,可能并不需要进行显示,为了更好的贴近项目应用,此处我们直接采用MT9V034的8bit灰度输出,对摄像头的驱动做了一些相应的修改,修改后的源代码如下表所示:

 

      驱动程序较之前删除了拼接部分,其余部分完全相同,修改完成之后,重新将其封装成一个IP,以便在VIVADO中调用,封装之后的IP如下图所示:

module MT_Sensor_ML(

    input CLK_i,

//---------------------------- CMOS sensor hardware interface --------------------------/

input cmos_vsync_i, //cmos vsync

input cmos_href_i, //cmos hsync refrence

input cmos_pclk_i, //cmos pxiel clock

output cmos_xclk_o, //cmos externl clock

input[7:0] cmos_data_i, //cmos data

output hs_o,//hs signal.

    output vs_o,//vs signal.

   // output de_o,//data enable.

    output [7:0] rgb_o,//data output,

    output vid_clk_ce

);

//----------------------视频输出解码模块---------------------------//

//wire  [7:0]rgb_o_r;

//assign rgb_o = {rgb_o_r[4:0]   ,3'd0 ,rgb_o_r[10:5]     ,2'd0,rgb_o_r[15:11],3'd0};

//assign rgb_o = {rgb_o_r,rgb_o_r,rgb_o_r};

reg [7:0]cmos_data_r;

reg cmos_href_r;

reg cmos_vsync_r;

 

always@(posedge cmos_pclk_i)

begin

   cmos_data_r <= cmos_data_i;

   cmos_href_r <= cmos_href_i;

   cmos_vsync_r<= cmos_vsync_i;

end

//assign rgb_o = 24'b11111111_00000000_11111111;

cmos_decode cmos_decode_u0(

//system signal.

.cmos_clk_i(CLK_i),//cmos senseor clock.

.rst_n_i(RESETn_i2c),//system reset.active low.

//cmos sensor hardware interface.

.cmos_pclk_i(cmos_pclk_i),//(cmos_pclk),//input pixel clock.

.cmos_href_i(cmos_href_r),//(cmos_href),//input pixel hs signal.

.cmos_vsync_i(cmos_vsync_r),//(cmos_vsync),//input pixel vs signal.

.cmos_data_i(cmos_data_r),//(cmos_data),//data.

.cmos_xclk_o(cmos_xclk_o),//(cmos_xclk),//output clock to cmos sensor.

//user interface.

.hs_o(hs_o),//hs signal.

.vs_o(vs_o),//vs signal.

// .de_o(de_o),//data enable.

.rgb565_o(rgb_o),//data output

.vid_clk_ce(vid_clk_ce)

    );

    

count_reset#(

        .num(20'hffff0)

    )(

        .clk_i(CLK_i),

        .rst_o(RESETn_i2c)

    );    

 

endmodule

 

10.3 OTSU HLS程序修改

      之前提到过,MT9V034可以直接输出灰度,不再需要进行灰度转换,所以在HLS程序当中也是需要做一些相应的修改,使其更贴近实际的项目应用。主要修改的部分是位宽的修改和中间灰度转换的删除之类的操作。首先,打开之前二值化的工程,修改top.h如下表所示:

#ifndef _TOP_H_

#define _TOP_H_

 

#include "hls_video.h"

#include "ap_int.h"

#include <math.h>

 

#define MAX_WIDTH  1920

#define MAX_HEIGHT 1080

 

#define INPUT_IMAGE           "QR.png"

//#define INPUT_IMAGE           "test_1080p.bmp"

#define OUTPUT_IMAGE          "result_1080p.bmp"

#define OUTPUT_IMAGE_GOLDEN   "result_1080p_golden.bmp"

 

// typedef video library core structures

typedef hls::stream<ap_axiu<8,1,1,1> >               AXI_STREAM;

typedef hls::Scalar<3, unsigned char>                 RGB_PIXEL;

typedef hls::Mat<MAX_HEIGHT, MAX_WIDTH, HLS_8UC3>     RGB_IMAGE;

typedef hls::Mat<MAX_HEIGHT, MAX_WIDTH, HLS_8UC1>     GRAY_IMAGE;

typedef hls::Scalar<1, unsigned char>                 GRAY_PIXEL;

 

typedef unsigned char    uchar;

typedef ap_uint<12>      int12_t;//自定义12位无符号整型

 

//顶层综合函数

void hls_otsu(AXI_STREAM& src_axi, AXI_STREAM& dst_axi, int rows, int cols);

 

#endif

      程序中修改部分已经用红色字体标识了出来,主要是修改了输入与输出的位宽,由RGB输入与输出改为了灰度输入与输出。然后看到top.cpp修改后的top.cpp如下表所示:

#include "top.h"

#include "hls_math.h"

 

#define Simulation 0

#if Simulation

#include "iostream"

using namespace std;

#endif

 

namespace hls

{

template<int SRC_T, int DST_T,int ROW, int COL, int N>

void threshold(Mat<ROW, COL, SRC_T> &_src,Mat<ROW, COL, DST_T> &_dst,ap_uint<8> (&map)[N]){

const int NUM_STATES = 4; //

Window<1,NUM_STATES,ap_uint<8> > addr_win;

 

//OSTU

ap_uint<BitWidth<ROW*COL>::Value> hist_out[N];

Window<1,NUM_STATES,ap_uint<BitWidth<ROW*COL>::Value> > hist_win;

ap_uint<BitWidth<ROW*COL>::Value> hist;

ap_uint<8> addr;

ap_uint<8> addr_last;

ap_uint<BitWidth<ROW*COL>::Value> hist_last;

ap_uint<8> addr_flag;

ap_uint<BitWidth<ROW*COL>::Value> hist_flag;

ap_uint<8> addr_w;

ap_uint<BitWidth<ROW*COL>::Value> hist_w;

ap_uint<8> threshold = 0;

ap_uint<BitWidth<ROW*COL>::Value> tmp=0;

float pixelPro[256];

 

for(int i=0;i<NUM_STATES;i++) {

#pragma HLS UNROLL

addr_win(0,i)=i;//NUM_STATES

hist_win(0,i)=0;

}

 

for(int i=0;i<N;i++){

hist_out[i] = 0;

pixelPro[i] = 0.0f;

}

 

int cols=_src.cols;

int rows=_src.rows;

assert(rows <= ROW);

assert(cols <= COL);

loop_height: for(int i=0;i<rows;i++){

loop_width: for(int j=0;j<cols;j++){

#pragma HLS PIPELINE

#pragma HLS LOOP_FLATTEN OFF

#pragma HLS DEPENDENCE array inter false

ap_uint<4> flag=NUM_STATES;

HLS_TNAME(SRC_T) tempsrc=0;

HLS_TNAME(DST_T) tempdst=0;

 

_src.data_stream[0] >> tempsrc;

tempdst = map[tempsrc];

_dst.data_stream[0] << tempdst;

for (int m=0; m<NUM_STATES; m++){

if (tempsrc==addr_win(0,m)){

flag = m;

break;

}

}

 

latency_region:{

#pragma HLS latency min=0 max=1

addr_last = addr_win(0,NUM_STATES-1);

hist_last = hist_win(0,NUM_STATES-1)+1;

            

for (int m=NUM_STATES-1; m>0; m--){

addr = addr_win(0,m-1);

hist = hist_win(0,m-1);

if (m == NUM_STATES/2) {

addr_w = addr;

if (m == flag+1) {

hist_w = hist+1;

} else {

hist_w = hist;

}

}

if (m==flag+1) {

addr_flag = addr;

hist_flag = hist+1;

addr_win(0,m) = addr_flag;

hist_win(0,m) = hist_flag;

} else {

addr_win(0,m) = addr;

hist_win(0,m) = hist;

}

}

            

if (flag==NUM_STATES) {

hist_win(0,0) = hist_out[tempsrc]+1;

addr_win(0,0) = tempsrc;

} else if (flag==NUM_STATES-1) {

addr_win(0,0) = addr_last;

hist_win(0,0) = hist_last;

} else if (flag>=NUM_STATES/2) {

addr_win(0,0) = addr_flag;

hist_win(0,0) = hist_flag;

} else {

addr_win(0,0) = addr_w;

hist_win(0,0) = hist_w;

}

        

hist_out[addr_w] = hist_w;

}

}

}

 

 

for (int m=0; m<NUM_STATES/2; m++) {

#pragma HLS PIPELINE

hist_out[addr_win(0,m)]=hist_win(0,m);

}

 

float scale = 255.0f/(cols*rows);

ap_uint<BitWidth<ROW*COL>::Value> sum=0;

loop_normalize: for(int i=0;i<N;i++){

#pragma HLS PIPELINE

tmp = hist_out[i];

pixelPro[i] = (float)(tmp)/(float)(cols*rows);

}

 

 

float w0, w1, u0tmp, u1tmp, u0, u1,deltaTmp, deltaMax = 0.0f;

loop_forward:for(int i = 0; i < 256; i++){

#pragma HLS loop_flatten off

w0 = w1 = u0tmp = u1tmp = u0 = u1 = deltaTmp = 0.0f;

 

loop_front:for(int j = 0; j < i; j++){

#pragma HLS PIPELINE II=5

w0 += pixelPro[j];

u0tmp += (float)j * pixelPro[j];

}

 

loop_back:for(int j = i; j < 256; j++){

#pragma HLPELINE II=5

u1tmp += (float)j * pixelPro[j];

}

 

w1 = 1 - w0;

u0 = u0tmp / w0;

u1 = u1tmp / w1;

 

 

deltaTmp = w0*w1*(u0-u1)*(u0-u1);

 

 

if(deltaTmp > deltaMax){

deltaMax = deltaTmp;

threshold = i;

}

}

#if Simulation

cout << "The Threshold is " << (int)(threshold) << endl;

#endif

 

 

loop_map:for(int i=0;i<N;i++){

#pragma HLS PIPELINE

tmp = hist_out[i];

sum+=tmp;

ap_uint<8> val=sr_cast< ap_uint<8> > (sum*scale);

#if Simulation

cout << "The data is " << (int)(threshold) << endl;

#endif

ap_uint<8> data_val = (val > threshold) ? 255 : 0;

map[i]=data_val;

}

map[0]=0;

}

 

 

static  ap_uint<8> array_data[256];

template<int SRC_T, int DST_T,int ROW, int COL>

void ostu_threshold(

Mat<ROW, COL, SRC_T> &_src,

Mat<ROW, COL, DST_T> &_dst)

{

#pragma HLS INLINE

threshold(_src, _dst, array_data);

}

}

 

void hls_otsu(AXI_STREAM& INPUT_STREAM, AXI_STREAM& OUTPUT_STREAM, int rows, int cols)

{

#pragma HLS INTERFACE axis port=INPUT_STREAM

#pragma HLS INTERFACE axis port=OUTPUT_STREAM

 

#pragma HLS RESOURCE core=AXI_SLAVE variable=rows metadata="-bus_bundle CONTROL_BUS"

#pragma HLS RESOURCE core=AXI_SLAVE variable=cols metadata="-bus_bundle CONTROL_BUS"

#pragma HLS RESOURCE core=AXI_SLAVE variable=return metadata="-bus_bundle CONTROL_BUS"

 

#pragma HLS INTERFACE ap_stable port=rows

#pragma HLS INTERFACE ap_stable port=cols

 

   // RGB_IMAGE  img_0(rows, cols);

    GRAY_IMAGE img_0(rows, cols);

    GRAY_IMAGE img_1(rows, cols);

    GRAY_IMAGE img_2(rows, cols);

    GRAY_IMAGE img_3(rows, cols);

// RGB_IMAGE img_3(rows, cols);

 

#pragma HLS dataflow

hls::AXIvideo2Mat(INPUT_STREAM, img_0);

hls::ostu_threshold(img_0,img_1);

hls::Dilate(img_1,img_2);

hls::Erode(img_2,img_3);

    hls::Mat2AXIvideo(img_3,OUTPUT_STREAM);

}

      在top.Cpp中,较之前的文件相比,删除了灰度转换,增加了图像的开运算操作,目的是用来消除噪声。将修改后的文件重新综合,综合完成之后再重新导出IP,之后将IP文件拷贝到工程的IP文件夹当中。之后的添加IP文件到Vivado工程当中的操作,想必大家已经十分的熟练了,在这里就不再重复的讲解了。

10.4 硬件工程修改

      本章的硬件工程可以直接在第一章的工程当中修改,节省设计的时间。打开第一章的工程,由于MT_sensor_ML这个IP我们已经对其重新进行了封装和修改,因此我们先在vivado中升级此IP,方法是单击Tools->report->report IP status命令,然后在最下方的升级区中点击Upgrade按钮即可进行升级,如下图所示(此处我已经升级过了,所以按钮是灰色的):

10.4.1 video in to AXI4-Stream修改

本章的MT9V034输出已经变为了8bit输出,因此vid_in IP也要进行一定的修改,修改IP配置如下图所示:

10.4.2 添加HLS IP

       在BD文件左侧的工具栏中单击按钮,然后在新弹出的界面中搜索IP的名字,此处是otsu,将其添加到工程当中来,如下图所示:

       将其添加到BD文件当中之后,单击Run connection Automation,如下图所示:

10.4.3 axis_subset IP

      在MT9V034驱动程序当中,我们移除了灰度像素的拼接操作,输出数据变为了8bit,我们知道,显示器要显示灰度图像,要将其拼接为24bit数据,因此,此处我们借助一个IP来完成这一操作,这个IP如下图所示:

      这个IP的功能是对输入的AXI数据进行修改(或修改信号,或改变数据位宽之类的操作),我们将其添加到BD文件当中(添加完成之后记得点击validata design使IP的参数自动变更,方便下一步操作),然后双击对其进行修改,如下图配置:

       此处,我们修改了两处配置,均在图中圈出,第一处将字节数变为3byte,也就是24bit输出,第二处将输入的8bit数据拼接为了24bit,这个IP在本章的作用也就是数据的拼接。修改完成之后将此IP的输入与DMA的输出连接,输出与OSD的输入连接即可。

10.5 SDK工程修改

      VIVADO部分工程修改完成之后,将其重新生成bit流文件,然后删除之前工程的SDK工程文件夹,之后重新导入硬件到SDK,创建一个新的空白工程,并添加一


路过

雷人

握手

鲜花

鸡蛋
发表评论

最新评论

引用 wldshy 2020-1-19 01:15
内容不齐

查看全部评论(1)

本文作者
2019-9-17 11:03
  • 1
    粉丝
  • 3475
    阅读
  • 1
    回复

关注米联客

扫描关注,了解最新资讯

联系人:汤经理
电话:0519-80699907
EMAIL:270682667@qq.com
地址:常州溧阳市天目云谷3号楼北楼201B

关注米联客

扫描关注,了解最新资讯

联系人:汤经理
电话:0519-80699907
EMAIL:270682667@qq.com
地址:常州溧阳市天目云谷3号楼北楼201B

关注米联客

扫描关注,了解最新资讯

联系人:汤经理
电话:0519-80699907
EMAIL:270682667@qq.com
地址:常州溧阳市天目云谷3号楼北楼201B
热门评论
排行榜