OpenCV to ACL Pack Migration Guide
This guide helps developers migrate existing OpenCV code to ACL Pack on Android arm64-v8a / Linux aarch64.
Key Differences
1. No cv::Mat — Raw Pointers + Stride
OpenCV wraps images in cv::Mat with automatic memory management. ACL Pack uses raw pointers and explicit stride.
// OpenCV
cv::Mat src = cv::imread("image.jpg");
cv::Mat dst;
cv::GaussianBlur(src, dst, cv::Size(5, 5), 1.0);
// ACL Pack — caller manages memory, stride is in bytes.
// The scalar CPP template path is the closest OpenCV-style API: one call
// covers every type and channel count the tier supports.
int width = 1920, height = 1280, cn = 3;
uint8_t* src = new uint8_t[width * height * cn];
uint8_t* dst = new uint8_t[width * height * cn];
// ... load image data into src ...
acl::filter::gaussianBlur<uint8_t, uint8_t>(
src, dst, width, height, cn,
/*srcStride=*/0, /*dstStride=*/0, // 0 = contiguous
/*kRadiusX=*/2, /*kRadiusY=*/2,
/*sigmaX=*/1.0, /*sigmaY=*/1.0,
/*constant=*/nullptr,
acl::BorderType::BORDER_REFLECT_101);
delete[] src;
delete[] dst;Key points:
strideis in bytes, not pixels. Pass0for contiguous memory.- Caller allocates and frees all buffers.
- No implicit format conversion — you must know your data type.
2. Template Types vs Runtime Dispatch
OpenCV determines data type at runtime (src.depth()). ACL Pack uses compile-time templates for the data type, while mode-style enums (interpolation / threshold / border / color) stay runtime.
// OpenCV — runtime type, runtime mode
cv::threshold(src, dst, 128, 255, cv::THRESH_BINARY);
// ACL Pack — compile-time element type, runtime ThreshMode
acl::neon::arithmetic::threshold(
src_u8, dst_u8, width, height,
/*thresh=*/128, /*maxVal=*/255, /*minVal=*/0,
/*srcStride=*/0, /*dstStride=*/0,
acl::ThreshMode::THRESH_BINARY);3. Namespace-Based Module Organization
| OpenCV | ACL Pack (NEON) | ACL Pack (CPP) |
|---|---|---|
cv::GaussianBlur | acl::neon::filter::gaussianBlur | acl::filter::gaussianBlur |
cv::resize | acl::neon::geometric::resize | acl::geometric::resize |
cv::threshold | acl::neon::arithmetic::threshold | acl::arithmetic::threshold |
cv::cvtColor | acl::neon::cvtcolor::* | acl::cvtcolor::* |
Use acl::neon::* for ARM64 performance, acl::{module}::* for the portable scalar path. Note: the scalar namespace sits directly under acl::{module}:: (not acl::cpp::{module}::). Drawing operators live under acl::draw::, contour analysis under acl::contour::, memory utilities under acl::memory::. All operator declarations ship in a single header — <acl/api.h>.
Tier Availability (before you port)
ACL Pack ships one static library, libacl.a, with the headers under include/acl/. The library exposes every operator entry; which calls actually work is decided by the license loaded at acl::init(licensePath). A call to an operator outside your tier returns -1005 (ACL_ERR_NOT_LICENSED) without writing the output buffer.
- Starter — baseline filter / color / geometric / arithmetic / analysis / draw operators.
- Pro — Starter plus advanced denoising, contrast, feature, contour, DFT, and HSV / Lab color paths.
- Business — Pro plus enterprise paths (HDR, distance transform, connected components, YUV utilities, SIFT / SURF).
- Trial — only 1 sample operator (
resize), fixed 1920×1280 input (other sizes return-1006), watermark on the output.
For per-operator signatures and the full operator catalog see the API Reference.
Migration Example: Complete Pipeline
Before (OpenCV)
#include <opencv2/opencv.hpp>
void processFrame(const cv::Mat& frame) {
cv::Mat gray, blurred, edges;
// Convert BGR to grayscale
cv::cvtColor(frame, gray, cv::COLOR_BGR2GRAY);
// Gaussian blur (5x5)
cv::GaussianBlur(gray, blurred, cv::Size(5, 5), 1.5);
// Canny edge detection
cv::Canny(blurred, edges, 50, 150);
// Find contours
std::vector<std::vector<cv::Point>> contours;
cv::findContours(edges, contours, cv::RETR_LIST, cv::CHAIN_APPROX_SIMPLE);
// Resize to 640x480
cv::Mat resized;
cv::resize(edges, resized, cv::Size(640, 480));
}After (ACL Pack)
#include <acl/acl.h>
#include <acl/api.h> // every operator lives in this single header
void processFrame(const uint8_t* bgrData, int width, int height) {
// Caller manages all buffers — ACL Pack never allocates internally.
uint8_t* rgb = new uint8_t[width * height * 3];
uint8_t* gray = new uint8_t[width * height];
uint8_t* blurred = new uint8_t[width * height];
uint8_t* edges = new uint8_t[width * height];
// BGR → RGB: explicit channel swap (RGB2Gray expects RGB-ordered input).
acl::neon::cvtcolor::channelSwap<acl::BGR2RGB>(
bgrData, rgb, width, height);
// RGB → Gray (default LUMA = 0.299R + 0.587G + 0.114B, matches cv::COLOR_*2GRAY).
acl::neon::cvtcolor::RGB2Gray<uint8_t>(
rgb, gray, width, height);
// Gaussian blur — NEON fixed 5x5 fast path (1ch u8, default border).
acl::neon::filter::gaussianBlur5x5(
gray, blurred, width, height);
// Canny edges (1ch u8, low=50 high=150, default aperture=3).
acl::neon::filter::canny(
blurred, edges, width, height, /*low=*/50, /*high=*/150);
// Find contours — CPP only, Pro tier.
// `Point2i` / `HierarchyEntry` / `ContourRetrMode` live in `acl::`.
std::vector<std::vector<acl::Point2i>> contours;
acl::analysis::findContours(
edges, width, height, /*srcStride=*/0,
contours, /*hierarchy=*/nullptr,
acl::CONTOUR_RETR_LIST,
acl::CONTOUR_CHAIN_APPROX_SIMPLE);
// Resize to 640x480 (1ch u8, bilinear).
int dstW = 640, dstH = 480;
uint8_t* resized = new uint8_t[dstW * dstH];
acl::neon::geometric::resize<uint8_t>(
edges, resized, width, height, dstW, dstH,
/*srcStride=*/0, /*dstStride=*/0,
acl::InterpMode::LINEAR2D);
// ... use results ...
delete[] rgb;
delete[] gray;
delete[] blurred;
delete[] edges;
delete[] resized;
}Not Yet Available (use OpenCV for these)
For the full list of operators ACL Pack already ships, see the Operator Catalog in the API Reference. The table below is what's still missing — keep using OpenCV for these.
| Operator | Priority | Notes |
|---|---|---|
cv::undistort / cv::initUndistortRectifyMap | P0 | Camera distortion correction |
cv::drawContours | P1 | Contour rendering |
cv::floodFill | P1 | Region filling |
cv::cornerSubPix | P2 | Sub-pixel corner refinement |
cv::dilate with custom kernel shapes | P2 | Currently only rectangular |
cv::dnn module | N/A | Use TFLite / NNAPI directly |
cv::VideoCapture / cv::imwrite | N/A | Use Android SDK for I/O |
Migration Notes
Start with the hot path. Profile your OpenCV code and migrate the slowest operations first. Even a partial migration yields measurable wins, and it lets you validate the integration on a small surface before committing to a full port.
Prefer NEON fixed-size entry points when your kernel is 3×3 / 5×5 / 11×11. The fixed-size NEON Gaussian / box / median paths are 2-25× faster than the templated path that supports arbitrary radii. The templated
acl::{module}::*scalar path is still the right choice when you need non-u8types or arbitrary parameters.
For complete API documentation, see API Reference.
For performance benchmarks, see Performance Whitepaper.