Note

This is the documentation for the latest development branch and may refer to features that are not available in released versions. If you are looking for the documentation for a specific release, use the drop-down menu on the left and select the desired version.

AI2D Application Development Guide#

Overview#

AI2D is a hardware-accelerated image preprocessing module used on the board before inference. It reduces CPU overhead and improves end-to-end runtime performance. AI2D provides five hardware preprocessing capabilities:

  • Crop

  • Shift

  • Resize

  • Pad

  • Affine

In visual tasks such as object detection, Resize and Pad are often combined to implement a standard letterbox preprocessing flow.

AI2D is mainly used during the deployment stage on the board to speed up preprocessing and improve inference throughput. For the C++ runtime interface details, refer to:

AI2D Runtime API

The sample source code is located at:

src/rtsmart/examples/ai/usage_ai2d

Build it in that directory:

./build_app.sh

The generated executables are placed in k230_bin. Copy the build outputs to the board before running the samples.

When using AI2D, note the following:

Note

  1. Affine and Resize are mutually exclusive and cannot be enabled at the same time.

  2. Shift only supports Raw16 input format.

  3. Pad fill values are configured per channel, so the number of fill values must match the image channel count.

  4. Even if you use only one AI2D function, the parameter structures for the other functions still need to be present; just set the corresponding enable flag to false.

  5. When multiple functions are configured together, the execution order is fixed:

    Crop -> Shift -> Resize / Affine -> Pad
    

    Make sure the configuration of each stage stays consistent in terms of shape and data format.

Preprocessing Methods#

Resize Method#

Resize is one of the most commonly used image preprocessing operations. It changes the image size before inference and is widely used in detection and classification tasks.

Source location:

src/rtsmart/examples/ai/usage_ai2d/test_resize

Typical flow:

  1. Read input data.

  2. Initialize the AI2D input tensor.

  3. Initialize the AI2D output tensor according to the target shape.

  4. Configure ai2d_resize_param_t.

  5. Construct ai2d_builder and call build_schedule().

  6. Run the configured preprocessing path through invoke().

  7. Read the output tensor data.

The sample resizes an input image to 640 x 320.

Sample code:

int main(int argc, char *argv[])
{
    std::cout << "case " << argv[0] << " build " << __DATE__ << " " << __TIME__ << std::endl;
    if (argc < 3)
    {
        std::cerr << "Usage: " << argv[0] << "<image> <debug_mode>" << std::endl;
        return -1;
    }

    int debug_mode = atoi(argv[2]);

    // Read image and convert to CHW + RGB format
    cv::Mat ori_img = cv::imread(argv[1]);
    int ori_w = ori_img.cols;
    int ori_h = ori_img.rows;
    std::vector<uint8_t> chw_vec;
    std::vector<cv::Mat> bgrChannels(3);
    cv::split(ori_img, bgrChannels);
    for (auto i = 2; i > -1; i--)
    {
        std::vector<uint8_t> data = std::vector<uint8_t>(bgrChannels[i].reshape(1, 1));
        chw_vec.insert(chw_vec.end(), data.begin(), data.end());
    }

    // Create AI2D input tensor, copy CHW_RGB data into it, and flush to DDR
    dims_t ai2d_in_shape{1, 3, ori_h, ori_w};
    runtime_tensor ai2d_in_tensor = host_runtime_tensor::create(typecode_t::dt_uint8, ai2d_in_shape, hrt::pool_shared).expect("cannot create input tensor");
    auto input_buf = ai2d_in_tensor.impl()->to_host().unwrap()->buffer().as_host().unwrap().map(map_access_::map_write).unwrap().buffer();
    memcpy(reinterpret_cast<char *>(input_buf.data()), chw_vec.data(), chw_vec.size());
    hrt::sync(ai2d_in_tensor, sync_op_t::sync_write_back, true).expect("write back input failed");

    // Resize target dimensions
    int out_w = 640;
    int out_h = 320;
    int size = out_w * out_h;

    // Create AI2D output tensor
    dims_t ai2d_out_shape{1, 3, out_h, out_w};
    runtime_tensor ai2d_out_tensor = host_runtime_tensor::create(typecode_t::dt_uint8, ai2d_out_shape, hrt::pool_shared).expect("cannot create input tensor");

    // Configure AI2D parameters. AI2D supports 5 preprocessing methods: crop/shift/pad/resize/affine.
    // Here resize is enabled.
    ai2d_datatype_t ai2d_dtype{ai2d_format::NCHW_FMT, ai2d_format::NCHW_FMT, ai2d_in_tensor.datatype(), ai2d_out_tensor.datatype()};
    ai2d_crop_param_t crop_param{false, 0, 0, 0, 0};
    ai2d_shift_param_t shift_param{false, 0};
    ai2d_pad_param_t pad_param{false, {{0, 0}, {0, 0}, {0, 0}, {0, 0}}, ai2d_pad_mode::constant, {0, 0, 0}};
    ai2d_resize_param_t resize_param{true, ai2d_interp_method::tf_bilinear, ai2d_interp_mode::half_pixel};
    ai2d_affine_param_t affine_param{false, ai2d_interp_method::cv2_bilinear, 0, 0, 127, 1, {0.5, 0.1, 0.0, 0.1, 0.5, 0.0}};

    // Build AI2D scheduler
    ai2d_builder builder(ai2d_in_shape, ai2d_out_shape, ai2d_dtype, crop_param, shift_param, pad_param, resize_param, affine_param);
    builder.build_schedule();
    // Run AI2D: preprocess from ai2d_in_tensor to ai2d_out_tensor
    builder.invoke(ai2d_in_tensor, ai2d_out_tensor).expect("error occurred in ai2d running");

    // Retrieve result and save as image
    auto output_buf = ai2d_out_tensor.impl()->to_host().unwrap()->buffer().as_host().unwrap().map(map_access_::map_write).unwrap().buffer();
    cv::Mat image_r = cv::Mat(out_h, out_w, CV_8UC1, output_buf.data());
    cv::Mat image_g = cv::Mat(out_h, out_w, CV_8UC1, output_buf.data() + size);
    cv::Mat image_b = cv::Mat(out_h, out_w, CV_8UC1, output_buf.data() + 2 * size);

    std::vector<cv::Mat> color_vec(3);
    color_vec.clear();
    color_vec.push_back(image_b);
    color_vec.push_back(image_g);
    color_vec.push_back(image_r);
    cv::Mat color_img;
    cv::merge(color_vec, color_img);
    cv::imwrite("test_resize.jpg", color_img);

    return 0;
}

Run it on the board:

./test_resize.elf test.jpg 2

Reference result:

resize_res

Crop Method#

Crop extracts a region of interest (ROI) from the original image according to the specified coordinates and size.

Source location:

src/rtsmart/examples/ai/usage_ai2d/test_crop

Typical flow:

  1. Read input data.

  2. Initialize the AI2D input tensor.

  3. Initialize the AI2D output tensor according to the cropped shape.

  4. Configure ai2d_crop_param_t.

  5. Construct ai2d_builder and call build_schedule().

  6. Run the preprocessing path through invoke().

  7. Read the output tensor data.

The reference sample crops a 400 x 400 region starting from [10, 10].

Sample code:

int main(int argc, char *argv[])
{
    std::cout << "case " << argv[0] << " build " << __DATE__ << " " << __TIME__ << std::endl;
    if (argc < 3)
    {
        std::cerr << "Usage: " << argv[0] << "<image> <debug_mode>" << std::endl;
        return -1;
    }

    int debug_mode = atoi(argv[2]);

    // Read image and convert to CHW + RGB format
    cv::Mat ori_img = cv::imread(argv[1]);
    int ori_w = ori_img.cols;
    int ori_h = ori_img.rows;
    std::vector<uint8_t> chw_vec;
    std::vector<cv::Mat> bgrChannels(3);
    cv::split(ori_img, bgrChannels);
    for (auto i = 2; i > -1; i--)
    {
        std::vector<uint8_t> data = std::vector<uint8_t>(bgrChannels[i].reshape(1, 1));
        chw_vec.insert(chw_vec.end(), data.begin(), data.end());
    }

    // Create AI2D input tensor, copy CHW_RGB data into it, and flush to DDR
    dims_t ai2d_in_shape{1, 3, ori_h, ori_w};
    runtime_tensor ai2d_in_tensor = host_runtime_tensor::create(typecode_t::dt_uint8, ai2d_in_shape, hrt::pool_shared).expect("cannot create input tensor");
    auto input_buf = ai2d_in_tensor.impl()->to_host().unwrap()->buffer().as_host().unwrap().map(map_access_::map_write).unwrap().buffer();
    memcpy(reinterpret_cast<char *>(input_buf.data()), chw_vec.data(), chw_vec.size());
    hrt::sync(ai2d_in_tensor, sync_op_t::sync_write_back, true).expect("write back input failed");

    // Crop parameters
    int crop_x = 10;
    int crop_y = 10;
    int crop_w = 400;
    int crop_h = 400;

    // Create AI2D output tensor
    dims_t ai2d_out_shape{1, 3, crop_h, crop_w};
    runtime_tensor ai2d_out_tensor = host_runtime_tensor::create(typecode_t::dt_uint8, ai2d_out_shape, hrt::pool_shared).expect("cannot create input tensor");

    // Configure AI2D parameters. Here crop is enabled.
    ai2d_datatype_t ai2d_dtype{ai2d_format::NCHW_FMT, ai2d_format::NCHW_FMT, ai2d_in_tensor.datatype(), ai2d_out_tensor.datatype()};
    ai2d_crop_param_t crop_param{true, crop_x, crop_y, crop_w, crop_h};
    ai2d_shift_param_t shift_param{false, 0};
    ai2d_pad_param_t pad_param{false, {{0, 0}, {0, 0}, {0, 0}, {0, 0}}, ai2d_pad_mode::constant, {114, 114, 114}};
    ai2d_resize_param_t resize_param{false, ai2d_interp_method::tf_bilinear, ai2d_interp_mode::half_pixel};
    ai2d_affine_param_t affine_param{false, ai2d_interp_method::cv2_bilinear, 0, 0, 127, 1, {0.5, 0.1, 0.0, 0.1, 0.5, 0.0}};

    // Build AI2D scheduler
    ai2d_builder builder(ai2d_in_shape, ai2d_out_shape, ai2d_dtype, crop_param, shift_param, pad_param, resize_param, affine_param);
    builder.build_schedule();
    // Run AI2D: preprocess from ai2d_in_tensor to ai2d_out_tensor
    builder.invoke(ai2d_in_tensor, ai2d_out_tensor).expect("error occurred in ai2d running");

    // Retrieve result and save as image
    auto output_buf = ai2d_out_tensor.impl()->to_host().unwrap()->buffer().as_host().unwrap().map(map_access_::map_write).unwrap().buffer();
    cv::Mat image_r = cv::Mat(crop_h, crop_w, CV_8UC1, output_buf.data());
    cv::Mat image_g = cv::Mat(crop_h, crop_w, CV_8UC1, output_buf.data() + crop_h * crop_w);
    cv::Mat image_b = cv::Mat(crop_h, crop_w, CV_8UC1, output_buf.data() + 2 * crop_h * crop_w);

    std::vector<cv::Mat> color_vec(3);
    color_vec.clear();
    color_vec.push_back(image_b);
    color_vec.push_back(image_g);
    color_vec.push_back(image_r);
    cv::Mat color_img;
    cv::merge(color_vec, color_img);
    cv::imwrite("test_crop.jpg", color_img);

    return 0;
}

Run it on the board:

./test_crop.elf test.jpg 2

Reference result:

crop_res

Pad Method#

Pad is an image-border fill operation used during preprocessing. It changes the final image size by adding pixels to the top, bottom, left, and right edges. The fill values can be customized.

Source location:

src/rtsmart/examples/ai/usage_ai2d/test_pad

Typical flow:

  1. Read input data.

  2. Initialize the AI2D input tensor.

  3. Initialize the AI2D output tensor according to the padded shape.

  4. Configure ai2d_pad_param_t.

  5. Construct ai2d_builder and call build_schedule().

  6. Run the preprocessing path through invoke().

  7. Read the output tensor data.

The sample pads the image by:

  • top: 100

  • bottom: 100

  • left: 200

  • right: 200

and uses pad_val = {114, 114, 114}.

Sample code:

int main(int argc, char *argv[])
{
    std::cout << "case " << argv[0] << " build " << __DATE__ << " " << __TIME__ << std::endl;
    if (argc < 3)
    {
        std::cerr << "Usage: " << argv[0] << "<image> <debug_mode>" << std::endl;
        return -1;
    }

    int debug_mode = atoi(argv[2]);

    // Read image and convert to CHW + RGB format
    cv::Mat ori_img = cv::imread(argv[1]);
    int ori_w = ori_img.cols;
    int ori_h = ori_img.rows;
    std::vector<uint8_t> chw_vec;
    std::vector<cv::Mat> bgrChannels(3);
    cv::split(ori_img, bgrChannels);
    for (auto i = 2; i > -1; i--)
    {
        std::vector<uint8_t> data = std::vector<uint8_t>(bgrChannels[i].reshape(1, 1));
        chw_vec.insert(chw_vec.end(), data.begin(), data.end());
    }

    // Create AI2D input tensor, copy CHW_RGB data into it, and flush to DDR
    dims_t ai2d_in_shape{1, 3, ori_h, ori_w};
    runtime_tensor ai2d_in_tensor = host_runtime_tensor::create(typecode_t::dt_uint8, ai2d_in_shape, hrt::pool_shared).expect("cannot create input tensor");
    auto input_buf = ai2d_in_tensor.impl()->to_host().unwrap()->buffer().as_host().unwrap().map(map_access_::map_write).unwrap().buffer();
    memcpy(reinterpret_cast<char *>(input_buf.data()), chw_vec.data(), chw_vec.size());
    hrt::sync(ai2d_in_tensor, sync_op_t::sync_write_back, true).expect("write back input failed");

    // Padding parameters
    int pad_top    = 100;
    int pad_bottom = 100;
    int pad_left   = 200;
    int pad_right  = 200;
    std::vector<int> pad_val = {114, 114, 114};
    int out_w = ori_w + pad_left + pad_right;
    int out_h = ori_h + pad_top + pad_bottom;
    int size  = out_w * out_h;

    // Create AI2D output tensor
    dims_t ai2d_out_shape{1, 3, out_h, out_w};
    runtime_tensor ai2d_out_tensor = host_runtime_tensor::create(typecode_t::dt_uint8, ai2d_out_shape, hrt::pool_shared).expect("cannot create input tensor");

    // Configure AI2D parameters. Here pad is enabled.
    ai2d_datatype_t ai2d_dtype{ai2d_format::NCHW_FMT, ai2d_format::NCHW_FMT, ai2d_in_tensor.datatype(), ai2d_out_tensor.datatype()};
    ai2d_crop_param_t crop_param{false, 0, 0, 0, 0};
    ai2d_shift_param_t shift_param{false, 0};
    ai2d_pad_param_t pad_param{true, {{0, 0}, {0, 0}, {pad_top, pad_bottom}, {pad_left, pad_right}}, ai2d_pad_mode::constant, pad_val};
    ai2d_resize_param_t resize_param{false, ai2d_interp_method::tf_bilinear, ai2d_interp_mode::half_pixel};
    ai2d_affine_param_t affine_param{false, ai2d_interp_method::cv2_bilinear, 0, 0, 127, 1, {0.5, 0.1, 0.0, 0.1, 0.5, 0.0}};

    // Build AI2D scheduler
    ai2d_builder builder(ai2d_in_shape, ai2d_out_shape, ai2d_dtype, crop_param, shift_param, pad_param, resize_param, affine_param);
    builder.build_schedule();
    // Run AI2D: preprocess from ai2d_in_tensor to ai2d_out_tensor
    builder.invoke(ai2d_in_tensor, ai2d_out_tensor).expect("error occurred in ai2d running");

    // Retrieve result and save as image
    auto output_buf = ai2d_out_tensor.impl()->to_host().unwrap()->buffer().as_host().unwrap().map(map_access_::map_write).unwrap().buffer();
    cv::Mat image_r = cv::Mat(out_h, out_w, CV_8UC1, output_buf.data());
    cv::Mat image_g = cv::Mat(out_h, out_w, CV_8UC1, output_buf.data() + size);
    cv::Mat image_b = cv::Mat(out_h, out_w, CV_8UC1, output_buf.data() + 2 * size);

    std::vector<cv::Mat> color_vec(3);
    color_vec.clear();
    color_vec.push_back(image_b);
    color_vec.push_back(image_g);
    color_vec.push_back(image_r);
    cv::Mat color_img;
    cv::merge(color_vec, color_img);
    cv::imwrite("test_pad.jpg", color_img);

    return 0;
}

Run it on the board:

./test_pad.elf test.jpg 2

Reference result:

pad_res

Affine Method#

Affine is a geometric transform used during image preprocessing. It supports operations such as rotation, translation, and scaling, while preserving straight lines and parallelism.

Source location:

src/rtsmart/examples/ai/usage_ai2d/test_affine

Typical flow:

  1. Read input data.

  2. Initialize the AI2D input tensor.

  3. Initialize the AI2D output tensor according to the transformed shape.

  4. Configure ai2d_affine_param_t.

  5. Construct ai2d_builder and call build_schedule().

  6. Run the preprocessing path through invoke().

  7. Read the output tensor data.

The reference sample uses an affine matrix that:

  • scales the image by 0.5

  • translates it by 200 pixels in both x and y

Sample code:

int main(int argc, char *argv[])
{
    std::cout << "case " << argv[0] << " build " << __DATE__ << " " << __TIME__ << std::endl;
    if (argc < 3)
    {
        std::cerr << "Usage: " << argv[0] << "<image> <debug_mode>" << std::endl;
        return -1;
    }

    int debug_mode = atoi(argv[2]);

    // Read image and convert to CHW + RGB format
    cv::Mat ori_img = cv::imread(argv[1]);
    int ori_w = ori_img.cols;
    int ori_h = ori_img.rows;
    std::vector<uint8_t> chw_vec;
    std::vector<cv::Mat> bgrChannels(3);
    cv::split(ori_img, bgrChannels);
    for (auto i = 2; i > -1; i--)
    {
        std::vector<uint8_t> data = std::vector<uint8_t>(bgrChannels[i].reshape(1, 1));
        chw_vec.insert(chw_vec.end(), data.begin(), data.end());
    }

    // Create AI2D input tensor, copy CHW_RGB data into it, and flush to DDR
    dims_t ai2d_in_shape{1, 3, ori_h, ori_w};
    runtime_tensor ai2d_in_tensor = host_runtime_tensor::create(typecode_t::dt_uint8, ai2d_in_shape, hrt::pool_shared).expect("cannot create input tensor");
    auto input_buf = ai2d_in_tensor.impl()->to_host().unwrap()->buffer().as_host().unwrap().map(map_access_::map_write).unwrap().buffer();
    memcpy(reinterpret_cast<char *>(input_buf.data()), chw_vec.data(), chw_vec.size());
    hrt::sync(ai2d_in_tensor, sync_op_t::sync_write_back, true).expect("write back input failed");

    // Affine transform matrix: scale 0.5x, translate 200px in X and Y
    std::vector<float> affine_matrix = {0.5, 0.0, 200.0,
                                        0.0, 0.5, 200.0};
    int out_w = (int)(0.5 * ori_w);
    int out_h = (int)(0.5 * ori_h);
    int size  = out_w * out_h;

    // Create AI2D output tensor
    dims_t ai2d_out_shape{1, 3, out_h, out_w};
    runtime_tensor ai2d_out_tensor = host_runtime_tensor::create(typecode_t::dt_uint8, ai2d_out_shape, hrt::pool_shared).expect("cannot create input tensor");

    // Configure AI2D parameters. Here affine is enabled.
    ai2d_datatype_t ai2d_dtype{ai2d_format::NCHW_FMT, ai2d_format::NCHW_FMT, ai2d_in_tensor.datatype(), ai2d_out_tensor.datatype()};
    ai2d_crop_param_t crop_param{false, 0, 0, 0, 0};
    ai2d_shift_param_t shift_param{false, 0};
    ai2d_pad_param_t pad_param{false, {{0, 0}, {0, 0}, {0, 0}, {0, 0}}, ai2d_pad_mode::constant, {0, 0, 0}};
    ai2d_resize_param_t resize_param{false, ai2d_interp_method::tf_bilinear, ai2d_interp_mode::half_pixel};
    ai2d_affine_param_t affine_param{true, ai2d_interp_method::cv2_bilinear, 0, 0, 127, 1, affine_matrix};

    // Build AI2D scheduler
    ai2d_builder builder(ai2d_in_shape, ai2d_out_shape, ai2d_dtype, crop_param, shift_param, pad_param, resize_param, affine_param);
    builder.build_schedule();
    // Run AI2D: preprocess from ai2d_in_tensor to ai2d_out_tensor
    builder.invoke(ai2d_in_tensor, ai2d_out_tensor).expect("error occurred in ai2d running");

    // Retrieve result and save as image
    auto output_buf = ai2d_out_tensor.impl()->to_host().unwrap()->buffer().as_host().unwrap().map(map_access_::map_write).unwrap().buffer();
    cv::Mat image_r = cv::Mat(out_h, out_w, CV_8UC1, output_buf.data());
    cv::Mat image_g = cv::Mat(out_h, out_w, CV_8UC1, output_buf.data() + size);
    cv::Mat image_b = cv::Mat(out_h, out_w, CV_8UC1, output_buf.data() + 2 * size);

    std::vector<cv::Mat> color_vec(3);
    color_vec.clear();
    color_vec.push_back(image_b);
    color_vec.push_back(image_g);
    color_vec.push_back(image_r);
    cv::Mat color_img;
    cv::merge(color_vec, color_img);
    cv::imwrite("test_affine.jpg", color_img);

    return 0;
}

Run it on the board:

./test_affine.elf test.jpg 2

Reference result:

affine_res

Shift Method#

Shift performs right-shift preprocessing on the input data. Each one-bit right shift divides the original value by 2. The input format must be RAW16.

Source location:

src/rtsmart/examples/ai/usage_ai2d/test_shift

Typical flow:

  1. Create or read the input data.

  2. Initialize the AI2D input tensor.

  3. Initialize the AI2D output tensor.

  4. Configure ai2d_shift_param_t.

  5. Construct ai2d_builder and call build_schedule().

  6. Run the preprocessing path through invoke().

  7. Read the output tensor data.

In the reference sample, a RAW16 image filled with 240 is created, then shifted right by one bit so all values become 120.

Sample code:

int main(int argc, char *argv[])
{
    std::cout << "case " << argv[0] << " build " << __DATE__ << " " << __TIME__ << std::endl;
    if (argc < 2)
    {
        std::cerr << "Usage: " << argv[0] << "<debug_mode:0,1,2>" << std::endl;
        return -1;
    }

    int debug_mode = atoi(argv[1]);

    // Create a 16-bit raw image initialized to 240
    cv::Mat ori_img(320, 320, CV_16UC3, cv::Scalar(240, 240, 240));
    cv::imwrite("ori_img.jpg", ori_img);

    // HWC, BGR
    int ori_w = ori_img.cols;
    int ori_h = ori_img.rows;

    // Create AI2D input tensor
    dims_t ai2d_in_shape{1, ori_h, ori_w, 3};
    runtime_tensor ai2d_in_tensor = host_runtime_tensor::create(typecode_t::dt_uint16, ai2d_in_shape, hrt::pool_shared).expect("cannot create input tensor");
    auto input_buf = ai2d_in_tensor.impl()->to_host().unwrap()->buffer().as_host().unwrap().map(map_access_::map_write).unwrap().buffer();
    memcpy(reinterpret_cast<uint16_t *>(input_buf.data()), ori_img.data, ori_h * ori_w * 3 * sizeof(uint16_t));
    hrt::sync(ai2d_in_tensor, sync_op_t::sync_write_back, true).expect("write back input failed");

    int out_w = ori_w;
    int out_h = ori_h;

    // Create AI2D output tensor
    dims_t ai2d_out_shape{1, out_h, out_w, 3};
    runtime_tensor ai2d_out_tensor = host_runtime_tensor::create(typecode_t::dt_uint16, ai2d_out_shape, hrt::pool_shared).expect("cannot create input tensor");

    // Configure AI2D parameters. Here shift is enabled: right-shift by 1 bit halves all values.
    ai2d_datatype_t ai2d_dtype{ai2d_format::RAW16, ai2d_format::RAW16, ai2d_in_tensor.datatype(), ai2d_out_tensor.datatype()};
    ai2d_crop_param_t crop_param{false, 0, 0, 0, 0};
    ai2d_shift_param_t shift_param{true, 1};
    ai2d_pad_param_t pad_param{false, {{0, 0}, {0, 0}, {0, 0}, {0, 0}}, ai2d_pad_mode::constant, {0, 0, 0}};
    ai2d_resize_param_t resize_param{false, ai2d_interp_method::tf_bilinear, ai2d_interp_mode::half_pixel};
    ai2d_affine_param_t affine_param{false, ai2d_interp_method::cv2_bilinear, 0, 0, 127, 1, {0.5, 0.1, 0.0, 0.1, 0.5, 0.0}};

    // Build AI2D scheduler
    ai2d_builder builder(ai2d_in_shape, ai2d_out_shape, ai2d_dtype, crop_param, shift_param, pad_param, resize_param, affine_param);
    builder.build_schedule();
    // Run AI2D: preprocess from ai2d_in_tensor to ai2d_out_tensor
    builder.invoke(ai2d_in_tensor, ai2d_out_tensor).expect("error occurred in ai2d running");

    // Retrieve result and save as image
    auto output_buf = ai2d_out_tensor.impl()->to_host().unwrap()->buffer().as_host().unwrap().map(map_access_::map_write).unwrap().buffer();
    cv::Mat image_r = cv::Mat(out_h, out_w, CV_16UC3, output_buf.data());
    cv::imwrite("test_shift.jpg", image_r);

    return 0;
}

Run it on the board:

./test_shift.elf 2

Reference result:

shift_res

Common AI2D Builder Pattern#

All AI2D samples use the same core pattern:

  1. Read or prepare the input data.

  2. Convert image data to the required layout, typically CHW + RGB.

  3. Create the AI2D input tensor and write the source data to it.

  4. Create the AI2D output tensor based on the target shape.

  5. Configure:

    • ai2d_datatype_t

    • ai2d_crop_param_t

    • ai2d_shift_param_t

    • ai2d_pad_param_t

    • ai2d_resize_param_t

    • ai2d_affine_param_t

  6. Construct ai2d_builder.

  7. Call build_schedule().

  8. Call invoke().

  9. Read back the output tensor and inspect or save the result.

This same builder-based pattern is reused in most K230 deployment projects that use AI2D before KPU inference.

Comments list
Comments
Log in