builtin-programs/recognition/optical-flow.folk
# (Most people don't need this program to be working unless you know
# you are using optical flow for some specific camera recognition
# task.)
# NVIDIA hardware optical flow through the Optical Flow SDK CUDA interface.
# No CUDA Toolkit is needed at build time: the driver entry points are loaded
# from libcuda and libnvidia-opticalflow at runtime.
#
# Returns an opaque OpticalFlow pointer. Sample it with sampleOpticalFlow and
# release it with freeOpticalFlow. Vectors point from f0 to f1 in source-image
# pixel units.
fn opticalFlowInit {imageLib} {
set cc [C]
$cc extend $imageLib
$cc include <dlfcn.h>
$cc include <pthread.h>
$cc include <stdlib.h>
$cc include <string.h>
$cc code {
/* Minimal CUDA Driver API ABI. Keeping this here makes the module
* compile on machines which have an NVIDIA runtime but no Toolkit. */
typedef int CUresult;
typedef int CUdevice;
typedef struct CUctx_st *CUcontext;
typedef unsigned long long CUdeviceptr;
typedef int CUmemorytype;
#define CUDA_SUCCESS 0
#define CU_MEMORYTYPE_HOST 1
#define CU_MEMORYTYPE_DEVICE 2
typedef struct CUDA_MEMCPY2D {
size_t srcXInBytes;
size_t srcY;
CUmemorytype srcMemoryType;
const void *srcHost;
CUdeviceptr srcDevice;
void *srcArray;
size_t srcPitch;
size_t dstXInBytes;
size_t dstY;
CUmemorytype dstMemoryType;
void *dstHost;
CUdeviceptr dstDevice;
void *dstArray;
size_t dstPitch;
size_t WidthInBytes;
size_t Height;
} CUDA_MEMCPY2D;
typedef CUresult (*PFN_cuInit)(unsigned int);
typedef CUresult (*PFN_cuDeviceGetCount)(int *);
typedef CUresult (*PFN_cuDeviceGet)(CUdevice *, int);
typedef CUresult (*PFN_cuCtxCreate)(CUcontext *, unsigned int, CUdevice);
typedef CUresult (*PFN_cuCtxDestroy)(CUcontext);
typedef CUresult (*PFN_cuCtxGetCurrent)(CUcontext *);
typedef CUresult (*PFN_cuCtxSetCurrent)(CUcontext);
typedef CUresult (*PFN_cuCtxSynchronize)(void);
typedef CUresult (*PFN_cuMemcpy2D)(const CUDA_MEMCPY2D *);
typedef CUresult (*PFN_cuGetErrorName)(CUresult, const char **);
typedef CUresult (*PFN_cuGetErrorString)(CUresult, const char **);
typedef struct CUDAFunctions {
void *library;
PFN_cuInit init;
PFN_cuDeviceGetCount deviceGetCount;
PFN_cuDeviceGet deviceGet;
PFN_cuCtxCreate contextCreate;
PFN_cuCtxDestroy contextDestroy;
PFN_cuCtxGetCurrent contextGetCurrent;
PFN_cuCtxSetCurrent contextSetCurrent;
PFN_cuCtxSynchronize contextSynchronize;
PFN_cuMemcpy2D memcpy2D;
PFN_cuGetErrorName getErrorName;
PFN_cuGetErrorString getErrorString;
} CUDAFunctions;
/* NVIDIA Optical Flow SDK 2.x ABI. */
typedef struct NvOFHandle_st *NvOFHandle;
typedef struct NvOFGPUBufferHandle_st *NvOFGPUBufferHandle;
typedef struct NVOFPrivDataHandle_st *NvOFPrivDataHandle;
typedef int NV_OF_STATUS;
typedef int NV_OF_BOOL;
typedef int NV_OF_CAPS;
typedef int NV_OF_OUTPUT_VECTOR_GRID_SIZE;
typedef int NV_OF_HINT_VECTOR_GRID_SIZE;
typedef int NV_OF_MODE;
typedef int NV_OF_PERF_LEVEL;
typedef int NV_OF_STEREO_DISPARITY_RANGE;
typedef int NV_OF_BUFFER_USAGE;
typedef int NV_OF_BUFFER_FORMAT;
typedef int NV_OF_CUDA_BUFFER_TYPE;
#define NV_OF_API_VERSION ((uint16_t)(2u << 4))
#define NV_OF_SUCCESS 0
#define NV_OF_TRUE 1
#define NV_OF_CAPS_SUPPORTED_OUTPUT_GRID_SIZES 0
#define NV_OF_MODE_OPTICALFLOW 1
#define NV_OF_PERF_LEVEL_MEDIUM 10
#define NV_OF_BUFFER_USAGE_INPUT 1
#define NV_OF_BUFFER_USAGE_OUTPUT 2
#define NV_OF_BUFFER_FORMAT_GRAYSCALE8 1
#define NV_OF_BUFFER_FORMAT_SHORT2 5
#define NV_OF_CUDA_BUFFER_TYPE_CUDEVICEPTR 2
typedef struct NV_OF_FLOW_VECTOR {
int16_t flowx;
int16_t flowy;
} NV_OF_FLOW_VECTOR;
typedef struct OpticalFlow {
uint32_t width;
uint32_t height;
uint32_t gridSize;
NV_OF_FLOW_VECTOR *vectors;
} OpticalFlow;
typedef struct NV_OF_INIT_PARAMS {
uint32_t width;
uint32_t height;
NV_OF_OUTPUT_VECTOR_GRID_SIZE outGridSize;
NV_OF_HINT_VECTOR_GRID_SIZE hintGridSize;
NV_OF_MODE mode;
NV_OF_PERF_LEVEL perfLevel;
NV_OF_BOOL enableExternalHints;
NV_OF_BOOL enableOutputCost;
NvOFPrivDataHandle hPrivData;
NV_OF_STEREO_DISPARITY_RANGE disparityRange;
NV_OF_BOOL enableRoi;
} NV_OF_INIT_PARAMS;
typedef struct NV_OF_BUFFER_DESCRIPTOR {
uint32_t width;
uint32_t height;
NV_OF_BUFFER_USAGE bufferUsage;
NV_OF_BUFFER_FORMAT bufferFormat;
} NV_OF_BUFFER_DESCRIPTOR;
typedef struct NV_OF_ROI_RECT {
uint32_t start_x, start_y, width, height;
} NV_OF_ROI_RECT;
typedef struct NV_OF_EXECUTE_INPUT_PARAMS {
NvOFGPUBufferHandle inputFrame;
NvOFGPUBufferHandle referenceFrame;
NvOFGPUBufferHandle externalHints;
NV_OF_BOOL disableTemporalHints;
uint32_t padding;
NvOFPrivDataHandle hPrivData;
uint32_t padding2;
uint32_t numRois;
NV_OF_ROI_RECT *roiData;
} NV_OF_EXECUTE_INPUT_PARAMS;
typedef struct NV_OF_EXECUTE_OUTPUT_PARAMS {
NvOFGPUBufferHandle outputBuffer;
NvOFGPUBufferHandle outputCostBuffer;
NvOFPrivDataHandle hPrivData;
} NV_OF_EXECUTE_OUTPUT_PARAMS;
typedef struct NV_OF_BUFFER_STRIDE {
uint32_t strideXInBytes;
uint32_t strideYInBytes;
} NV_OF_BUFFER_STRIDE;
typedef struct NV_OF_CUDA_BUFFER_STRIDE_INFO {
NV_OF_BUFFER_STRIDE strideInfo[3];
uint32_t numPlanes;
} NV_OF_CUDA_BUFFER_STRIDE_INFO;
typedef NV_OF_STATUS (*PFN_nvCreateOpticalFlowCuda)(
CUcontext, NvOFHandle *);
typedef NV_OF_STATUS (*PFN_nvOFInit)(
NvOFHandle, const NV_OF_INIT_PARAMS *);
typedef NV_OF_STATUS (*PFN_nvOFCreateGPUBufferCuda)(
NvOFHandle, const NV_OF_BUFFER_DESCRIPTOR *,
NV_OF_CUDA_BUFFER_TYPE, NvOFGPUBufferHandle *);
typedef void *(*PFN_nvOFGPUBufferGetCUarray)(NvOFGPUBufferHandle);
typedef CUdeviceptr (*PFN_nvOFGPUBufferGetCUdeviceptr)(
NvOFGPUBufferHandle);
typedef NV_OF_STATUS (*PFN_nvOFGPUBufferGetStrideInfo)(
NvOFGPUBufferHandle, NV_OF_CUDA_BUFFER_STRIDE_INFO *);
typedef NV_OF_STATUS (*PFN_nvOFSetIOCudaStreams)(
NvOFHandle, void *, void *);
typedef NV_OF_STATUS (*PFN_nvOFExecute)(
NvOFHandle, const NV_OF_EXECUTE_INPUT_PARAMS *,
NV_OF_EXECUTE_OUTPUT_PARAMS *);
typedef NV_OF_STATUS (*PFN_nvOFDestroyGPUBufferCuda)(
NvOFGPUBufferHandle);
typedef NV_OF_STATUS (*PFN_nvOFDestroy)(NvOFHandle);
typedef NV_OF_STATUS (*PFN_nvOFGetLastError)(
NvOFHandle, char *, uint32_t *);
typedef NV_OF_STATUS (*PFN_nvOFGetCaps)(
NvOFHandle, NV_OF_CAPS, uint32_t *, uint32_t *);
typedef struct NV_OF_CUDA_API_FUNCTION_LIST {
PFN_nvCreateOpticalFlowCuda nvCreateOpticalFlowCuda;
PFN_nvOFInit nvOFInit;
PFN_nvOFCreateGPUBufferCuda nvOFCreateGPUBufferCuda;
PFN_nvOFGPUBufferGetCUarray nvOFGPUBufferGetCUarray;
PFN_nvOFGPUBufferGetCUdeviceptr nvOFGPUBufferGetCUdeviceptr;
PFN_nvOFGPUBufferGetStrideInfo nvOFGPUBufferGetStrideInfo;
PFN_nvOFSetIOCudaStreams nvOFSetIOCudaStreams;
PFN_nvOFExecute nvOFExecute;
PFN_nvOFDestroyGPUBufferCuda nvOFDestroyGPUBufferCuda;
PFN_nvOFDestroy nvOFDestroy;
PFN_nvOFGetLastError nvOFGetLastError;
PFN_nvOFGetCaps nvOFGetCaps;
} NV_OF_CUDA_API_FUNCTION_LIST;
typedef NV_OF_STATUS (*PFN_NvOFAPICreateInstanceCuda)(
uint32_t, NV_OF_CUDA_API_FUNCTION_LIST *);
typedef struct OpticalFlowRuntime {
CUDAFunctions cuda;
void *opticalFlowLibrary;
NV_OF_CUDA_API_FUNCTION_LIST of;
int attempted;
int ready;
char error[512];
} OpticalFlowRuntime;
static OpticalFlowRuntime runtime;
typedef struct OpticalFlowSession {
pthread_mutex_t mutex;
int initialized;
CUcontext context;
NvOFHandle handle;
uint32_t gridSize;
uint32_t width;
uint32_t height;
uint32_t flowWidth;
uint32_t flowHeight;
NvOFGPUBufferHandle input;
NvOFGPUBufferHandle reference;
NvOFGPUBufferHandle output;
NV_OF_CUDA_BUFFER_STRIDE_INFO inputStride;
NV_OF_CUDA_BUFFER_STRIDE_INFO referenceStride;
NV_OF_CUDA_BUFFER_STRIDE_INFO outputStride;
CUdeviceptr inputPointer;
CUdeviceptr referencePointer;
CUdeviceptr outputPointer;
uint8_t *grayFrame;
size_t grayFrameSize;
uint64_t referenceUniq;
int referenceValid;
} OpticalFlowSession;
static OpticalFlowSession session = {
.mutex = PTHREAD_MUTEX_INITIALIZER,
};
static void *ofOpenLibrary(const char *first, const char *second)
{
void *library = dlopen(first, RTLD_NOW | RTLD_LOCAL);
if (!library && second)
library = dlopen(second, RTLD_NOW | RTLD_LOCAL);
return library;
}
static int ofLoadSymbol(void *library, const char *name, void **result)
{
*result = dlsym(library, name);
if (*result) return 1;
snprintf(runtime.error, sizeof(runtime.error),
"opticalFlow: missing driver symbol %s", name);
return 0;
}
#define OF_LOAD(LIBRARY, FIELD, NAME) \
do { if (!ofLoadSymbol((LIBRARY), (NAME), \
(void **)&(FIELD))) return 0; } while (0)
static int ofLoadRuntime(void)
{
if (runtime.ready) return 1;
if (runtime.attempted) return 0;
runtime.attempted = 1;
#if defined(__APPLE__)
snprintf(runtime.error, sizeof(runtime.error),
"opticalFlow: NVIDIA CUDA optical flow is unavailable "
"on macOS");
return 0;
#else
runtime.cuda.library = ofOpenLibrary("libcuda.so.1", "libcuda.so");
if (!runtime.cuda.library) {
snprintf(runtime.error, sizeof(runtime.error),
"opticalFlow: could not load libcuda.so.1: %s",
dlerror());
return 0;
}
OF_LOAD(runtime.cuda.library, runtime.cuda.init, "cuInit");
OF_LOAD(runtime.cuda.library, runtime.cuda.deviceGetCount,
"cuDeviceGetCount");
OF_LOAD(runtime.cuda.library, runtime.cuda.deviceGet, "cuDeviceGet");
OF_LOAD(runtime.cuda.library, runtime.cuda.contextCreate,
"cuCtxCreate_v2");
OF_LOAD(runtime.cuda.library, runtime.cuda.contextDestroy,
"cuCtxDestroy_v2");
OF_LOAD(runtime.cuda.library, runtime.cuda.contextGetCurrent,
"cuCtxGetCurrent");
OF_LOAD(runtime.cuda.library, runtime.cuda.contextSetCurrent,
"cuCtxSetCurrent");
OF_LOAD(runtime.cuda.library, runtime.cuda.contextSynchronize,
"cuCtxSynchronize");
OF_LOAD(runtime.cuda.library, runtime.cuda.memcpy2D,
"cuMemcpy2D_v2");
/* These are diagnostic-only and may be absent on very old drivers. */
runtime.cuda.getErrorName =
(PFN_cuGetErrorName)dlsym(runtime.cuda.library, "cuGetErrorName");
runtime.cuda.getErrorString =
(PFN_cuGetErrorString)dlsym(runtime.cuda.library,
"cuGetErrorString");
runtime.opticalFlowLibrary = ofOpenLibrary(
"libnvidia-opticalflow.so.1", "libnvidia-opticalflow.so");
if (!runtime.opticalFlowLibrary) {
snprintf(runtime.error, sizeof(runtime.error),
"opticalFlow: could not load "
"libnvidia-opticalflow.so.1: %s", dlerror());
return 0;
}
PFN_NvOFAPICreateInstanceCuda createInstance = NULL;
OF_LOAD(runtime.opticalFlowLibrary, createInstance,
"NvOFAPICreateInstanceCuda");
NV_OF_STATUS status = createInstance(NV_OF_API_VERSION, &runtime.of);
if (status != NV_OF_SUCCESS) {
snprintf(runtime.error, sizeof(runtime.error),
"opticalFlow: NvOFAPICreateInstanceCuda failed (%d)",
status);
return 0;
}
runtime.ready = 1;
return 1;
#endif
}
static void ofCUDAError(char *error, size_t size, const char *operation,
CUresult result)
{
const char *name = NULL, *description = NULL;
if (runtime.cuda.getErrorName)
runtime.cuda.getErrorName(result, &name);
if (runtime.cuda.getErrorString)
runtime.cuda.getErrorString(result, &description);
snprintf(error, size, "opticalFlow: %s failed: %s%s%s (%d)",
operation, name ? name : "CUDA error",
description ? ": " : "", description ? description : "",
result);
}
static void ofSDKError(char *error, size_t size, const char *operation,
NV_OF_STATUS status, NvOFHandle handle)
{
char detail[256] = {0};
uint32_t detailSize = sizeof(detail);
if (handle && runtime.of.nvOFGetLastError)
runtime.of.nvOFGetLastError(handle, detail, &detailSize);
snprintf(error, size, "opticalFlow: %s failed (%d)%s%s",
operation, status, detail[0] ? ": " : "", detail);
}
static void ofCopyGrayRow(Image image, uint32_t y, uint8_t *output)
{
const uint8_t *row = image.data + (size_t)y * image.bytesPerRow;
if (image.components == 1) {
memcpy(output, row, image.width);
return;
}
for (uint32_t x = 0; x < image.width; x++) {
const uint8_t *pixel = row + (size_t)x * image.components;
output[x] = image.components >= 3
? (uint8_t)((77u * pixel[0] + 150u * pixel[1] +
29u * pixel[2] + 128u) >> 8)
: pixel[0];
}
}
static void ofCopyGrayImage(Image image, uint8_t *output,
size_t outputPitch)
{
if (image.components == 1 && image.bytesPerRow == image.width &&
outputPitch == image.width) {
memcpy(output, image.data, (size_t)image.width * image.height);
return;
}
for (uint32_t y = 0; y < image.height; y++)
ofCopyGrayRow(image, y,
output + (size_t)y * outputPitch);
}
static CUresult ofUploadGray(Image image, CUdeviceptr destination,
size_t destinationPitch)
{
ofCopyGrayImage(image, session.grayFrame, image.width);
CUDA_MEMCPY2D copy = {0};
copy.srcMemoryType = CU_MEMORYTYPE_HOST;
copy.srcHost = session.grayFrame;
copy.srcPitch = image.width;
copy.dstMemoryType = CU_MEMORYTYPE_DEVICE;
copy.dstDevice = destination;
copy.dstPitch = destinationPitch;
copy.WidthInBytes = image.width;
copy.Height = image.height;
return runtime.cuda.memcpy2D(©);
}
static void ofDestroyBuffers(void)
{
if (session.output)
runtime.of.nvOFDestroyGPUBufferCuda(session.output);
if (session.reference)
runtime.of.nvOFDestroyGPUBufferCuda(session.reference);
if (session.input)
runtime.of.nvOFDestroyGPUBufferCuda(session.input);
session.input = NULL;
session.reference = NULL;
session.output = NULL;
session.inputPointer = 0;
session.referencePointer = 0;
session.outputPointer = 0;
session.referenceUniq = 0;
session.referenceValid = 0;
memset(&session.inputStride, 0, sizeof(session.inputStride));
memset(&session.referenceStride, 0, sizeof(session.referenceStride));
memset(&session.outputStride, 0, sizeof(session.outputStride));
free(session.grayFrame);
session.grayFrame = NULL;
session.grayFrameSize = 0;
session.width = 0;
session.height = 0;
session.flowWidth = 0;
session.flowHeight = 0;
}
static void ofDestroyHandle(void)
{
ofDestroyBuffers();
if (session.handle) runtime.of.nvOFDestroy(session.handle);
session.handle = NULL;
session.gridSize = 0;
}
static int ofCreateHandle(char *error, size_t errorSize)
{
NV_OF_STATUS status = runtime.of.nvCreateOpticalFlowCuda(
session.context, &session.handle);
if (status != NV_OF_SUCCESS) {
ofSDKError(error, errorSize, "nvCreateOpticalFlowCuda", status,
session.handle);
session.handle = NULL;
return 0;
}
uint32_t capCount = 0;
status = runtime.of.nvOFGetCaps(
session.handle, NV_OF_CAPS_SUPPORTED_OUTPUT_GRID_SIZES,
NULL, &capCount);
if (status != NV_OF_SUCCESS || !capCount) {
ofSDKError(error, errorSize, "nvOFGetCaps", status,
session.handle);
ofDestroyHandle();
return 0;
}
uint32_t *gridSizes = malloc(sizeof(*gridSizes) * capCount);
if (!gridSizes) {
snprintf(error, errorSize, "opticalFlow: out of host memory");
ofDestroyHandle();
return 0;
}
status = runtime.of.nvOFGetCaps(
session.handle, NV_OF_CAPS_SUPPORTED_OUTPUT_GRID_SIZES,
gridSizes, &capCount);
if (status == NV_OF_SUCCESS) {
for (uint32_t i = 0; i < capCount; i++) {
uint32_t candidate = gridSizes[i];
if ((candidate == 1 || candidate == 2 || candidate == 4) &&
(!session.gridSize || candidate < session.gridSize))
session.gridSize = candidate;
}
}
free(gridSizes);
if (status != NV_OF_SUCCESS || !session.gridSize) {
ofSDKError(error, errorSize, "output-grid query", status,
session.handle);
ofDestroyHandle();
return 0;
}
return 1;
}
static int ofConfigure(uint32_t width, uint32_t height,
char *error, size_t errorSize)
{
if (session.width == width && session.height == height &&
session.input && session.reference && session.output)
return 1;
if (session.width || session.height) {
ofDestroyHandle();
if (!ofCreateHandle(error, errorSize)) return 0;
} else {
ofDestroyBuffers();
if (!session.handle && !ofCreateHandle(error, errorSize))
return 0;
}
NV_OF_INIT_PARAMS init = {0};
init.width = width;
init.height = height;
init.outGridSize = session.gridSize;
init.mode = NV_OF_MODE_OPTICALFLOW;
init.perfLevel = NV_OF_PERF_LEVEL_MEDIUM;
NV_OF_STATUS status = runtime.of.nvOFInit(session.handle, &init);
if (status != NV_OF_SUCCESS) {
ofSDKError(error, errorSize, "nvOFInit", status,
session.handle);
goto fail;
}
uint32_t flowWidth =
(width + session.gridSize - 1) / session.gridSize;
uint32_t flowHeight =
(height + session.gridSize - 1) / session.gridSize;
NV_OF_BUFFER_DESCRIPTOR inputDescription = {
width, height, NV_OF_BUFFER_USAGE_INPUT,
NV_OF_BUFFER_FORMAT_GRAYSCALE8
};
NV_OF_BUFFER_DESCRIPTOR outputDescription = {
flowWidth, flowHeight, NV_OF_BUFFER_USAGE_OUTPUT,
NV_OF_BUFFER_FORMAT_SHORT2
};
status = runtime.of.nvOFCreateGPUBufferCuda(
session.handle, &inputDescription,
NV_OF_CUDA_BUFFER_TYPE_CUDEVICEPTR, &session.input);
if (status != NV_OF_SUCCESS) {
ofSDKError(error, errorSize, "create input buffer", status,
session.handle);
goto fail;
}
status = runtime.of.nvOFCreateGPUBufferCuda(
session.handle, &inputDescription,
NV_OF_CUDA_BUFFER_TYPE_CUDEVICEPTR, &session.reference);
if (status != NV_OF_SUCCESS) {
ofSDKError(error, errorSize, "create reference buffer", status,
session.handle);
goto fail;
}
status = runtime.of.nvOFCreateGPUBufferCuda(
session.handle, &outputDescription,
NV_OF_CUDA_BUFFER_TYPE_CUDEVICEPTR, &session.output);
if (status != NV_OF_SUCCESS) {
ofSDKError(error, errorSize, "create output buffer", status,
session.handle);
goto fail;
}
if (runtime.of.nvOFGPUBufferGetStrideInfo(
session.input, &session.inputStride) != NV_OF_SUCCESS ||
runtime.of.nvOFGPUBufferGetStrideInfo(
session.reference, &session.referenceStride) !=
NV_OF_SUCCESS ||
runtime.of.nvOFGPUBufferGetStrideInfo(
session.output, &session.outputStride) != NV_OF_SUCCESS ||
!session.inputStride.strideInfo[0].strideXInBytes ||
!session.referenceStride.strideInfo[0].strideXInBytes ||
!session.outputStride.strideInfo[0].strideXInBytes) {
snprintf(error, errorSize,
"opticalFlow: NVIDIA returned invalid buffer strides");
goto fail;
}
session.inputPointer =
runtime.of.nvOFGPUBufferGetCUdeviceptr(session.input);
session.referencePointer =
runtime.of.nvOFGPUBufferGetCUdeviceptr(session.reference);
session.outputPointer =
runtime.of.nvOFGPUBufferGetCUdeviceptr(session.output);
if (!session.inputPointer || !session.referencePointer ||
!session.outputPointer) {
snprintf(error, errorSize,
"opticalFlow: NVIDIA returned a null CUDA buffer");
goto fail;
}
session.grayFrameSize = (size_t)width * height;
session.grayFrame = malloc(session.grayFrameSize);
if (!session.grayFrame) {
snprintf(error, errorSize, "opticalFlow: out of host memory");
goto fail;
}
session.width = width;
session.height = height;
session.flowWidth = flowWidth;
session.flowHeight = flowHeight;
return 1;
fail:
ofDestroyHandle();
return 0;
}
}
$cc proc initOpticalFlow {} void {
if (!ofLoadRuntime()) FOLK_ERROR("%s", runtime.error);
char error[512] = {0};
int success = 0;
pthread_mutex_lock(&session.mutex);
if (session.initialized) {
pthread_mutex_unlock(&session.mutex);
return;
}
CUresult cudaResult = runtime.cuda.init(0);
if (cudaResult != CUDA_SUCCESS) {
ofCUDAError(error, sizeof(error), "cuInit", cudaResult);
goto done;
}
CUcontext previousContext = NULL;
runtime.cuda.contextGetCurrent(&previousContext);
int deviceCount = 0;
cudaResult = runtime.cuda.deviceGetCount(&deviceCount);
if (cudaResult != CUDA_SUCCESS) {
ofCUDAError(error, sizeof(error), "cuDeviceGetCount", cudaResult);
goto restore;
}
for (int i = 0; i < deviceCount; i++) {
CUdevice device;
if (runtime.cuda.deviceGet(&device, i) != CUDA_SUCCESS) continue;
if (runtime.cuda.contextCreate(&session.context, 0, device) !=
CUDA_SUCCESS)
continue;
if (ofCreateHandle(error, sizeof(error))) {
success = 1;
break;
}
runtime.cuda.contextDestroy(session.context);
session.context = NULL;
}
if (!success)
snprintf(error, sizeof(error),
"opticalFlow: no CUDA device supports NVIDIA Optical Flow");
restore:
runtime.cuda.contextSetCurrent(previousContext);
if (success) session.initialized = 1;
done:
pthread_mutex_unlock(&session.mutex);
if (!success) FOLK_ERROR("%s", error);
}
$cc proc shutdownOpticalFlow {} void {
pthread_mutex_lock(&session.mutex);
if (session.initialized) {
CUcontext previousContext = NULL;
CUcontext destroyedContext = session.context;
runtime.cuda.contextGetCurrent(&previousContext);
runtime.cuda.contextSetCurrent(destroyedContext);
ofDestroyHandle();
runtime.cuda.contextDestroy(destroyedContext);
session.context = NULL;
session.initialized = 0;
runtime.cuda.contextSetCurrent(
previousContext == destroyedContext ? NULL : previousContext);
}
pthread_mutex_unlock(&session.mutex);
}
$cc proc runOpticalFlow {Image f0 Image f1} OpticalFlow* {
if (!f0.data || !f1.data || !f0.width || !f0.height)
FOLK_ERROR("opticalFlow: frames must be non-empty images");
if (f0.width != f1.width || f0.height != f1.height)
FOLK_ERROR("opticalFlow: frame dimensions differ (%ux%u and %ux%u)",
f0.width, f0.height, f1.width, f1.height);
if (f0.components < 1 || f1.components < 1)
FOLK_ERROR("opticalFlow: frames have no image components");
char error[512] = {0};
CUresult cudaResult;
NV_OF_STATUS status;
NV_OF_FLOW_VECTOR *hostFlow = NULL;
OpticalFlow *answer = NULL;
CUcontext previousContext = NULL;
pthread_mutex_lock(&session.mutex);
if (!session.initialized) {
snprintf(error, sizeof(error),
"opticalFlow: library was not initialized");
goto cleanup;
}
runtime.cuda.contextGetCurrent(&previousContext);
cudaResult = runtime.cuda.contextSetCurrent(session.context);
if (cudaResult != CUDA_SUCCESS) {
ofCUDAError(error, sizeof(error), "cuCtxSetCurrent", cudaResult);
goto cleanup;
}
if (!ofConfigure(f0.width, f0.height, error, sizeof(error)))
goto cleanup;
int reusePreviousReference =
session.referenceValid && f0.uniq &&
f0.uniq == session.referenceUniq;
session.referenceValid = 0;
if (reusePreviousReference) {
NvOFGPUBufferHandle buffer = session.input;
session.input = session.reference;
session.reference = buffer;
NV_OF_CUDA_BUFFER_STRIDE_INFO stride = session.inputStride;
session.inputStride = session.referenceStride;
session.referenceStride = stride;
CUdeviceptr pointer = session.inputPointer;
session.inputPointer = session.referencePointer;
session.referencePointer = pointer;
} else {
cudaResult = ofUploadGray(
f0, session.inputPointer,
session.inputStride.strideInfo[0].strideXInBytes);
if (cudaResult != CUDA_SUCCESS) {
ofCUDAError(error, sizeof(error), "input upload", cudaResult);
goto cleanup;
}
}
cudaResult = ofUploadGray(
f1, session.referencePointer,
session.referenceStride.strideInfo[0].strideXInBytes);
if (cudaResult != CUDA_SUCCESS) {
ofCUDAError(error, sizeof(error), "reference upload", cudaResult);
goto cleanup;
}
NV_OF_EXECUTE_INPUT_PARAMS executeInput = {0};
executeInput.inputFrame = session.input;
executeInput.referenceFrame = session.reference;
executeInput.disableTemporalHints = NV_OF_TRUE;
NV_OF_EXECUTE_OUTPUT_PARAMS executeOutput = {0};
executeOutput.outputBuffer = session.output;
status = runtime.of.nvOFExecute(
session.handle, &executeInput, &executeOutput);
if (status != NV_OF_SUCCESS) {
ofSDKError(error, sizeof(error), "nvOFExecute", status,
session.handle);
goto cleanup;
}
cudaResult = runtime.cuda.contextSynchronize();
if (cudaResult != CUDA_SUCCESS) {
ofCUDAError(error, sizeof(error), "cuCtxSynchronize", cudaResult);
goto cleanup;
}
session.referenceUniq = f1.uniq;
session.referenceValid = f1.uniq != 0;
hostFlow = malloc(sizeof(*hostFlow) *
(size_t)session.flowWidth * session.flowHeight);
if (!hostFlow) {
snprintf(error, sizeof(error), "opticalFlow: out of host memory");
goto cleanup;
}
size_t outputPitch =
session.outputStride.strideInfo[0].strideXInBytes;
size_t outputRowBytes =
(size_t)session.flowWidth * sizeof(*hostFlow);
CUDA_MEMCPY2D download = {0};
download.srcMemoryType = CU_MEMORYTYPE_DEVICE;
download.srcDevice = session.outputPointer;
download.srcPitch = outputPitch;
download.dstMemoryType = CU_MEMORYTYPE_HOST;
download.dstHost = hostFlow;
download.dstPitch = outputRowBytes;
download.WidthInBytes = outputRowBytes;
download.Height = session.flowHeight;
cudaResult = runtime.cuda.memcpy2D(&download);
if (cudaResult != CUDA_SUCCESS) {
ofCUDAError(error, sizeof(error), "flow download", cudaResult);
goto cleanup;
}
answer = malloc(sizeof(*answer));
if (!answer) {
snprintf(error, sizeof(error), "opticalFlow: out of host memory");
goto cleanup;
}
*answer = (OpticalFlow) {
.width = session.flowWidth,
.height = session.flowHeight,
.gridSize = session.gridSize,
.vectors = hostFlow,
};
hostFlow = NULL;
cleanup:
free(hostFlow);
if (session.initialized)
runtime.cuda.contextSetCurrent(previousContext);
pthread_mutex_unlock(&session.mutex);
if (!answer) FOLK_ERROR("%s", error);
return answer;
}
$cc proc sampleOpticalFlow {OpticalFlow* flow double pixelX double pixelY} Jim_Obj* {
FOLK_ENSURE(flow && flow->vectors && flow->width && flow->height &&
flow->gridSize);
int gridX = (int)(pixelX / flow->gridSize);
int gridY = (int)(pixelY / flow->gridSize);
if (gridX < 0) gridX = 0;
if (gridY < 0) gridY = 0;
if (gridX >= (int)flow->width) gridX = (int)flow->width - 1;
if (gridY >= (int)flow->height) gridY = (int)flow->height - 1;
NV_OF_FLOW_VECTOR vector =
flow->vectors[(size_t)gridY * flow->width + gridX];
Jim_Obj *xy[2] = {
Jim_NewDoubleObj(interp, vector.flowx / 32.0),
Jim_NewDoubleObj(interp, vector.flowy / 32.0),
};
return Jim_NewListObj(interp, xy, 2);
}
$cc proc opticalFlowTransferInfo {} Jim_Obj* {
pthread_mutex_lock(&session.mutex);
Jim_Obj *items[16] = {
Jim_NewStringObj(interp, "width", -1),
Jim_NewIntObj(interp, session.width),
Jim_NewStringObj(interp, "height", -1),
Jim_NewIntObj(interp, session.height),
Jim_NewStringObj(interp, "inputStrideX", -1),
Jim_NewIntObj(interp,
session.inputStride.strideInfo[0].strideXInBytes),
Jim_NewStringObj(interp, "inputStrideY", -1),
Jim_NewIntObj(interp,
session.inputStride.strideInfo[0].strideYInBytes),
Jim_NewStringObj(interp, "flowWidth", -1),
Jim_NewIntObj(interp, session.flowWidth),
Jim_NewStringObj(interp, "flowHeight", -1),
Jim_NewIntObj(interp, session.flowHeight),
Jim_NewStringObj(interp, "outputStrideX", -1),
Jim_NewIntObj(interp,
session.outputStride.strideInfo[0].strideXInBytes),
Jim_NewStringObj(interp, "outputStrideY", -1),
Jim_NewIntObj(interp,
session.outputStride.strideInfo[0].strideYInBytes),
};
Jim_Obj *answer = Jim_NewListObj(interp, items, 16);
pthread_mutex_unlock(&session.mutex);
return answer;
}
$cc proc freeOpticalFlow {OpticalFlow* flow} void {
if (!flow) return;
free(flow->vectors);
free(flow);
}
set opticalFlowLib [$cc compile]
$opticalFlowLib initOpticalFlow
return $opticalFlowLib
}
When the image library is /imageLib/ {
set opticalFlowLib [opticalFlowInit $imageLib]
Claim the optical flow library is $opticalFlowLib \
-destructor [list $opticalFlowLib shutdownOpticalFlow]
}