// sobel_filter_axis3.cpp
// 2020/02/27 by marsee
#include <ap_int.h>
#include <hls_stream.h>
#include <ap_axi_sdata.h>
#define HORIZONTAL 0
#define VERTICAL 1
ap_int<32> sobel_fil(ap_int<32> h_or_v, ap_int<32> x0y0, ap_int<32> x1y0, ap_int<32> x2y0, ap_int<32> x0y1,
ap_int<32> x1y1, ap_int<32> x2y1, ap_int<32> x0y2, ap_int<32> x1y2, ap_int<32> x2y2);
ap_int<32> conv_rgb2y(ap_int<32> rgb);
ap_int<32> square_root8(ap_int<32> val);
#define DISPLAY_WIDTH 800
#define DISPLAY_HIGHT 600
int sobel_filter_axis(hls::stream<ap_axis<32,1,1,1> >& ins, hls::stream<ap_axis<32,1,1,1> >& outs){
#pragma HLS INTERFACE axis register both port=ins
#pragma HLS INTERFACE axis register both port=outs
#pragma HLS INTERFACE s_axilite port=return
ap_axis<32,1,1,1> pix;
ap_axis<32,1,1,1> sobel;
ap_int<32> sobel_val, sobel_h_val, sobel_v_val;
ap_int<32> line_buf[2][DISPLAY_WIDTH];
#pragma HLS array_partition variable=line_buf block factor=2 dim=1
#pragma HLS resource variable=line_buf core=RAM_2P
ap_int<32> pix_mat[3][3];
#pragma HLS array_partition variable=pix_mat complete
LOOP_WAIT_USER : do { // user が 1になった時にフレームがスタートする
#pragma HLS LOOP_TRIPCOUNT min=1 max=1 avg=1
ins >> pix;
} while(pix.user == 0);
LOOP_Y: for(int y=0; y<DISPLAY_HIGHT; y++){
#pragma HLS LOOP_TRIPCOUNT min=48 max=48 avg=48
LOOP_X: for(int x=0; x<DISPLAY_WIDTH; x++){
#pragma HLS PIPELINE II=1
#pragma HLS LOOP_TRIPCOUNT min=64 max=64 avg=64
if (!(x==0 && y==0)) // 最初の入力はすでに入力されている
ins >> pix; // AXI4-Stream からの入力
LOOP_PIX_MAT_K: for(int k=0; k<3; k++){
LOOP_PIX_MAT_M: for(int m=0; m<2; m++){
pix_mat[k][m] = pix_mat[k][m+1];
}
}
pix_mat[0][2] = line_buf[0][x];
pix_mat[1][2] = line_buf[1][x];
ap_int<32> y_val = conv_rgb2y(pix.data);
pix_mat[2][2] = y_val;
line_buf[0][x] = line_buf[1][x]; // 行の入れ替え
line_buf[1][x] = y_val;
sobel_h_val = sobel_fil(HORIZONTAL, pix_mat[0][0], pix_mat[0][1], pix_mat[0][2],
pix_mat[1][0], pix_mat[1][1], pix_mat[1][2],
pix_mat[2][0], pix_mat[2][1], pix_mat[2][2]);
sobel_v_val = sobel_fil(VERTICAL, pix_mat[0][0], pix_mat[0][1], pix_mat[0][2],
pix_mat[1][0], pix_mat[1][1], pix_mat[1][2],
pix_mat[2][0], pix_mat[2][1], pix_mat[2][2]);
sobel_val = square_root8(sobel_h_val*sobel_h_val + sobel_v_val*sobel_v_val);
sobel.data = (sobel_val<<16)+(sobel_val<<8)+sobel_val;
if(x==0 && y==0) // 最初のピクセル
sobel.user = 1;
else
sobel.user = 0;
if(x == (DISPLAY_WIDTH-1)) // 行の最後
sobel.last = 1;
else
sobel.last = 0;
if(x<2 || y<2)
sobel.data = 0;
outs << sobel;
}
}
return(0);
}
// RGBからYへの変換
// RGBのフォーマットは、{8'd0, R(8bits), G(8bits), B(8bits)}, 1pixel = 32bits
// 輝度信号Yのみに変換する。変換式は、Y = 0.299R + 0.587G + 0.114B
// "YUVフォーマット及び YUV<->RGB変換"を参考にした。http://vision.kuee.kyoto-u.ac.jp/~hiroaki/firewire/yuv.html
// 2013/09/27 : float を止めて、すべてint にした
ap_int<32> conv_rgb2y(ap_int<32> rgb){
ap_int<32> r, g, b, y_f;
ap_int<32> y;
b = rgb & 0xff;
g = (rgb>>8) & 0xff;
r = (rgb>>16) & 0xff;
y_f = 77*r + 150*g + 29*b; //y_f = 0.299*r + 0.587*g + 0.114*b;の係数に256倍した
y = y_f >> 8; // 256で割る
return(y);
}
// sobel filter
// HORZONTAL
// x0y0 x1y0 x2y0 1 2 1
// x0y1 x1y1 x2y1 0 0 0
// x0y2 x1y2 x2y2 -1 -2 -1
// VERTICAL
// x0y0 x1y0 x2y0 1 0 -1
// x0y1 x1y1 x2y1 2 0 -2
// x0y2 x1y2 x2y2 1 0 -1
ap_int<32> sobel_fil(ap_int<32> h_or_v, ap_int<32> x0y0, ap_int<32> x1y0, ap_int<32> x2y0, ap_int<32> x0y1,
ap_int<32> x1y1, ap_int<32> x2y1, ap_int<32> x0y2, ap_int<32> x1y2, ap_int<32> x2y2){
ap_int<32> y;
if(h_or_v == HORIZONTAL){
y = x0y0 + 2*x1y0 + x2y0 - x0y2 - 2*x1y2 - x2y2;
} else {
y = x0y0 - x2y0 + 2*x0y1 - 2*x2y1 + x0y2 - x2y2;
}
if(y<0)
y = -y;
//y = 0;
else if(y>255)
y = 255;
return(y);
}
// square_root8
// 8bit幅のsquare_rootを求める
ap_int<32> square_root8(ap_int<32> val){
ap_int<32> temp = 0;
ap_int<32> square;
for(int i=7; i>=0; --i){
temp += (1 << i);
square = temp * temp;
if(square > val){
temp -= (1 << i);
}
}
return(temp);
}
// sobel_filter_axis3_tb.cpp
// 2020/02/27 by marsee
#include <stdio.h>
#include <stdint.h>
#include "hls_opencv.h"
#include <ap_int.h>
#include <hls_stream.h>
#include <ap_axi_sdata.h>
int sobel_filter_axis(hls::stream<ap_axis<32,1,1,1> >& ins, hls::stream<ap_axis<32,1,1,1> >& outs);
int sobel_filter_soft(int32_t *cam_fb, int32_t *sobel_fb,
int32_t x_size, int32_t y_size);
int32_t square_root8_soft(int32_t val);
const char INPUT_BMP_FILE[] = "test2.jpg";
const char OUTPUT_BMP_FILE[] = "sobel.jpg";
int main(){
hls::stream<ap_axis<32,1,1,1> > ins;
hls::stream<ap_axis<32,1,1,1> > ins_soft;
hls::stream<ap_axis<32,1,1,1> > outs;
hls::stream<ap_axis<32,1,1,1> > outs_soft;
ap_axis<32,1,1,1> pix;
ap_axis<32,1,1,1> vals;
// BMPファイルをMat に読み込む
cv::Mat img = cv::imread(INPUT_BMP_FILE);
// ピクセルを入れる領域の確保
std::vector<int32_t> rd_bmp(sizeof(int32_t)*img.cols*img.rows);
std::vector<int32_t> hw_sobel(sizeof(int32_t)*img.cols*img.rows);
std::vector<int32_t> sw_sobel(sizeof(int32_t)*img.cols*img.rows);
// rd_bmp にBMPのピクセルを代入
cv::Mat_<cv::Vec3b> dst_vec3b = cv::Mat_<cv::Vec3b>(img);
for (int y=0; y<img.rows; y++){
for (int x=0; x<img.cols; x++){
cv::Vec3b pixel;
pixel = dst_vec3b(y,x);
rd_bmp[y*img.cols+x] = (pixel[0] & 0xff) | ((pixel[1] & 0xff)<<8) | ((pixel[2] & 0xff)<<16);
// blue - pixel[0]; green - pixel[1]; red - pixel[2];
}
}
// ins に入力データを用意する
for(int i=0; i<5; i++){ // dummy data
pix.user = 0;
pix.data = i;
ins << pix;
}
for(int j=0; j < img.rows; j++){
for(int i=0; i < img.cols; i++){
pix.data = (ap_int<32>)rd_bmp[(j*img.cols)+i];
if (j==0 && i==0) // 最初のデータの時に TUSER を 1 にする
pix.user = 1;
else
pix.user = 0;
if (i == img.cols-1) // 行の最後でTLASTをアサートする
pix.last = 1;
else
pix.last = 0;
ins << pix;
}
}
sobel_filter_axis(ins, outs); // ハードウェアのソーベルフィルタ
sobel_filter_soft(rd_bmp.data(), sw_sobel.data(), img.cols, img.rows); // ソフトウェアのソーベルフィルタ
// ハードウェアとソフトウェアのソーベルフィルタの値のチェック
for (int y=0; y<img.rows; y++){
for (int x=0; x<img.cols; x++){
outs >> vals;
ap_int<32> val = vals.data;
hw_sobel[y*img.cols+x] = (int32_t)val;
if (val != sw_sobel[y*img.cols+x]){
printf("ERROR HW and SW results mismatch x = %ld, y = %ld, HW = %d, SW = %d\n",
x, y, val, sw_sobel[y*img.cols+x]);
return(1);
}
}
}
printf("Success HW and SW results match\n");
const int sobel_row = img.rows;
const int sobel_cols = img.cols;
cv::Mat wbmpf(sobel_row, sobel_cols, CV_8UC3);
// wbmpf にsobel フィルタ処理後の画像を入力
cv::Mat_<cv::Vec3b> sob_vec3b = cv::Mat_<cv::Vec3b>(wbmpf);
for (int y=0; y<wbmpf.rows; y++){
for (int x=0; x<wbmpf.cols; x++){
cv::Vec3b pixel;
pixel = sob_vec3b(y,x);
int32_t rgb = hw_sobel[y*wbmpf.cols+x];
pixel[0] = (rgb & 0xff); // blue
pixel[1] = (rgb & 0xff00) >> 8; // green
pixel[2] = (rgb & 0xff0000) >> 16; // red
sob_vec3b(y,x) = pixel;
}
}
// ハードウェアのソーベルフィルタの結果を bmp ファイルへ出力する
cv::imwrite(OUTPUT_BMP_FILE, wbmpf);
return(0);
}
#define HORIZONTAL 0
#define VERTICAL 1
int32_t sobel_fil_soft(int32_t h_or_v, int32_t x0y0, int32_t x1y0, int32_t x2y0, int32_t x0y1,
int32_t x1y1, int32_t x2y1, int32_t x0y2, int32_t x1y2, int32_t x2y2);
int32_t conv_rgb2y_soft(int32_t rgb);
int sobel_filter_soft(int32_t *cam_fb, int32_t *sobel_fb,
int32_t x_size, int32_t y_size){
int32_t sobel_val, sobel_h_val, sobel_v_val;
int32_t pix[3][3];
for(int y=0; y<y_size; y++){
for(int x=0; x<x_size; x++){
for(int i=2; i>=0; --i){
for(int j=2; j>=0; --j){
if(x>=2 && y>=2)
pix[i][j] = conv_rgb2y_soft(cam_fb[(y-i)*x_size+(x-j)]);
else
pix[i][j] = 0;
}
}
sobel_h_val = sobel_fil_soft(HORIZONTAL,pix[0][0], pix[0][1], pix[0][2],
pix[1][0], pix[1][1], pix[1][2],
pix[2][0], pix[2][1], pix[2][2]);
sobel_v_val = sobel_fil_soft(VERTICAL, pix[0][0], pix[0][1], pix[0][2],
pix[1][0], pix[1][1], pix[1][2],
pix[2][0], pix[2][1], pix[2][2]);
sobel_val = square_root8_soft(sobel_h_val*sobel_h_val + sobel_v_val*sobel_v_val);
sobel_fb[y*x_size+x] = (sobel_val<<16)+(sobel_val<<8)+sobel_val;
}
}
return(0);
}
// RGBからYへの変換
// RGBのフォーマットは、{8'd0, R(8bits), G(8bits), B(8bits)}, 1pixel = 32bits
// 輝度信号Yのみに変換する。変換式は、Y = 0.299R + 0.587G + 0.114B
// "YUVフォーマット及び YUV<->RGB変換"を参考にした。http://vision.kuee.kyoto-u.ac.jp/~hiroaki/firewire/yuv.html
// 2013/09/27 : float を止めて、すべてint にした
int32_t conv_rgb2y_soft(int32_t rgb){
int32_t r, g, b, y_f;
int32_t y;
b = rgb & 0xff;
g = (rgb>>8) & 0xff;
r = (rgb>>16) & 0xff;
y_f = 77*r + 150*g + 29*b; //y_f = 0.299*r + 0.587*g + 0.114*b;の係数に256倍した
y = y_f >> 8; // 256で割る
return(y);
}
// sobel filter
// HORZONTAL
// x0y0 x1y0 x2y0 1 2 1
// x0y1 x1y1 x2y1 0 0 0
// x0y2 x1y2 x2y2 -1 -2 -1
// VERTICAL
// x0y0 x1y0 x2y0 1 0 -1
// x0y1 x1y1 x2y1 2 0 -2
// x0y2 x1y2 x2y2 1 0 -1
int32_t sobel_fil_soft(int32_t h_or_v, int32_t x0y0, int32_t x1y0, int32_t x2y0, int32_t x0y1,
int32_t x1y1, int32_t x2y1, int32_t x0y2, int32_t x1y2, int32_t x2y2){
int32_t y;
if(h_or_v == HORIZONTAL){
y = x0y0 + 2*x1y0 + x2y0 - x0y2 - 2*x1y2 - x2y2;
} else {
y = x0y0 - x2y0 + 2*x0y1 - 2*x2y1 + x0y2 - x2y2;
}
if(y<0)
y = -y;
//y = 0;
else if(y>255)
y = 255;
return(y);
}
// square_root8_soft
// 8bit幅のsquare_rootを求める
int32_t square_root8_soft(int32_t val){
int32_t temp = 0;
int32_t square;
for(int i=7; i>=0; --i){
temp += (1 << i);
square = temp * temp;
if(square > val){
temp -= (1 << i);
}
}
return(temp);
}
// sobel_filter_hvl.h
// 2020/02/25 by marsee
#ifndef __SOBEL_FILTER_HVL_H__
#define __SOBEL_FILTER_HVL_H__
#include "ap_axi_sdata.h"
#include "hls_video.h"
#define MAX_HEIGHT 600
#define MAX_WIDTH 800
typedef hls::stream<ap_axiu<32,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;
#endif
// sobel_filter_hvl.cpp
// 2020/02/25 by marsee
// 2016/04/03 : グレー変換あり Sobel フィルタ
#include "sobel_filter_hvl.h"
void sobel_filter(AXI_STREAM& INPUT_STREAM, AXI_STREAM& OUTPUT_STREAM, int rows, int
cols) {
#pragma HLS DATAFLOW
#pragma HLS INTERFACE ap_stable port=cols
#pragma HLS INTERFACE ap_stable port=rows
#pragma HLS INTERFACE s_axilite port=return
#pragma HLS INTERFACE axis register both port=OUTPUT_STREAM
#pragma HLS INTERFACE axis register both port=INPUT_STREAM
#pragma HLS INTERFACE s_axilite port=cols
#pragma HLS INTERFACE s_axilite port=rows
RGB_IMAGE img_0(rows, cols);
GRAY_IMAGE img_1g(rows, cols);
GRAY_IMAGE img_2g(rows, cols);
RGB_IMAGE img_3(rows, cols);
hls::AXIvideo2Mat(INPUT_STREAM, img_0);
hls::CvtColor<HLS_BGR2GRAY>(img_0, img_1g);
hls::Sobel<1,0,3>(img_1g, img_2g);
hls::CvtColor<HLS_GRAY2BGR>(img_2g, img_3);
hls::Mat2AXIvideo(img_3, OUTPUT_STREAM);
}
// sobel_filter_hvl_tb.cpp
// 2020/02/25 by marsee
// OpenCV 2 の Mat を使用したバージョン
// 2016/04/03 : グレー変換あり Sobel フィルタ
#include <iostream>
#include "hls_opencv.h"
#include "sobel_filter_hvl.h"
using namespace cv;
#define INPUT_IMAGE "test2.jpg"
#define OUTPUT_IMAGE "test2_result.jpg"
#define OUTPUT_IMAGE_CV "test2_result_cv.jpg"
void sobel_filter(AXI_STREAM& INPUT_STREAM, AXI_STREAM& OUTPUT_STREAM, int rows, int
cols);
void opencv_sobel_filter(Mat& src, Mat& dst);
int main (int argc, char** argv) {
// OpenCV で 画像を読み込む
Mat src = imread(INPUT_IMAGE);
AXI_STREAM src_axi, dst_axi;
// Mat フォーマットから AXI4 Stream へ変換
cvMat2AXIvideo(src, src_axi);
// image_filter() 関数をコール
sobel_filter(src_axi, dst_axi, src.rows, src.cols);
// AXI4 Stream から Mat フォーマットへ変換
// dst は宣言時にサイズとカラー・フォーマットを定義する必要がある
Mat dst(src.rows, src.cols, CV_8UC3);
AXIvideo2cvMat(dst_axi, dst);
// Mat フォーマットからファイルに書き込み
imwrite(OUTPUT_IMAGE, dst);
// opencv_image_filter() をコール
Mat dst_cv(src.rows, src.cols, CV_8UC3);
opencv_sobel_filter(src, dst_cv);
imwrite(OUTPUT_IMAGE_CV, dst_cv);
// dst と dst_cv が同じ画像かどうか?比較する
for (int y=0; y<src.rows; y++){
Vec3b* dst_ptr = dst.ptr<Vec3b>(y);
Vec3b* dst_cv_ptr = dst_cv.ptr<Vec3b>(y);
for (int x=0; x<src.cols; x++){
Vec3b dst_bgr = dst_ptr[x];
Vec3b dst_cv_bgr = dst_cv_ptr[x];
// bgr のどれかが間違っていたらエラー
if (dst_bgr[0] != dst_cv_bgr[0] || dst_bgr[1] != dst_cv_bgr[1] || dst_bgr[2] != dst_cv_bgr[2]){
printf("x = %d, y = %d, Error dst=%d,%d,%d dst_cv=%d,%d,%d\n", x, y,
dst_bgr[0], dst_bgr[1], dst_bgr[0], dst_cv_bgr[0], dst_cv_bgr[1], dst_cv_bgr[2]);
//return 1;
}
}
}
printf("Test with 0 errors.\n");
return 0;
}
void opencv_sobel_filter(Mat& src, Mat& dst){
Mat gray(src.rows, src.cols, CV_8UC1);
Mat img0g(src.rows, src.cols, CV_8UC1);
cvtColor(src, gray, CV_BGR2GRAY);
Sobel(gray, img0g, IPL_DEPTH_16S, 1, 0, 3);
cvtColor(img0g, dst, CV_GRAY2BGR);
}
ERROR: [DRC UTLZ-1] Resource utilization: RAMB18 and RAMB36/FIFO over-utilized in Top Level Design (This design requires more RAMB18 and RAMB36/FIFO cells than are available in the target device. This design requires 470 of such cell types but only 432 compatible sites are available in the target device. Please analyze your synthesis results and constraints to ensure the design is mapped to Xilinx primitives as expected. If so, please consider targeting a larger device.)
ERROR: [DRC UTLZ-1] Resource utilization: RAMB36/FIFO over-utilized in Top Level Design (This design requires more RAMB36/FIFO cells than are available in the target device. This design requires 235 of such cell types but only 216 compatible sites are available in the target device. Please analyze your synthesis results and constraints to ensure the design is mapped to Xilinx primitives as expected. If so, please consider targeting a larger device.)
ERROR: [DRC UTLZ-1] Resource utilization: RAMB36E2 over-utilized in Top Level Design (This design requires more RAMB36E2 cells than are available in the target device. This design requires 235 of such cell types but only 216 compatible sites are available in the target device. Please analyze your synthesis results and constraints to ensure the design is mapped to Xilinx primitives as expected. If so, please consider targeting a larger device.)
INFO: [Vivado_Tcl 4-198] DRC finished with 3 Errors
#pragma HLS stream variable=imgInput1.data dim=1 depth=16
#pragma HLS stream variable=imgOutput1.data dim=1 depth=16
のように new/ を除く。set file "sobel_ex5_0.xdc"
# Add/Import constrs file and set constrs file properties
set file "[file normalize "$origin_dir/Vivado/constrains/sobel_ex5_0.xdc"]"
set file_imported [import_files -fileset constrs_1 [list $file]]
set file "sobel_ex5_0.xdc"
set file_obj [get_files -of_objects [get_filesets constrs_1] [list "*$file"]]
set_property -name "file_type" -value "XDC" -objects $file_obj
を入力した。-D__SDSVHLS__ -I/home/masaaki/xfopencv/include --std=c++0x
を入力した。-D__SDSVHLS__ -I/home/masaaki/xfopencv/include --std=c++0x
を入力した。-D__SDSVHLS__ -I/home/masaaki/xfopencv/include --std=c++0x
// xf_sobel.cpp
// 2020/02/13 by marsee
// xfopencv/HLS_Use_Model/Standalone_HLS_AXI_Example/xf_ip_accel_app.cpp のコードを引用している
// https://github.com/Xilinx/xfopencv/blob/master/HLS_Use_Model/Standalone_HLS_AXI_Example/xf_ip_accel_app.cpp
#include "xf_sobel_config.h"
#include "common/xf_infra.h"
void xf_sobel(hls::stream< ap_axiu<8,1,1,1> >& _src,hls::stream< ap_axiu<8,1,1,1> >& _dst,int height,int width){
#pragma HLS INTERFACE s_axilite port=return
#pragma HLS INTERFACE axis register both port=_src
#pragma HLS INTERFACE axis register both port=_dst
xf::Mat<XF_8UC1, HEIGHT, WIDTH, NPC1> imgInput1(height,width);
xf::Mat<XF_8UC1, HEIGHT, WIDTH, NPC1> dstgx(height,width);
xf::Mat<XF_8UC1, HEIGHT, WIDTH, NPC1> dstgy(height,width);
#pragma HLS stream variable=imgInput1.data dim=1 depth=1
#pragma HLS stream variable=imgOutput1.data dim=1 depth=1
#pragma HLS dataflow
xf::AXIvideo2xfMat(_src, imgInput1);
sobel_accel(imgInput1,dstgx, dstgy);
xf::xfMat2AXIvideo(dstgx, _dst);
}
// xf_sobel_tb.cpp
// 2020/02/13 by marsee
// xfopencv/HLS_Use_Model/Standalone_HLS_AXI_Example/xf_dilation_tb.cpp のコードを引用している
// https://github.com/Xilinx/xfopencv/blob/master/HLS_Use_Model/Standalone_HLS_AXI_Example/xf_dilation_tb.cpp
#include "xf_headers.h"
#include "xf_sobel_config.h"
#include "common/xf_infra.h"
#include "common/xf_axi.h"
void xf_sobel(hls::stream< ap_axiu<8,1,1,1> >& _src,hls::stream< ap_axiu<8,1,1,1> >& _dst,int height,int width);
int main(int argc, char** argv)
{
if(argc != 2)
{
fprintf(stderr,"Invalid Number of Arguments!\nUsage:\n");
fprintf(stderr,"<Executable Name> <input image path> \n");
return -1;
}
cv::Mat out_img,ocv_ref;
cv::Mat in_img,in_img1,diff;
// reading in the color image
in_img = cv::imread(argv[1], 0);
if (in_img.data == NULL)
{
fprintf(stderr,"Cannot open image at %s\n", argv[1]);
return 0;
}
// create memory for output images
ocv_ref.create(in_img.rows,in_img.cols,CV_8UC1);
diff.create(in_img.rows,in_img.cols,CV_8UC1);
in_img1.create(in_img.rows,in_img.cols,CV_8UC1);
uint16_t height = in_img.rows;
uint16_t width = in_img.cols;
///////////////// Opencv Reference ////////////////////////
cv::Sobel(in_img, ocv_ref, CV_8UC1, 1, 0, 3);
cv::imwrite("out_ocv.jpg", ocv_ref);
hls::stream< ap_axiu<8,1,1,1> > _src,_dst;
cvMat2AXIvideoxf<NPC1>(in_img, _src);
xf_sobel(_src, _dst, height, width);
AXIvideo2cvMatxf<NPC1>(_dst, in_img1);
cv::imwrite("hls.jpg", in_img1);
////////////////// Compute Absolute Difference ////////////////////
cv::absdiff(ocv_ref, in_img1, diff);
cv::imwrite("out_error.jpg", diff);
// Find minimum and maximum differences.
double minval=256,maxval=0;
int cnt = 0;
for (int i=0; i<in_img.rows; i++)
{
for(int j=0; j<in_img.cols; j++)
{
uchar v = diff.at<uchar>(i,j);
if (v>0)
cnt++;
if (minval > v)
minval = v;
if (maxval < v)
maxval = v;
}
}
float err_per = 100.0*(float)cnt/(in_img.rows*in_img.cols);
fprintf(stderr,"Minimum error in intensity = %f\n Maximum error in intensity = %f\n Percentage of pixels above error threshold = %f\n",minval,maxval,err_per);
if(err_per > 0.3f)
return 1;
return 0;
}
と教えていただたいので、depth=16 にしてもう一度やってみた。教えていただいた方も depth=16 にされていたようだが、私も 16 にした理由は、LUT を使用した分散RAM の depth が 16 だからである。教えていただいてありがとうございました。C/RTL協調シミュレーションにおいてスループットが1/2となってしまっているのは、xf::Matのメンバであるhls::streamに対して、depth=1と指定されているのが原因のようです。
#pragma HLS stream variable=imgInput1.data dim=1 depth=16
#pragma HLS stream variable=imgOutput1.data dim=1 depth=16
Vivado HLS 2019.1 で使用モデルが正しく機能するようにするため、次の変更を加える必要があります。
1. 適切なコンパイル時間オプションの使用: HLS で xfOpenCV 関数を使用する際、コンパイル時に -D__SDSVHLS__
および -std=c++0x オプションを使用する必要があります。
2. インターフェイス レベル引数へのインターフェイス プラグマの指定: 最上位インターフェイス引数を (1 つ以上の
読み出し/書き込みアクセスを持つ) ポインターとして含む関数には、m_axi インターフェイス プラグマを指定す
る必要があります。次に例を示します。
void lut_accel(xf::Mat<TYPE, HEIGHT, WIDTH, NPC1> &imgInput,
xf::Mat<TYPE, HEIGHT, WIDTH, NPC1> &imgOutput, unsigned char *lut_ptr)
{
#pragma HLS INTERFACE m_axi depth=256 port=lut_ptr offset=direct bundle=lut_ptr
xf::LUT< TYPE, HEIGHT, WIDTH, NPC1> (imgInput,imgOutput,lut_ptr);
}
を-cflags "-D__SDSVHLS__ -I../../include --std=c++0x"
に変更した。-cflags "-D__SDSVHLS__ -I/home/masaaki/xfopencv/include --std=c++0x"
ということなので、やってみよう。Vitis 環境では、v++ コマンドで --nk オプションを使用してカーネル (計算ユニット) のインスタンス数を指定します。--nk オプションは、xlcbin ファイルにインスタンシエートするカーネルの数と名前マップを指定します。
を入力した。--config ../src/krnl_stream_dmar_lap_dmaw.ini
[connectivity]
nk=dma_read:1:dma_read_2
nk=krnl_lap_filter_dmaw:1:krnl_lap_filter_dmaw_2
stream_connect=dma_read_2.outs:krnl_lap_filter_dmaw_2.ins
stream_connect=dma_read_1.outs:krnl_lap_filter_dmaw_1.ins
// krnl_streaming_lap_host3.cpp
// 2020/02/08 by marsee
// Vitis-Tutorials/docs/mixing-c-rtl-kernels/reference-files/src/host/host_step1.cpp のコードを引用します
// https://github.com/Xilinx/Vitis-Tutorials/blob/master/docs/mixing-c-rtl-kernels/reference-files/src/host/host_step1.cpp
#define CL_HPP_CL_1_2_DEFAULT_BUILD
#define CL_HPP_TARGET_OPENCL_VERSION 120
#define CL_HPP_MINIMUM_OPENCL_VERSION 120
#define CL_HPP_ENABLE_PROGRAM_CONSTRUCTION_FROM_ARRAY_COMPATIBILITY 1
#define CL_USE_DEPRECATED_OPENCL_1_2_APIS
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/time.h>
#include <vector>
#include <CL/cl2.hpp>
#include <iostream>
#include <fstream>
#include <CL/cl_ext_xilinx.h>
#include <unistd.h>
#include <limits.h>
#include <sys/stat.h>
#include <ap_int.h>
#include <hls_stream.h>
#include <ap_axi_sdata.h>
#include "bmp_header.h"
int laplacian_fil_soft(int32_t x0y0, int32_t x1y0, int32_t x2y0, int32_t x0y1, int32_t x1y1, int32_t x2y1, int32_t x0y2, int32_t x1y2, int32_t x2y2);
int32_t conv_rgb2y_soft(int32_t rgb);
int32_t lap_filter_axis_soft(hls::stream<ap_axis<32,1,1,1> >& ins, hls::stream<ap_axis<32,1,1,1> >& outs, int32_t width, int32_t height); // software
static const std::string error_message =
"Error: Result mismatch:\n"
"i = %d CPU result = %d Device result = %d\n";
//Some Library functions to be used.
template <typename T>
struct aligned_allocator
{
using value_type = T;
T* allocate(std::size_t num)
{
void* ptr = nullptr;
if (posix_memalign(&ptr,4096,num*sizeof(T)))
throw std::bad_alloc();
return reinterpret_cast<T*>(ptr);
}
void deallocate(T* p, std::size_t num)
{
free(p);
}
};
#define OCL_CHECK(error,call) \
call; \
if (error != CL_SUCCESS) { \
printf("%s:%d Error calling " #call ", error code is: %d\n", \
__FILE__,__LINE__, error); \
exit(EXIT_FAILURE); \
}
namespace xcl {
std::vector<cl::Device> get_devices(const std::string& vendor_name) {
size_t i;
cl_int err;
std::vector<cl::Platform> platforms;
OCL_CHECK(err, err = cl::Platform::get(&platforms));
cl::Platform platform;
for (i = 0 ; i < platforms.size(); i++){
platform = platforms[i];
OCL_CHECK(err, std::string platformName = platform.getInfo<CL_PLATFORM_NAME>(&err));
if (platformName == vendor_name){
std::cout << "Found Platform" << std::endl;
std::cout << "Platform Name: " << platformName.c_str() << std::endl;
break;
}
}
if (i == platforms.size()) {
std::cout << "Error: Failed to find Xilinx platform" << std::endl;
exit(EXIT_FAILURE);
}
//Getting ACCELERATOR Devices and selecting 1st such device
std::vector<cl::Device> devices;
OCL_CHECK(err, err = platform.getDevices(CL_DEVICE_TYPE_ACCELERATOR, &devices));
return devices;
}
std::vector<cl::Device> get_xil_devices() {
return get_devices("Xilinx");
}
char* read_binary_file(const std::string &xclbin_file_name, unsigned &nb)
{
std::cout << "INFO: Reading " << xclbin_file_name << std::endl;
if(access(xclbin_file_name.c_str(), R_OK) != 0) {
printf("ERROR: %s xclbin not available please build\n", xclbin_file_name.c_str());
exit(EXIT_FAILURE);
}
//Loading XCL Bin into char buffer
std::cout << "Loading: '" << xclbin_file_name.c_str() << "'\n";
std::ifstream bin_file(xclbin_file_name.c_str(), std::ifstream::binary);
bin_file.seekg (0, bin_file.end);
nb = bin_file.tellg();
bin_file.seekg (0, bin_file.beg);
char *buf = new char [nb];
bin_file.read(buf, nb);
return buf;
}
};
int main(int argc, char* argv[])
{
long x, y;
BITMAPFILEHEADER bmpfhr; // BMPファイルのファイルヘッダ(for Read)
BITMAPINFOHEADER bmpihr; // BMPファイルのINFOヘッダ(for Read)
FILE *fbmpr, *fbmpw;
int32_t blue, green, red;
const char* xclbinFilename;
hls::stream<ap_axis<32,1,1,1> > ins_soft;
hls::stream<ap_axis<32,1,1,1> > outs_soft;
ap_axis<32,1,1,1> pix;
ap_axis<32,1,1,1> vals_soft;
if (argc==2) {
xclbinFilename = argv[1];
std::cout <<"Using FPGA binary file specfied through the command line: " << xclbinFilename << std::endl;
}
else {
xclbinFilename = "../streaming_lap_filter.xclbin";
std::cout << "No FPGA binary file specified through the command line, using:" << xclbinFilename <<std::endl;
}
if ((fbmpr = fopen("test.bmp", "rb")) == NULL){ // test.bmp をオープン
fprintf(stderr, "Can't open test.bmp by binary read mode\n");
exit(1);
}
// bmpヘッダの読み出し
fread(&bmpfhr.bfType, sizeof(uint16_t), 1, fbmpr);
fread(&bmpfhr.bfSize, sizeof(uint32_t), 1, fbmpr);
fread(&bmpfhr.bfReserved1, sizeof(uint16_t), 1, fbmpr);
fread(&bmpfhr.bfReserved2, sizeof(uint16_t), 1, fbmpr);
fread(&bmpfhr.bfOffBits, sizeof(uint32_t), 1, fbmpr);
fread(&bmpihr, sizeof(BITMAPINFOHEADER), 1, fbmpr);
// ピクセルを入れるメモリをアロケートする
std::vector<int32_t,aligned_allocator<int32_t>> rd_bmp(bmpihr.biWidth * bmpihr.biHeight);
std::vector<int32_t,aligned_allocator<int32_t>> hw_lapd(bmpihr.biWidth * bmpihr.biHeight);
size_t size_in_bytes = (bmpihr.biWidth * bmpihr.biHeight) * sizeof(int32_t);
// rd_bmp にBMPのピクセルを代入。その際に、行を逆転する必要がある
for (y=0; y<bmpihr.biHeight; y++){
for (x=0; x<bmpihr.biWidth; x++){
blue = fgetc(fbmpr);
green = fgetc(fbmpr);
red = fgetc(fbmpr);
rd_bmp[((bmpihr.biHeight-1)-y)*bmpihr.biWidth+x] = (blue & 0xff) | ((green & 0xff)<<8) | ((red & 0xff)<<16);
}
}
fclose(fbmpr);
std::vector<cl::Device> devices = xcl::get_xil_devices();
cl::Device device = devices[0];
devices.resize(1);
// Creating Context and Command Queue for selected device
cl::Context context(device);
cl::CommandQueue q(context, device, CL_QUEUE_PROFILING_ENABLE |
CL_QUEUE_OUT_OF_ORDER_EXEC_MODE_ENABLE);
// Load xclbin
std::cout << "Loading: '" << xclbinFilename << "'\n";
std::ifstream bin_file(xclbinFilename, std::ifstream::binary);
bin_file.seekg (0, bin_file.end);
unsigned nb = bin_file.tellg();
bin_file.seekg (0, bin_file.beg);
char *buf = new char [nb];
bin_file.read(buf, nb);
// Creating Program from Binary File
cl::Program::Binaries bins;
bins.push_back({buf,nb});
cl::Program program(context, devices, bins);
// This call will get the kernel object from program. A kernel is an
// OpenCL function that is executed on the FPGA.
cl::Kernel krnl_dma_read(program,"dma_read");
cl::Kernel krnl_lap_filter_dmaw(program,"krnl_lap_filter_dmaw");
// These commands will allocate memory on the Device. The cl::Buffer objects can
// be used to reference the memory locations on the device.
cl::Buffer rd_bmp_buf(context, CL_MEM_USE_HOST_PTR | CL_MEM_READ_ONLY,
size_in_bytes, rd_bmp.data());
cl::Buffer hw_lapd_buf(context, CL_MEM_USE_HOST_PTR | CL_MEM_READ_WRITE,
size_in_bytes, hw_lapd.data());
//set the kernel Arguments
krnl_dma_read.setArg(0,rd_bmp_buf);
krnl_dma_read.setArg(2,bmpihr.biWidth);
krnl_dma_read.setArg(3,bmpihr.biHeight);
krnl_lap_filter_dmaw.setArg(1,hw_lapd_buf);
krnl_lap_filter_dmaw.setArg(2,bmpihr.biWidth);
krnl_lap_filter_dmaw.setArg(3,bmpihr.biHeight);
// Data will be transferred from system memory over PCIe to the FPGA on-board
// DDR memory.
q.enqueueMigrateMemObjects({rd_bmp_buf},0/* 0 means from host*/);
struct timeval start_time, end_time;
gettimeofday(&start_time, NULL);
//Launch the Kernel
q.enqueueTask(krnl_dma_read);
q.enqueueTask(krnl_lap_filter_dmaw);
q.finish();
// The result of the previous kernel execution will need to be retrieved in
// order to view the results. This call will transfer the data from FPGA to
// source_results vector
q.enqueueMigrateMemObjects({hw_lapd_buf},CL_MIGRATE_MEM_OBJECT_HOST);
q.finish();
// 時間計測
gettimeofday(&end_time, NULL);
if (end_time.tv_usec < start_time.tv_usec) {
printf("total time = %ld.%06ld sec\n", end_time.tv_sec - start_time.tv_sec - 1, 1000000 + end_time.tv_usec - start_time.tv_usec);
}
else {
printf("total time = %ld.%06ld sec\n", end_time.tv_sec - start_time.tv_sec, end_time.tv_usec - start_time.tv_usec);
}
// ソフトウェアとハードウェアのチェック
// ins_soft に入力データを用意する
for(int i=0; i<5; i++){ // dummy data
pix.user = 0;
pix.data = int32_t(i);
ins_soft << pix;
}
for(int j=0; j < bmpihr.biHeight; j++){
for(int i=0; i < bmpihr.biWidth; i++){
pix.data = rd_bmp[(j*bmpihr.biWidth)+i];
if (j==0 && i==0) // 最初のデータの時に TUSER を 1 にする
pix.user = 1;
else
pix.user = 0;
if (i == bmpihr.biWidth-1) // 行の最後でTLASTをアサートする
pix.last = 1;
else
pix.last = 0;
ins_soft << pix;
}
}
lap_filter_axis_soft(ins_soft, outs_soft, bmpihr.biWidth, bmpihr.biHeight); // ソフトウェアのラプラシアン・フィルタ
// ハードウェアとソフトウェアのラプラシアン・フィルタの値のチェック
for (y=0; y<bmpihr.biHeight; y++){
for (x=0; x<bmpihr.biWidth; x++){
outs_soft >> vals_soft;
if (hw_lapd[y*bmpihr.biWidth+x] != vals_soft.data){
printf("ERROR HW and SW results mismatch x = %ld, y = %ld, HW = %d, SW = %d\n", x, y, int(hw_lapd[y*bmpihr.biWidth+x]), int(vals_soft.data));
//return(1);
}
}
}
printf("Success HW and SW results match\n");
// ハードウェアのラプラシアンフィルタの結果を temp_lap.bmp へ出力する
if ((fbmpw=fopen("temp_lap.bmp", "wb")) == NULL){
fprintf(stderr, "Can't open temp_lap.bmp by binary write mode\n");
exit(1);
}
// BMPファイルヘッダの書き込み
fwrite(&bmpfhr.bfType, sizeof(uint16_t), 1, fbmpw);
fwrite(&bmpfhr.bfSize, sizeof(uint32_t), 1, fbmpw);
fwrite(&bmpfhr.bfReserved1, sizeof(uint16_t), 1, fbmpw);
fwrite(&bmpfhr.bfReserved2, sizeof(uint16_t), 1, fbmpw);
fwrite(&bmpfhr.bfOffBits, sizeof(uint32_t), 1, fbmpw);
fwrite(&bmpihr, sizeof(BITMAPINFOHEADER), 1, fbmpw);
// RGB データの書き込み、逆順にする
for (y=0; y<bmpihr.biHeight; y++){
for (x=0; x<bmpihr.biWidth; x++){
blue = hw_lapd[((bmpihr.biHeight-1)-y)*bmpihr.biWidth+x] & 0xff;
green = (hw_lapd[((bmpihr.biHeight-1)-y)*bmpihr.biWidth+x] >> 8) & 0xff;
red = (hw_lapd[((bmpihr.biHeight-1)-y)*bmpihr.biWidth+x]>>16) & 0xff;
fputc(blue, fbmpw);
fputc(green, fbmpw);
fputc(red, fbmpw);
}
}
fclose(fbmpw);
return(0);
}
int lap_filter_axis_soft(hls::stream<ap_axis<32,1,1,1> >& ins, hls::stream<ap_axis<32,1,1,1> >& outs, int32_t width, int32_t height){
ap_axis<32,1,1,1> pix;
ap_axis<32,1,1,1> lap;
int32_t **line_buf;
int32_t pix_mat[3][3];
int32_t lap_fil_val;
int32_t i;
// line_buf の1次元目の配列をアロケートする
if ((line_buf =(int32_t **)malloc(sizeof(int32_t *) * 2)) == NULL){
fprintf(stderr, "Can't allocate line_buf[3][]\n");
exit(1);
}
// メモリをアロケートする
for (i=0; i<2; i++){
if ((line_buf[i]=(int32_t *)malloc(sizeof(int32_t) * width)) == NULL){
fprintf(stderr, "Can't allocate line_buf[%d]\n", i);
exit(1);
}
}
do { // user が 1になった時にフレームがスタートする
ins >> pix;
} while(pix.user == 0);
for (int y=0; y<height; y++){
for (int x=0; x<width; x++){
if (!(x==0 && y==0)) // 最初の入力はすでに入力されている
ins >> pix; // AXI4-Stream からの入力
for (int k=0; k<3; k++){
for (int m=0; m<2; m++){
pix_mat[k][m] = pix_mat[k][m+1];
}
}
pix_mat[0][2] = line_buf[0][x];
pix_mat[1][2] = line_buf[1][x];
int32_t y_val = conv_rgb2y_soft(pix.data);
pix_mat[2][2] = y_val;
line_buf[0][x] = line_buf[1][x]; // 行の入れ替え
line_buf[1][x] = y_val;
lap_fil_val = laplacian_fil_soft( pix_mat[0][0], pix_mat[0][1], pix_mat[0][2],
pix_mat[1][0], pix_mat[1][1], pix_mat[1][2],
pix_mat[2][0], pix_mat[2][1], pix_mat[2][2]);
lap.data = (lap_fil_val<<16)+(lap_fil_val<<8)+lap_fil_val; // RGB同じ値を入れる
if (x<2 || y<2) // 最初の2行とその他の行の最初の2列は無効データなので0とする
lap.data = 0;
if (x==0 && y==0) // 最初のデータでは、TUSERをアサートする
lap.user = 1;
else
lap.user = 0;
if (x == (width-1)) // 行の最後で TLAST をアサートする
lap.last = 1;
else
lap.last = 0;
outs << lap; // AXI4-Stream へ出力
}
}
for (i=0; i<2; i++)
free(line_buf[i]);
free(line_buf);
return 0;
}
// RGBからYへの変換
// RGBのフォーマットは、{8'd0, R(8bits), G(8bits), B(8bits)}, 1pixel = 32bits
// 輝度信号Yのみに変換する。変換式は、Y = 0.299R + 0.587G + 0.114B
// "YUVフォーマット及び YUV<->RGB変換"を参考にした。http://vision.kuee.kyoto-u.ac.jp/~hiroaki/firewire/yuv.html
// 2013/09/27 : float を止めて、すべてint にした
int32_t conv_rgb2y_soft(int32_t rgb){
int32_t r, g, b, y_f;
int32_t y;
b = rgb & 0xff;
g = (rgb>>8) & 0xff;
r = (rgb>>16) & 0xff;
y_f = 77*r + 150*g + 29*b; //y_f = 0.299*r + 0.587*g + 0.114*b;の係数に256倍した
y = y_f >> 8; // 256で割る
return(y);
}
// ラプラシアンフィルタ
// x0y0 x1y0 x2y0 -1 -1 -1
// x0y1 x1y1 x2y1 -1 8 -1
// x0y2 x1y2 x2y2 -1 -1 -1
int32_t laplacian_fil_soft(int32_t x0y0, int32_t x1y0, int32_t x2y0, int32_t x0y1, int32_t x1y1,
int32_t x2y1, int32_t x0y2, int32_t x1y2, int32_t x2y2)
{
int32_t y;
y = -x0y0 -x1y0 -x2y0 -x0y1 +8*x1y1 -x2y1 -x0y2 -x1y2 -x2y2;
if (y<0)
y = -y;
else if (y>255)
y = 255;
return(y);
}
--config ../src/krnl_stream_dmar_lap_dmaw.ini --dk chipscope:dma_read_1 --dk chipscope:krnl_lap_filter_dmaw_1:ins --dk chipscope:krnl_lap_filter_dmaw_1:outm --dk chipscope:krnl_lap_filter_dmaw_1:x_size
[connectivity]
stream_connect=dma_read_1.outs:krnl_lap_filter_dmaw_1.ins
// krnl_lap_filter_dmaw.cpp
// 2020/02/08 by marsee
#include <ap_int.h>
#include <hls_stream.h>
#include <ap_axi_sdata.h>
#include <stdint.h>
// RGBからYへの変換
// RGBのフォーマットは、{8'd0, R(8bits), G(8bits), B(8bits)}, 1pixel = 32bits
// 輝度信号Yのみに変換する。変換式は、Y = 0.299R + 0.587G + 0.114B
// "YUVフォーマット及び YUV<->RGB変換"を参考にした。http://vision.kuee.kyoto-u.ac.jp/~hiroaki/firewire/yuv.html
// 2013/09/27 : float を止めて、すべてint にした
int32_t conv_rgb2y(int32_t rgb){
int32_t r, g, b, y_f;
int32_t y;
b = rgb & 0xff;
g = (rgb>>8) & 0xff;
r = (rgb>>16) & 0xff;
y_f = 77*r + 150*g + 29*b; //y_f = 0.299*r + 0.587*g + 0.114*b;の係数に256倍した
y = y_f >> 8; // 256で割る
return(y);
}
// ラプラシアンフィルタ
// x0y0 x1y0 x2y0 -1 -1 -1
// x0y1 x1y1 x2y1 -1 8 -1
// x0y2 x1y2 x2y2 -1 -1 -1
int32_t laplacian_fil(int32_t x0y0, int32_t x1y0, int32_t x2y0, int32_t x0y1,
int32_t x1y1, int32_t x2y1, int32_t x0y2, int32_t x1y2, int32_t x2y2)
{
int32_t y;
y = -x0y0 -x1y0 -x2y0 -x0y1 +8*x1y1 -x2y1 -x0y2 -x1y2 -x2y2;
if (y<0)
y = -y;
else if (y>255)
y = 255;
return(y);
}
void krnl_lap_filter(hls::stream<ap_axiu<32,0,0,0> >& ins, hls::stream<ap_axiu<32,0,0,0> >& outs,
int32_t x_size, int32_t y_size){
ap_axiu<32,0,0,0> pix;
ap_axiu<32,0,0,0> lap;
int32_t line_buf[2][1920]; // supported HD resolution
#pragma HLS array_partition variable=line_buf block factor=2 dim=1
#pragma HLS resource variable=line_buf core=RAM_2P
int32_t pix_mat[3][3];
#pragma HLS array_partition variable=pix_mat complete
int32_t lap_fil_val;
LOOP_X : for (int y=0; y<y_size; y++){
#pragma HLS LOOP_TRIPCOUNT min=48 max=600
LOOP_Y : for (int x=0; x<x_size; x++){
#pragma HLS LOOP_TRIPCOUNT min=64 max=800
#pragma HLS PIPELINE II=1
ins >> pix; // AXI4-Stream からの入力
Loop4 : for (int k=0; k<3; k++){
Loop5 : for (int m=0; m<2; m++){
#pragma HLS UNROLL
pix_mat[k][m] = pix_mat[k][m+1];
}
}
pix_mat[0][2] = line_buf[0][x];
pix_mat[1][2] = line_buf[1][x];
int32_t y_val = conv_rgb2y(pix.data);
pix_mat[2][2] = y_val;
line_buf[0][x] = line_buf[1][x]; // 行の入れ替え
line_buf[1][x] = y_val;
lap_fil_val = laplacian_fil( pix_mat[0][0], pix_mat[0][1], pix_mat[0][2],
pix_mat[1][0], pix_mat[1][1], pix_mat[1][2],
pix_mat[2][0], pix_mat[2][1], pix_mat[2][2]);
lap.data = (lap_fil_val<<16)+(lap_fil_val<<8)+lap_fil_val; // RGB同じ値を入れる
if (x<2 || y<2) // 最初の2行とその他の行の最初の2列は無効データなので0とする
lap.data = 0;
if (x==(x_size-1) && y==(y_size-1)) // フレームの最後で TLAST をアサートする
lap.last = 1;
else
lap.last = 0;
outs << lap; // ストリームへ出力
}
}
LOOP_WAIT_LAST: while(pix.last == 0) { // last が 1 になるまで待つ
#pragma HLS PIPELINE II=1
#pragma HLS LOOP_TRIPCOUNT min=1 max=1 avg=1
ins >> pix;
};
}
void dma_write(hls::stream<ap_axiu<32,0,0,0> >& ins, volatile int32_t *outm,
int32_t x_size, int32_t y_size){
ap_axiu<32,0,0,0> pix;
LOOP_DWY: for(int y=0; y<y_size; y++){
#pragma HLS LOOP_TRIPCOUNT min=48 max=600
LOOP_DWX: for(int x=0; x<x_size; x++){
#pragma HLS LOOP_TRIPCOUNT min=64 max=800
#pragma HLS PIPELINE II=1
ins >> pix;
outm[x_size*y+x] = pix.data;
}
}
}
//extern "C" {
void krnl_lap_filter_dmaw(hls::stream<ap_axiu<32,0,0,0> >& ins, volatile int32_t *outm,
int32_t x_size, int32_t y_size){
#pragma HLS DATAFLOW
#pragma HLS INTERFACE m_axi depth=3072 port=outm offset=slave bundle=gmem
#pragma HLS INTERFACE axis register both port=ins
#pragma HLS INTERFACE s_axilite port=y_size bundle=control
#pragma HLS INTERFACE s_axilite port=x_size bundle=control
#pragma HLS INTERFACE s_axilite port=return bundle=control
hls::stream<ap_axiu<32,0,0,0> > lap_stream;
krnl_lap_filter(ins, lap_stream, x_size, y_size);
dma_write(lap_stream, outm, x_size, y_size);
}
//}
// krnl_lap_filter_dmaw_tb.cpp
// 2020/02/08 by marsee
#include "hls_opencv.h"
#include <ap_int.h>
#include <hls_stream.h>
#include <ap_axi_sdata.h>
void krnl_lap_filter_dmaw(hls::stream<ap_axiu<32,0,0,0> >& ins, volatile int32_t *outm,
int32_t x_size, int32_t y_size);
void krnl_lap_filter_soft(hls::stream<ap_axiu<32,0,0,0> >& ins, hls::stream<ap_axiu<32,0,0,0> >& outs,
int32_t x_size, int32_t y_size);
const char INPUT_BMP_FILE[] = "test.bmp";
const char OUTPUT_BMP_FILE[] = "lap.bmp";
int main(){
hls::stream<ap_axiu<32,0,0,0> > ins;
hls::stream<ap_axiu<32,0,0,0> > ins_soft;
hls::stream<ap_axiu<32,0,0,0> > outs_soft;
ap_axiu<32,0,0,0> pix;
ap_axiu<32,0,0,0> vals_soft;
// BMPファイルをMat に読み込む
cv::Mat img = cv::imread(INPUT_BMP_FILE);
// ピクセルを入れる領域の確保
std::vector<int32_t> rd_bmp(sizeof(int32_t)*img.cols*img.rows);
std::vector<int32_t> hw_lap(sizeof(int32_t)*(img.cols)*(img.rows));
std::vector<int32_t> sw_lap(sizeof(int32_t)*(img.cols)*(img.rows));
// rd_bmp にBMPのピクセルを代入
cv::Mat_<cv::Vec3b> dst_vec3b = cv::Mat_<cv::Vec3b>(img);
for (int y=0; y<img.rows; y++){
for (int x=0; x<img.cols; x++){
cv::Vec3b pixel;
pixel = dst_vec3b(y,x);
rd_bmp[y*img.cols+x] = (pixel[0] & 0xff) | ((pixel[1] & 0xff)<<8) | ((pixel[2] & 0xff)<<16);
// blue - pixel[0]; green - pixel[1]; red - pixel[2];
}
}
// ins に入力データを用意する
for(int j=0; j < img.rows; j++){
for(int i=0; i < img.cols; i++){
pix.data = (ap_int<32>)rd_bmp[(j*img.cols)+i];
if ((i==img.cols-1) && (j==img.rows-1)) // フレームの最後で last をアサートする
pix.last = 1;
else
pix.last = 0;
ins << pix;
ins_soft << pix;
}
}
krnl_lap_filter_dmaw(ins, hw_lap.data(), img.cols, img.rows); // ハードウェアのソーベルフィルタ
krnl_lap_filter_soft(ins_soft, outs_soft,img.cols, img.rows); // ソフトウェアのソーベルフィルタ
// ハードウェアとソフトウェアのソーベルフィルタの値のチェック
for (int y=0; y<img.rows; y++){
for (int x=0; x<img.cols; x++){
ap_int<32> val = hw_lap[y*img.cols+x];
outs_soft >> vals_soft;
ap_int<32> val_soft = vals_soft.data;
if (val != val_soft){
printf("ERROR HW and SW results mismatch x = %ld, y = %ld, HW = %x, SW = %x\n",
x, y, val, val_soft);
return(1);
}
}
}
printf("Success HW and SW results match\n");
const int lap_rows = img.rows;
const int lap_cols = img.cols;
cv::Mat wbmpf(lap_rows, lap_cols, CV_8UC3);
// wbmpf にラプラシアンフィルタ処理後の画像を入力
cv::Mat_<cv::Vec3b> lap_vec3b = cv::Mat_<cv::Vec3b>(wbmpf);
for (int y=0; y<wbmpf.rows; y++){
for (int x=0; x<wbmpf.cols; x++){
cv::Vec3b pixel;
pixel = lap_vec3b(y,x);
int32_t rgb = hw_lap[y*wbmpf.cols+x];
pixel[0] = (rgb & 0xff); // blue
pixel[1] = (rgb & 0xff00) >> 8; // green
pixel[2] = (rgb & 0xff0000) >> 16; // red
lap_vec3b(y,x) = pixel;
}
}
// ハードウェアのソーベルフィルタの結果を bmp ファイルへ出力する
cv::imwrite(OUTPUT_BMP_FILE, wbmpf);
return(0);
}
// RGBからYへの変換
// RGBのフォーマットは、{8'd0, R(8bits), G(8bits), B(8bits)}, 1pixel = 32bits
// 輝度信号Yのみに変換する。変換式は、Y = 0.299R + 0.587G + 0.114B
// "YUVフォーマット及び YUV<->RGB変換"を参考にした。http://vision.kuee.kyoto-u.ac.jp/~hiroaki/firewire/yuv.html
// 2013/09/27 : float を止めて、すべてint にした
int32_t conv_rgb2y_soft(int32_t rgb){
int32_t r, g, b, y_f;
int32_t y;
b = rgb & 0xff;
g = (rgb>>8) & 0xff;
r = (rgb>>16) & 0xff;
y_f = 77*r + 150*g + 29*b; //y_f = 0.299*r + 0.587*g + 0.114*b;の係数に256倍した
y = y_f >> 8; // 256で割る
return(y);
}
// ラプラシアンフィルタ
// x0y0 x1y0 x2y0 -1 -1 -1
// x0y1 x1y1 x2y1 -1 8 -1
// x0y2 x1y2 x2y2 -1 -1 -1
int32_t laplacian_fil_soft(int32_t x0y0, int32_t x1y0, int32_t x2y0, int32_t x0y1,
int32_t x1y1, int32_t x2y1, int32_t x0y2, int32_t x1y2, int32_t x2y2)
{
int32_t y;
y = -x0y0 -x1y0 -x2y0 -x0y1 +8*x1y1 -x2y1 -x0y2 -x1y2 -x2y2;
if (y<0)
y = -y;
else if (y>255)
y = 255;
return(y);
}
void krnl_lap_filter_soft(hls::stream<ap_axiu<32,0,0,0> >& ins, hls::stream<ap_axiu<32,0,0,0> >& outs,
int32_t x_size, int32_t y_size){
ap_axiu<32,0,0,0> pix;
ap_axiu<32,0,0,0> lap;
int32_t line_buf[2][1920]; // supported HD resolution
int32_t pix_mat[3][3];
int32_t lap_fil_val;
LOOP_X : for (int y=0; y<y_size; y++){
LOOP_Y : for (int x=0; x<x_size; x++){
ins >> pix; // AXI4-Stream からの入力
Loop4 : for (int k=0; k<3; k++){
Loop5 : for (int m=0; m<2; m++){
pix_mat[k][m] = pix_mat[k][m+1];
}
}
pix_mat[0][2] = line_buf[0][x];
pix_mat[1][2] = line_buf[1][x];
int32_t y_val = conv_rgb2y_soft(pix.data);
pix_mat[2][2] = y_val;
line_buf[0][x] = line_buf[1][x]; // 行の入れ替え
line_buf[1][x] = y_val;
lap_fil_val = laplacian_fil_soft( pix_mat[0][0], pix_mat[0][1], pix_mat[0][2],
pix_mat[1][0], pix_mat[1][1], pix_mat[1][2],
pix_mat[2][0], pix_mat[2][1], pix_mat[2][2]);
lap.data = (lap_fil_val<<16)+(lap_fil_val<<8)+lap_fil_val; // RGB同じ値を入れる
if (x<2 || y<2) // 最初の2行とその他の行の最初の2列は無効データなので0とする
lap.data = 0;
if (x==(x_size-1) && y==(y_size-1)) // フレームの最後で TLAST をアサートする
lap.last = 1;
else
lap.last = 0;
outs << lap; // ストリームへ出力
}
}
LOOP_WAIT_LAST: while(pix.last == 0) { // last が 1 になるまで待つ
ins >> pix;
};
}
// krnl_lap_filter2.cpp
// 2020/02/07 by marsee
#include <ap_int.h>
#include <hls_stream.h>
#include <ap_axi_sdata.h>
#include <stdint.h>
// RGBからYへの変換
// RGBのフォーマットは、{8'd0, R(8bits), G(8bits), B(8bits)}, 1pixel = 32bits
// 輝度信号Yのみに変換する。変換式は、Y = 0.299R + 0.587G + 0.114B
// "YUVフォーマット及び YUV<->RGB変換"を参考にした。http://vision.kuee.kyoto-u.ac.jp/~hiroaki/firewire/yuv.html
// 2013/09/27 : float を止めて、すべてint にした
int32_t conv_rgb2y(int32_t rgb){
int32_t r, g, b, y_f;
int32_t y;
b = rgb & 0xff;
g = (rgb>>8) & 0xff;
r = (rgb>>16) & 0xff;
y_f = 77*r + 150*g + 29*b; //y_f = 0.299*r + 0.587*g + 0.114*b;の係数に256倍した
y = y_f >> 8; // 256で割る
return(y);
}
// ラプラシアンフィルタ
// x0y0 x1y0 x2y0 -1 -1 -1
// x0y1 x1y1 x2y1 -1 8 -1
// x0y2 x1y2 x2y2 -1 -1 -1
int32_t laplacian_fil(int32_t x0y0, int32_t x1y0, int32_t x2y0, int32_t x0y1,
int32_t x1y1, int32_t x2y1, int32_t x0y2, int32_t x1y2, int32_t x2y2)
{
int32_t y;
y = -x0y0 -x1y0 -x2y0 -x0y1 +8*x1y1 -x2y1 -x0y2 -x1y2 -x2y2;
if (y<0)
y = -y;
else if (y>255)
y = 255;
return(y);
}
extern "C" {
void krnl_lap_filter(hls::stream<ap_axiu<32,0,0,0> >& ins, hls::stream<ap_axiu<32,0,0,0> >& outs,
int32_t x_size, int32_t y_size){
#pragma HLS DATAFLOW
#pragma HLS INTERFACE s_axilite port=y_size bundle=control
#pragma HLS INTERFACE s_axilite port=x_size bundle=control
#pragma HLS INTERFACE axis register both port=outs
#pragma HLS INTERFACE axis register both port=ins
#pragma HLS INTERFACE s_axilite port=return bundle=control
hls::stream<ap_axiu<32,0,0,0> > buf;
#pragma HLS STREAM variable=buf depth=4096 dim=1
ap_axiu<32,0,0,0> pix;
ap_axiu<32,0,0,0> lap;
int32_t line_buf[2][1920]; // supported HD resolution
#pragma HLS array_partition variable=line_buf block factor=2 dim=1
#pragma HLS resource variable=line_buf core=RAM_2P
int32_t pix_mat[3][3];
#pragma HLS array_partition variable=pix_mat complete
int32_t lap_fil_val;
LOOP_X1 : for (int y=0; y<y_size; y++){
#pragma HLS LOOP_TRIPCOUNT min=48 max=600
LOOP_Y1 : for (int x=0; x<x_size; x++){
#pragma HLS LOOP_TRIPCOUNT min=64 max=800
#pragma HLS PIPELINE II=1
ins >> pix;
buf << pix;
}
}
LOOP_X2 : for (int y=0; y<y_size; y++){
#pragma HLS LOOP_TRIPCOUNT min=48 max=600
LOOP_Y2 : for (int x=0; x<x_size; x++){
#pragma HLS LOOP_TRIPCOUNT min=64 max=800
#pragma HLS PIPELINE II=1
buf >> pix; // AXI4-Stream からの入力
Loop4 : for (int k=0; k<3; k++){
Loop5 : for (int m=0; m<2; m++){
#pragma HLS UNROLL
pix_mat[k][m] = pix_mat[k][m+1];
}
}
pix_mat[0][2] = line_buf[0][x];
pix_mat[1][2] = line_buf[1][x];
int32_t y_val = conv_rgb2y(pix.data);
pix_mat[2][2] = y_val;
line_buf[0][x] = line_buf[1][x]; // 行の入れ替え
line_buf[1][x] = y_val;
lap_fil_val = laplacian_fil( pix_mat[0][0], pix_mat[0][1], pix_mat[0][2],
pix_mat[1][0], pix_mat[1][1], pix_mat[1][2],
pix_mat[2][0], pix_mat[2][1], pix_mat[2][2]);
lap.data = (lap_fil_val<<16)+(lap_fil_val<<8)+lap_fil_val; // RGB同じ値を入れる
if (x<2 || y<2) // 最初の2行とその他の行の最初の2列は無効データなので0とする
lap.data = 0;
if (x==(x_size-1) && y==(y_size-1)) // フレームの最後で TLAST をアサートする
lap.last = 1;
else
lap.last = 0;
outs << lap; // ストリームへ出力
}
}
/*LOOP_WAIT_LAST: while(pix.last == 0) { // last が 1 になるまで待つ
#pragma HLS PIPELINE II=1
#pragma HLS LOOP_TRIPCOUNT min=1 max=1 avg=1
ins >> pix;
}; */
}
}
--config ../src/krnl_stream_dmar_lap_dmaw.ini --dk chipscope:dma_read_1 --dk chipscope:dma_write_1 --dk chipscope:krnl_lap_filter_1:ins --dk chipscope:krnl_lap_filter_1:outs --dk chipscope:krnl_lap_filter_1:x_size
[connectivity]
stream_connect=dma_read_1.outs:krnl_lap_filter_1.ins
stream_connect=krnl_lap_filter_1.outs:dma_write_1.ins
を入力して、Apply and Close ボタンをクリックする。--config ../src/krnl_stream_dmar_lap_dmaw.ini
// krnl_streaming_lap_host.cpp
// 2020/02/02 by marsee
//
// Vitis-Tutorials/docs/mixing-c-rtl-kernels/reference-files/src/host/host_step1.cpp のコードを引用します
// https://github.com/Xilinx/Vitis-Tutorials/blob/master/docs/mixing-c-rtl-kernels/reference-files/src/host/host_step1.cpp
#define CL_HPP_CL_1_2_DEFAULT_BUILD
#define CL_HPP_TARGET_OPENCL_VERSION 120
#define CL_HPP_MINIMUM_OPENCL_VERSION 120
#define CL_HPP_ENABLE_PROGRAM_CONSTRUCTION_FROM_ARRAY_COMPATIBILITY 1
#define CL_USE_DEPRECATED_OPENCL_1_2_APIS
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/time.h>
#include <vector>
#include <CL/cl2.hpp>
#include <iostream>
#include <fstream>
#include <CL/cl_ext_xilinx.h>
#include <unistd.h>
#include <limits.h>
#include <sys/stat.h>
#include <ap_int.h>
#include <hls_stream.h>
#include <ap_axi_sdata.h>
#include "bmp_header.h"
int laplacian_fil_soft(int32_t x0y0, int32_t x1y0, int32_t x2y0, int32_t x0y1, int32_t x1y1, int32_t x2y1, int32_t x0y2, int32_t x1y2, int32_t x2y2);
int32_t conv_rgb2y_soft(int32_t rgb);
int32_t lap_filter_axis_soft(hls::stream<ap_axis<32,1,1,1> >& ins, hls::stream<ap_axis<32,1,1,1> >& outs, int32_t width, int32_t height); // software
static const std::string error_message =
"Error: Result mismatch:\n"
"i = %d CPU result = %d Device result = %d\n";
//Some Library functions to be used.
template <typename T>
struct aligned_allocator
{
using value_type = T;
T* allocate(std::size_t num)
{
void* ptr = nullptr;
if (posix_memalign(&ptr,4096,num*sizeof(T)))
throw std::bad_alloc();
return reinterpret_cast<T*>(ptr);
}
void deallocate(T* p, std::size_t num)
{
free(p);
}
};
#define OCL_CHECK(error,call) \
call; \
if (error != CL_SUCCESS) { \
printf("%s:%d Error calling " #call ", error code is: %d\n", \
__FILE__,__LINE__, error); \
exit(EXIT_FAILURE); \
}
namespace xcl {
std::vector<cl::Device> get_devices(const std::string& vendor_name) {
size_t i;
cl_int err;
std::vector<cl::Platform> platforms;
OCL_CHECK(err, err = cl::Platform::get(&platforms));
cl::Platform platform;
for (i = 0 ; i < platforms.size(); i++){
platform = platforms[i];
OCL_CHECK(err, std::string platformName = platform.getInfo<CL_PLATFORM_NAME>(&err));
if (platformName == vendor_name){
std::cout << "Found Platform" << std::endl;
std::cout << "Platform Name: " << platformName.c_str() << std::endl;
break;
}
}
if (i == platforms.size()) {
std::cout << "Error: Failed to find Xilinx platform" << std::endl;
exit(EXIT_FAILURE);
}
//Getting ACCELERATOR Devices and selecting 1st such device
std::vector<cl::Device> devices;
OCL_CHECK(err, err = platform.getDevices(CL_DEVICE_TYPE_ACCELERATOR, &devices));
return devices;
}
std::vector<cl::Device> get_xil_devices() {
return get_devices("Xilinx");
}
char* read_binary_file(const std::string &xclbin_file_name, unsigned &nb)
{
std::cout << "INFO: Reading " << xclbin_file_name << std::endl;
if(access(xclbin_file_name.c_str(), R_OK) != 0) {
printf("ERROR: %s xclbin not available please build\n", xclbin_file_name.c_str());
exit(EXIT_FAILURE);
}
//Loading XCL Bin into char buffer
std::cout << "Loading: '" << xclbin_file_name.c_str() << "'\n";
std::ifstream bin_file(xclbin_file_name.c_str(), std::ifstream::binary);
bin_file.seekg (0, bin_file.end);
nb = bin_file.tellg();
bin_file.seekg (0, bin_file.beg);
char *buf = new char [nb];
bin_file.read(buf, nb);
return buf;
}
};
int main(int argc, char* argv[])
{
long x, y;
BITMAPFILEHEADER bmpfhr; // BMPファイルのファイルヘッダ(for Read)
BITMAPINFOHEADER bmpihr; // BMPファイルのINFOヘッダ(for Read)
FILE *fbmpr, *fbmpw;
int32_t blue, green, red;
const char* xclbinFilename;
hls::stream<ap_axis<32,1,1,1> > ins_soft;
hls::stream<ap_axis<32,1,1,1> > outs_soft;
ap_axis<32,1,1,1> pix;
ap_axis<32,1,1,1> vals_soft;
if (argc==2) {
xclbinFilename = argv[1];
std::cout <<"Using FPGA binary file specfied through the command line: " << xclbinFilename << std::endl;
}
else {
xclbinFilename = "../streaming_lap_filter.xclbin";
std::cout << "No FPGA binary file specified through the command line, using:" << xclbinFilename <<std::endl;
}
if ((fbmpr = fopen("test.bmp", "rb")) == NULL){ // test.bmp をオープン
fprintf(stderr, "Can't open test.bmp by binary read mode\n");
exit(1);
}
// bmpヘッダの読み出し
fread(&bmpfhr.bfType, sizeof(uint16_t), 1, fbmpr);
fread(&bmpfhr.bfSize, sizeof(uint32_t), 1, fbmpr);
fread(&bmpfhr.bfReserved1, sizeof(uint16_t), 1, fbmpr);
fread(&bmpfhr.bfReserved2, sizeof(uint16_t), 1, fbmpr);
fread(&bmpfhr.bfOffBits, sizeof(uint32_t), 1, fbmpr);
fread(&bmpihr, sizeof(BITMAPINFOHEADER), 1, fbmpr);
// ピクセルを入れるメモリをアロケートする
std::vector<int32_t,aligned_allocator<int32_t>> rd_bmp(bmpihr.biWidth * bmpihr.biHeight);
std::vector<int32_t,aligned_allocator<int32_t>> hw_lapd(bmpihr.biWidth * bmpihr.biHeight);
size_t size_in_bytes = (bmpihr.biWidth * bmpihr.biHeight) * sizeof(int32_t);
// rd_bmp にBMPのピクセルを代入。その際に、行を逆転する必要がある
for (y=0; y<bmpihr.biHeight; y++){
for (x=0; x<bmpihr.biWidth; x++){
blue = fgetc(fbmpr);
green = fgetc(fbmpr);
red = fgetc(fbmpr);
rd_bmp[((bmpihr.biHeight-1)-y)*bmpihr.biWidth+x] = (blue & 0xff) | ((green & 0xff)<<8) | ((red & 0xff)<<16);
}
}
fclose(fbmpr);
std::vector<cl::Device> devices = xcl::get_xil_devices();
cl::Device device = devices[0];
devices.resize(1);
// Creating Context and Command Queue for selected device
cl::Context context(device);
cl::CommandQueue q(context, device, CL_QUEUE_PROFILING_ENABLE);
// Load xclbin
printf("a"); fflush(stdout);
std::cout << "Loading: '" << xclbinFilename << "'\n";
printf("b"); fflush(stdout);
std::ifstream bin_file(xclbinFilename, std::ifstream::binary);
printf("b"); fflush(stdout);
bin_file.seekg (0, bin_file.end);
printf("b"); fflush(stdout);
unsigned nb = bin_file.tellg();
printf("b"); fflush(stdout);
bin_file.seekg (0, bin_file.beg);
printf("b"); fflush(stdout);
char *buf = new char [nb];
bin_file.read(buf, nb);
printf("b"); fflush(stdout);
// Creating Program from Binary File
cl::Program::Binaries bins;
bins.push_back({buf,nb});
cl::Program program(context, devices, bins);
printf("b"); fflush(stdout);
// This call will get the kernel object from program. A kernel is an
// OpenCL function that is executed on the FPGA.
cl::Kernel krnl_dma_read(program,"dma_read");
cl::Kernel krnl_lap_filter(program,"krnl_lap_filter");
cl::Kernel krnl_dma_write(program,"dma_write");
printf("c"); fflush(stdout);
// These commands will allocate memory on the Device. The cl::Buffer objects can
// be used to reference the memory locations on the device.
cl::Buffer rd_bmp_buf(context, CL_MEM_USE_HOST_PTR | CL_MEM_READ_ONLY,
size_in_bytes, rd_bmp.data());
cl::Buffer hw_lapd_buf(context, CL_MEM_USE_HOST_PTR | CL_MEM_READ_WRITE,
size_in_bytes, hw_lapd.data());
//set the kernel Arguments
krnl_dma_read.setArg(0,rd_bmp_buf);
krnl_dma_read.setArg(2,bmpihr.biWidth);
krnl_dma_read.setArg(3,bmpihr.biHeight);
krnl_lap_filter.setArg(2,bmpihr.biWidth);
krnl_lap_filter.setArg(3,bmpihr.biHeight);
krnl_dma_write.setArg(1,hw_lapd_buf);
krnl_dma_write.setArg(2,bmpihr.biWidth);
krnl_dma_write.setArg(3,bmpihr.biHeight);
printf("e"); fflush(stdout);
// Data will be transferred from system memory over PCIe to the FPGA on-board
// DDR memory.
q.enqueueMigrateMemObjects({rd_bmp_buf},0/* 0 means from host*/);
printf("d"); fflush(stdout);
struct timeval start_time, end_time;
gettimeofday(&start_time, NULL);
//Launch the Kernel
q.enqueueTask(krnl_dma_read);
q.enqueueTask(krnl_lap_filter);
q.enqueueTask(krnl_dma_write);
printf("f"); fflush(stdout);
q.finish();
printf("h"); fflush(stdout);
q.finish();
printf("h"); fflush(stdout);
// The result of the previous kernel execution will need to be retrieved in
// order to view the results. This call will transfer the data from FPGA to
// source_results vector
q.enqueueMigrateMemObjects({hw_lapd_buf},CL_MIGRATE_MEM_OBJECT_HOST);
printf("g"); fflush(stdout);
q.finish();
printf("h"); fflush(stdout);
// 時間計測
gettimeofday(&end_time, NULL);
printf("i"); fflush(stdout);
if (end_time.tv_usec < start_time.tv_usec) {
printf("total time = %ld.%06ld sec\n", end_time.tv_sec - start_time.tv_sec - 1, 1000000 + end_time.tv_usec - start_time.tv_usec);
}
else {
printf("total time = %ld.%06ld sec\n", end_time.tv_sec - start_time.tv_sec, end_time.tv_usec - start_time.tv_usec);
}
// ソフトウェアとハードウェアのチェック
// ins_soft に入力データを用意する
for(int i=0; i<5; i++){ // dummy data
pix.user = 0;
pix.data = int32_t(i);
ins_soft << pix;
}
for(int j=0; j < bmpihr.biHeight; j++){
for(int i=0; i < bmpihr.biWidth; i++){
pix.data = rd_bmp[(j*bmpihr.biWidth)+i];
if (j==0 && i==0) // 最初のデータの時に TUSER を 1 にする
pix.user = 1;
else
pix.user = 0;
if (i == bmpihr.biWidth-1) // 行の最後でTLASTをアサートする
pix.last = 1;
else
pix.last = 0;
ins_soft << pix;
}
}
lap_filter_axis_soft(ins_soft, outs_soft, bmpihr.biWidth, bmpihr.biHeight); // ソフトウェアのラプラシアン・フィルタ
// ハードウェアとソフトウェアのラプラシアン・フィルタの値のチェック
for (y=0; y<bmpihr.biHeight; y++){
for (x=0; x<bmpihr.biWidth; x++){
outs_soft >> vals_soft;
if (hw_lapd[y*bmpihr.biWidth+x] != vals_soft.data){
printf("ERROR HW and SW results mismatch x = %ld, y = %ld, HW = %d, SW = %d\n", x, y, int(hw_lapd[y*bmpihr.biWidth+x]), int(vals_soft.data));
//return(1);
}
}
}
printf("Success HW and SW results match\n");
// ハードウェアのラプラシアンフィルタの結果を temp_lap.bmp へ出力する
if ((fbmpw=fopen("temp_lap.bmp", "wb")) == NULL){
fprintf(stderr, "Can't open temp_lap.bmp by binary write mode\n");
exit(1);
}
// BMPファイルヘッダの書き込み
fwrite(&bmpfhr.bfType, sizeof(uint16_t), 1, fbmpw);
fwrite(&bmpfhr.bfSize, sizeof(uint32_t), 1, fbmpw);
fwrite(&bmpfhr.bfReserved1, sizeof(uint16_t), 1, fbmpw);
fwrite(&bmpfhr.bfReserved2, sizeof(uint16_t), 1, fbmpw);
fwrite(&bmpfhr.bfOffBits, sizeof(uint32_t), 1, fbmpw);
fwrite(&bmpihr, sizeof(BITMAPINFOHEADER), 1, fbmpw);
// RGB データの書き込み、逆順にする
for (y=0; y<bmpihr.biHeight; y++){
for (x=0; x<bmpihr.biWidth; x++){
blue = hw_lapd[((bmpihr.biHeight-1)-y)*bmpihr.biWidth+x] & 0xff;
green = (hw_lapd[((bmpihr.biHeight-1)-y)*bmpihr.biWidth+x] >> 8) & 0xff;
red = (hw_lapd[((bmpihr.biHeight-1)-y)*bmpihr.biWidth+x]>>16) & 0xff;
fputc(blue, fbmpw);
fputc(green, fbmpw);
fputc(red, fbmpw);
}
}
fclose(fbmpw);
return(0);
}
int lap_filter_axis_soft(hls::stream<ap_axis<32,1,1,1> >& ins, hls::stream<ap_axis<32,1,1,1> >& outs, int32_t width, int32_t height){
ap_axis<32,1,1,1> pix;
ap_axis<32,1,1,1> lap;
int32_t **line_buf;
int32_t pix_mat[3][3];
int32_t lap_fil_val;
int32_t i;
// line_buf の1次元目の配列をアロケートする
if ((line_buf =(int32_t **)malloc(sizeof(int32_t *) * 2)) == NULL){
fprintf(stderr, "Can't allocate line_buf[3][]\n");
exit(1);
}
// メモリをアロケートする
for (i=0; i<2; i++){
if ((line_buf[i]=(int32_t *)malloc(sizeof(int32_t) * width)) == NULL){
fprintf(stderr, "Can't allocate line_buf[%d]\n", i);
exit(1);
}
}
do { // user が 1になった時にフレームがスタートする
ins >> pix;
} while(pix.user == 0);
for (int y=0; y<height; y++){
for (int x=0; x<width; x++){
if (!(x==0 && y==0)) // 最初の入力はすでに入力されている
ins >> pix; // AXI4-Stream からの入力
for (int k=0; k<3; k++){
for (int m=0; m<2; m++){
pix_mat[k][m] = pix_mat[k][m+1];
}
}
pix_mat[0][2] = line_buf[0][x];
pix_mat[1][2] = line_buf[1][x];
int32_t y_val = conv_rgb2y_soft(pix.data);
pix_mat[2][2] = y_val;
line_buf[0][x] = line_buf[1][x]; // 行の入れ替え
line_buf[1][x] = y_val;
lap_fil_val = laplacian_fil_soft( pix_mat[0][0], pix_mat[0][1], pix_mat[0][2],
pix_mat[1][0], pix_mat[1][1], pix_mat[1][2],
pix_mat[2][0], pix_mat[2][1], pix_mat[2][2]);
lap.data = (lap_fil_val<<16)+(lap_fil_val<<8)+lap_fil_val; // RGB同じ値を入れる
if (x<2 || y<2) // 最初の2行とその他の行の最初の2列は無効データなので0とする
lap.data = 0;
if (x==0 && y==0) // 最初のデータでは、TUSERをアサートする
lap.user = 1;
else
lap.user = 0;
if (x == (width-1)) // 行の最後で TLAST をアサートする
lap.last = 1;
else
lap.last = 0;
outs << lap; // AXI4-Stream へ出力
}
}
for (i=0; i<2; i++)
free(line_buf[i]);
free(line_buf);
return 0;
}
// RGBからYへの変換
// RGBのフォーマットは、{8'd0, R(8bits), G(8bits), B(8bits)}, 1pixel = 32bits
// 輝度信号Yのみに変換する。変換式は、Y = 0.299R + 0.587G + 0.114B
// "YUVフォーマット及び YUV<->RGB変換"を参考にした。http://vision.kuee.kyoto-u.ac.jp/~hiroaki/firewire/yuv.html
// 2013/09/27 : float を止めて、すべてint にした
int32_t conv_rgb2y_soft(int32_t rgb){
int32_t r, g, b, y_f;
int32_t y;
b = rgb & 0xff;
g = (rgb>>8) & 0xff;
r = (rgb>>16) & 0xff;
y_f = 77*r + 150*g + 29*b; //y_f = 0.299*r + 0.587*g + 0.114*b;の係数に256倍した
y = y_f >> 8; // 256で割る
return(y);
}
// ラプラシアンフィルタ
// x0y0 x1y0 x2y0 -1 -1 -1
// x0y1 x1y1 x2y1 -1 8 -1
// x0y2 x1y2 x2y2 -1 -1 -1
int32_t laplacian_fil_soft(int32_t x0y0, int32_t x1y0, int32_t x2y0, int32_t x0y1, int32_t x1y1,
int32_t x2y1, int32_t x0y2, int32_t x1y2, int32_t x2y2)
{
int32_t y;
y = -x0y0 -x1y0 -x2y0 -x0y1 +8*x1y1 -x2y1 -x0y2 -x1y2 -x2y2;
if (y<0)
y = 0;
else if (y>255)
y = 255;
return(y);
}
// krnl_dma_read2.cpp
// 2020/01/31 by marsee
#include <stdint.h>
#include <ap_int.h>
#include <hls_stream.h>
#include <ap_axi_sdata.h>
#define X_SIZE 64
#define Y_SIZE 48
//extern "C" {
void dma_read2(volatile int32_t *inm, hls::stream<ap_axiu<32,0,0,0> >& outs){
#pragma HLS INTERFACE s_axilite port=return bundle=control
#pragma HLS INTERFACE axis register both port=outs
#pragma HLS INTERFACE m_axi depth=3072 port=inm offset=slave bundle=gmem
ap_axiu<32,0,0,0> pix;
LOOP_DRY: for(int y=0; y<Y_SIZE; y++){
LOOP_DRX: for(int x=0; x<X_SIZE; x++){
#pragma HLS PIPELINE II=1
pix.data = inm[X_SIZE*y+x];
if(x==(X_SIZE-1) && y==(Y_SIZE-1))
pix.last = 1;
else
pix.last = 0;
outs << pix;
}
}
}
//}
// krnl_dma_read2_tb.cpp
// 2020/01/31 by marsee
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <ap_int.h>
#include <hls_stream.h>
#include <iostream>
#include <fstream>
#include <ap_axi_sdata.h>
#include "bmp_header.h"
void dma_read2(volatile int32_t *inm, hls::stream<ap_axiu<32,0,0,0> >& outs);
int main(){
BITMAPFILEHEADER bmpfhr; // BMPファイルのファイルヘッダ(for Read)
BITMAPINFOHEADER bmpihr; // BMPファイルのINFOヘッダ(for Read)
FILE *fbmpr, *fbmpw;
int32_t *rd_bmp, *dmar;
int32_t blue, green, red;
hls::stream<ap_axiu<32,0,0,0> > outs;
ap_axiu<32,0,0,0> vals;
if ((fbmpr = fopen("test.bmp", "rb")) == NULL){ // test.bmp をオープン
fprintf(stderr, "Can't open test.bmp by binary read mode\n");
exit(1);
}
// bmpヘッダの読み出し
fread(&bmpfhr.bfType, sizeof(uint16_t), 1, fbmpr);
fread(&bmpfhr.bfSize, sizeof(uint32_t), 1, fbmpr);
fread(&bmpfhr.bfReserved1, sizeof(uint16_t), 1, fbmpr);
fread(&bmpfhr.bfReserved2, sizeof(uint16_t), 1, fbmpr);
fread(&bmpfhr.bfOffBits, sizeof(uint32_t), 1, fbmpr);
fread(&bmpihr, sizeof(BITMAPINFOHEADER), 1, fbmpr);
// ピクセルを入れるメモリをアロケートする
if ((rd_bmp =(int32_t *)malloc(sizeof(int32_t) * (bmpihr.biWidth * bmpihr.biHeight))) == NULL){
fprintf(stderr, "Can't allocate rd_bmp memory\n");
exit(1);
}
if ((dmar =(int32_t *)malloc(sizeof(int32_t) * (bmpihr.biWidth * bmpihr.biHeight))) == NULL){
fprintf(stderr, "Can't allocate hw_lapd memory\n");
exit(1);
}
// rd_bmp にBMPのピクセルを代入。その際に、行を逆転する必要がある
for(int y=0; y<bmpihr.biHeight; y++){
for(int x=0; x<bmpihr.biWidth; x++){
blue = fgetc(fbmpr);
green = fgetc(fbmpr);
red = fgetc(fbmpr);
rd_bmp[((bmpihr.biHeight-1)-y)*bmpihr.biWidth+x] = (int32_t)((blue & 0xff) | ((green & 0xff)<<8) | ((red & 0xff)<<16));
}
}
fclose(fbmpr);
dma_read2(rd_bmp, outs);
// DMAされた値のチェック
for(int y=0; y<bmpihr.biHeight; y++){
for(int x=0; x<bmpihr.biWidth; x++){
outs >> vals;
dmar[(y*bmpihr.biWidth)+x] = (int32_t)vals.data;
if ((int32_t)vals.data != rd_bmp[(y*bmpihr.biWidth)+x]){
printf("ERROR HW and SW results mismatch x = %ld, y = %ld, DMAR = %d, ORG = %d\n", x, y, (int)vals.data, (int)rd_bmp[(y*bmpihr.biWidth)+x]);
return(1);
}
}
}
std::cout << "Success DMA READ results match" << std::endl;
std::cout << std::endl;
// DMA_Read の結果を dma_read.bmp へ出力する
if ((fbmpw=fopen("dma_read.bmp", "wb")) == NULL){
fprintf(stderr, "Can't open temp_lap.bmp by binary write mode\n");
exit(1);
}
// BMPファイルヘッダの書き込み
fwrite(&bmpfhr.bfType, sizeof(uint16_t), 1, fbmpw);
fwrite(&bmpfhr.bfSize, sizeof(uint32_t), 1, fbmpw);
fwrite(&bmpfhr.bfReserved1, sizeof(uint16_t), 1, fbmpw);
fwrite(&bmpfhr.bfReserved2, sizeof(uint16_t), 1, fbmpw);
fwrite(&bmpfhr.bfOffBits, sizeof(uint32_t), 1, fbmpw);
fwrite(&bmpihr, sizeof(BITMAPINFOHEADER), 1, fbmpw);
// RGB データの書き込み、逆順にする
for (int y=0; y<bmpihr.biHeight; y++){
for (int x=0; x<bmpihr.biWidth; x++){
blue = dmar[((bmpihr.biHeight-1)-y)*bmpihr.biWidth+x] & 0xff;
green = (dmar[((bmpihr.biHeight-1)-y)*bmpihr.biWidth+x] >> 8) & 0xff;
red = (dmar[((bmpihr.biHeight-1)-y)*bmpihr.biWidth+x]>>16) & 0xff;
fputc(blue, fbmpw);
fputc(green, fbmpw);
fputc(red, fbmpw);
}
}
fclose(fbmpw);
free(rd_bmp);
free(dmar);
return(0);
}
日 | 月 | 火 | 水 | 木 | 金 | 土 |
---|---|---|---|---|---|---|
- | - | - | - | - | - | 1 |
2 | 3 | 4 | 5 | 6 | 7 | 8 |
9 | 10 | 11 | 12 | 13 | 14 | 15 |
16 | 17 | 18 | 19 | 20 | 21 | 22 |
23 | 24 | 25 | 26 | 27 | 28 | 29 |