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

S04-CH09 自适应二值化的硬件实现

摘要: 软件版本:VIVADO2017.4操作系统:WIN10 64bit硬件平台:适用米联客 ZYNQ系列开发板米联客(MSXBO)论坛:www.osrc.cn答疑解惑专栏开通,欢迎大家给我提问!!9.1概述 本章为大家介绍了如何设计OTSU自适应二值化的功 ...

软件版本:VIVADO2017.4

操作系统:WIN10 64bit

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

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

9.1概述

      本章为大家介绍了如何设计OTSU自适应二值化的功能,并通过硬件验证功能。本章的功能是自主设计的一个程序功能,这对大家在设计自己的程序功能时能提供一定的帮助。另外本章中为大家介绍了一种时序延迟的方法,希望大家可以在课后多加揣摩。

9.2 OTSU自适应二值化原理简介

      最大类间方差法是由日本学者大津于1979年提出的,是一种自适应的阈值确定的方法,又叫大津法,简称OTSU。它是按图像的灰度特性,将图像分成背景和目标2部分。背景和目标之间的类间方差0越大,说明构成图像的2部分的差别越大,当部分目标错分为背景或部分背景错分为目标都会导致2部分差别变小。因此,使类间方差最大的分割意味着错分概率最小。对于图像I(x,y),前景(即目标)和背景的分割阈值记作T,属于前景的像素点数占整幅图像的比例记为ω0,其平均灰度μ0;背景像素点数占整幅图像的比例为ω1,其平均灰度为μ1。图像的总平均灰度记为μ,类间方差记为g。假设图像的背景较暗,并且图像的大小为M×N,图像中像素的灰度值小于阈值T的像素个数记作N0,像素灰度大于阈值T的像素个数记作N1,则有:
                  ω0=N0/ M×N (1)
                  ω1=N1/ M×N (2)
                  N0+N1=M×N (3)
                  ω0+ω1=1 (4)
                  μ=ω0*μ0+ω1*μ1 (5)
                  g=ω0(μ0-μ)^2+ω1(μ1-μ)^2 (6)
将式(5)代入式(6),得到等价公式: g=ω0ω1(μ0-μ1)^2 (7)
采用遍历的方法得到使类间方差最大的阈值T,即为所求。

Otsu算法步骤如下:
设图象包含L个灰度级(0,1…,L-1),灰度值为i的的象素点数为Ni ,图象总的象素点数为N=N0+N1+...+N(L-1)。灰度值为i的点的概为:
P(i) = N(i)/N.
门限t将整幅图象分为暗区c1和亮区c2两类,则类间方差σ是t的函数:
σ=a1*a2(u1-u2)^2 (2)
式中,aj 为类cj的面积与图象总面积之比,a1 = sum(P(i)) i->t, a2 = 1-a1; uj为类cj的均值,u1 = sum(i*P(i))/a1 0->t, 
u2 = sum(i*P(i))/a2, t+1->L-1 
该法选择最佳门限t^ 使类间方差最大,即:令Δu=u1-u2,σb = max{a1(t)*a2(t)Δu^2}

9.3 HLS实现

9.3.1 工程创建

Step1:打开HLS,按照之前介绍的方法,创建一个新的工程,命名为otsu_threshold。

Step2:右单击Source选项,选择New File,创建一个名为Top.cpp的文件。

Step3:在打开的编辑区中,把下面的程序拷贝进去:

#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;//声明1行4列,每个元素为无符号8bit的窗口

 

//OSTU需要统计不同灰度值个数

ap_uint<BitWidth<ROW*COL>::Value> hist_out[N];//以行列乘积最大位宽为边界,如ROW*COL = 1920*1080 = 2073600 ,

//尾位宽存储需21位,则ap_uint<BitWidth<ROW*COL>::Value> 等同于ap_uint<21>

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);

}

 

//经典ostu算法,得到前景和背景的分割  

//遍历灰度级[0,255],计算出方差最大的灰度值,为最佳阈值

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_counter_color(AXI_STREAM_IN& INPUT_STREAM, AXI_STREAM_OUT& 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_1(rows, cols);

    GRAY_IMAGE img_2(rows, cols);

RGB_IMAGE img_3(rows, cols);

 

#pragma HLS dataflow

hls::AXIvideo2Mat(INPUT_STREAM, img_0);

hls::CvtColor<HLS_RGB2GRAY>(img_0,img_1);

hls::ostu_threshold(img_1,img_2);

hls::CvtColor<HLS_GRAY2RGB>(img_2,img_3);//将灰度图像转换为RGB图像

    hls::Mat2AXIvideo(img_3,OUTPUT_STREAM);

}

Step4:再在Source中添加一个名为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           "car.bmp"

//#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<32,1,1,1> >               AXI_STREAM_IN;

typedef hls::stream<ap_axiu<32,1,1,1> >               AXI_STREAM_OUT;

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_counter_color(AXI_STREAM_IN& src_axi, AXI_STREAM_OUT& dst_axi, int rows, int cols);

 

#endif

Step5:在Test Bench中,用同样的方法添加一个名为Test.cpp的测试程序。添加如下代码:

//Note::OSTU实现使用上一帧数据的结果处理下一帧数据,第一帧仿真时黑屏,通过下面代码ifi==1)屏蔽

//仿真时至少需要运行两次

 

const int N = 2;//仿真运行的次数,至少为2

 

int main (int argc, char** argv){

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

//获取图像数据

IplImage* src = cvLoadImage(INPUT_IMAGE);

IplImage* dst = cvCreateImage(cvGetSize(src), src->depth, src->nChannels);

//cvShowImage("hls_src", src);//显示原始图像

 

//使用HLS处理

AXI_STREAM_IN  src_axi;

AXI_STREAM_OUT dst_axi;

IplImage2AXIvideo(src, src_axi);

hls_counter_color(src_axi, dst_axi, src->height, src->width);

AXIvideo2IplImage(dst_axi, dst);

cvShowImage("hls", dst);//显示处理后的图像

 

//使用opencv库处理

//opencv_image_filter(src,dst);

//cvShowImage("opencv", dst);//显示处理后的图像

 

if(i == 1)

waitKey(0);

 

//释放内存

cvReleaseImage(&src);

cvReleaseImage(&dst);

}

}

Step6:在Test Bench中添加一张名为car.bmp的测试图片,图片可以在我们提供的源程序中的Image文件夹中找到。完整的工程如下图所示:

9.3.2 仿真及优化

Step1:单击Project settings快捷键。


路过

雷人

握手

鲜花

鸡蛋
发表评论

最新评论

引用 wldshy 2020-1-19 01:14
内容不全

查看全部评论(1)

本文作者
2019-9-17 11:02
  • 1
    粉丝
  • 2871
    阅读
  • 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
热门评论
排行榜