【施工中】 halide学习笔记

**Halide is a programming language designed to make it easier to write high-performance image and array processing code on modern machines. **

halide有两个特性比较吸引人。一个是对于各种平台架构的支持。

  * CPU architectures: X86, ARM, MIPS, Hexagon, PowerPC
  * Operating systems: Linux, Windows, macOS, Android, iOS, Qualcomm QuRT
  * GPU Compute APIs: CUDA, OpenCL, OpenGL, OpenGL Compute Shaders, Apple Metal, Microsoft Direct X 12

另一个是把计算什么和怎么计算(何时计算)分离开来。

Halide的Schedule可以由程序员来指定一些策略,指定硬件的buffer大小,缓冲线的相关设置,这样可以根据不同的计算硬件的特性来实现高效率的计算单元的调度,而图像算法的计算实现却不需要修改。

Halide的运行有两种方式,一种是JIT的模式,另一种是AOT的模式。JIT模式使用起来比较方便,可以直接将算法和Halide的代码生成generator封装成一个类,在程序的其他部分调用这个类即可。在嵌入式环境和交叉编译环境下一般使用AOT模式,此时需要调用compiler函数将算法代码和Halide的代码生成generator编译位目标机器的代码,生成一个.o目标文件和.h头文件。然后在独立的目标机器的应用的工程的源代码中通过头文件调用算法实现的计算函数,并在build的时候链接上.o文件,这样就得到一个可以在目标机器上运行的用Halide实现算法的程序了。一般DSP上都是这种方式来做的。

可以直接参考tutorials 来学习

下面是一段将Halide Buffer转化成opencv Mat的代码,用于调试。

 1    
 2    // Include some support code for loading pngs.
 3    // #include "halide_image_io.h"
 4    #include <iostream>
 5    #include "para_norm.h"
 6    #include "HalideBuffer.h"
 7    #include "clock.h"
 8    #include "halide_image_io.h"  // for Halide::tools to load image. just for debug
 9    #include <opencv2/opencv.hpp> // to show image
10    
11    // #include "Halide.h"
12    using namespace std;
13    using namespace cv;
14    using namespace Halide;
15    using namespace Halide::Runtime;
16    // using namespace Halide::Tools;
17    void convertHalide2Mat(const Buffer<uint8_t> &src, cv::Mat &dest)
18    {
19        if (dest.empty())
20            dest.create(cv::Size(src.width(), src.height()), CV_MAKETYPE(CV_8U, src.channels()));
21        const int ch = dest.channels();
22        if (ch == 1)
23        {
24            for (int j = 0; j < dest.rows; j++)
25            {
26                for (int i = 0; i < dest.cols; i++)
27                {
28                    dest.at<uchar>(j, i) = src(i, j);
29                }
30            }
31        }
32        else if (ch == 3)
33        {
34            for (int j = 0; j < dest.rows; j++)
35            {
36                for (int i = 0; i < dest.cols; i++)
37                {
38                    dest.at<uchar>(j, 3 * i + 0) = src(i, j, 2);
39                    dest.at<uchar>(j, 3 * i + 1) = src(i, j, 1);
40                    dest.at<uchar>(j, 3 * i + 2) = src(i, j, 0);
41                }
42            }
43        }
44    }
45    int main(int argc, char **argv)
46    {
47    
48        Halide::Runtime::Buffer<uint8_t> input = Halide::Tools::load_image("/data/github/learnhalide/png.png");
49        cv::Mat Img;
50        convertHalide2Mat(input,Img);
51        cv::imshow("debug", Img);
52        cv::waitKey(0);
53        return 0;
54    }
55    
56

吐槽下hahide的文档...各种函数全靠试...

试了好久得到的,opencv Mat转halide::buffer的办法:

 1    
 2    cv::Mat image = cv::imread("/data/github/learnhalide/png.png");
 3        cv::imshow("input",image);
 4        cv::waitKey(0);
 5        halide_buffer_t buffer;
 6        // memset(&buffer, 0, sizeof(buffer));
 7    
 8        buffer.host = image.data;
 9        printf("image.data %d %d %d\n",image.data[0],image.data[1],image.data[2]);
10        printf("buffer.host %d %d %d\n",buffer.host[0],buffer.host[1],buffer.host[2]);
11        // 数据没有copy过来
12        buffer.type = halide_type_t(halide_type_uint,8);
13        buffer.dimensions = 3;
14        buffer.dim = (halide_dimension_t *)malloc(3*4*sizeof(uint32_t));
15        buffer.dim[0].extent = image.cols;
16        buffer.dim[1].extent = image.rows;
17        buffer.dim[2].extent = image.channels();
18        buffer.dim[0].min = buffer.dim[1].min = buffer.dim[2].min = 0;
19    
20    
21        buffer.dim[0].stride = image.step1(1);
22        buffer.dim[1].stride = image.step1(0);
23        buffer.dim[2].stride = 1;
24    
25        Halide::Runtime::Buffer<uint8_t> input(buffer);
26        printf("input buffer:%d %d %d\n",input.width(),input.height(),input.channels());
27        // printf("%d\n",input(2,2));
28        const halide_buffer_t * 	raw_buffer = input.raw_buffer();
29        printf("raw buffer: %d %d %d\n",raw_buffer->host[0],raw_buffer->host[1],raw_buffer->host[2]);
30    
31    
32        cv::Mat Img;
33        convertHalide2Mat(input, Img);
34        printf("Mat(%d %d)\n", Img.cols, Img.rows);
35        cv::imshow("debug", Img);
36        cv::waitKey(0);
37    

可能会报错Error: Constraint violated: input.stride.0 (3) == 1 (1),原因是:

**We have a default constraint of stride==1 on the innermost dimension, so that vectorization works out well**

Constraint violated: f.stride.0 (2) == 1 (1) #3109

解决办法是(对于AOT的编译方式):

ImageParam input(type_of<uint8_t>(), 3);

input.dim(0).set_stride(Expr());

一种更简便的opencv Mat得到halide buffer的办法. 一个关键的问题是opencv Mat的memory layout是Interleaved的方式,也就是如下:

// RGBRGBRGBRGBRGBRGBRGBRGB

// RGBRGBRGBRGBRGBRGBRGBRGB

// RGBRGBRGBRGBRGBRGBRGBRGB

// RGBRGBRGBRGBRGBRGBRGBRGB

但是Halide::buffer的默认memory layout是Planar的方式:

// RRRRRRRR

// RRRRRRRR

// RRRRRRRR

// RRRRRRRR

// GGGGGGGG

// GGGGGGGG

// GGGGGGGG

// GGGGGGGG

// BBBBBBBB

// BBBBBBBB

// BBBBBBBB

// BBBBBBBB

因此需要用到函数

make_interleaved()

1cv::Mat image = cv::imread("/data/github/learnhalide/png.png");
2Halide::Runtime::Buffer<uint8_t> input = Halide::Runtime::Buffer<uint8_t>::make_interleaved(image.data,1240, 460, 3);