This commit is contained in:
Ubuntu
2026-07-03 16:58:28 +08:00
commit 32b43f85bd
6240 changed files with 1987678 additions and 0 deletions
+59
View File
@@ -0,0 +1,59 @@
/**
* AWB Processor - OpenCV Gray World White Balance
* Compiled to WASM via Emscripten (MAIN_MODULE, dynamic linking with .so)
*
* Reads: /data/input.png (MEMFS, injected by Node.js runner)
* Writes: /data/output.png (MEMFS, read by Node.js runner)
* Stdout: metadata string
*/
#include <stdio.h>
#include <opencv2/core.hpp>
#include <opencv2/imgproc.hpp>
#include <opencv2/imgcodecs.hpp>
int main() {
const char* input = "/data/input.png";
const char* output = "/data/output.png";
cv::Mat img = cv::imread(input);
if (img.empty()) {
fprintf(stderr, "ERR:Failed to read: %s\n", input);
return 1;
}
int h = img.rows, w = img.cols;
// Convert to float for accuracy
cv::Mat imgf;
img.convertTo(imgf, CV_32FC3);
// Split channels (BGR order in OpenCV)
std::vector<cv::Mat> ch;
cv::split(imgf, ch);
// Compute channel means
cv::Scalar m = cv::mean(imgf);
// Gray World: average of all channels
float avg = (m[0] + m[1] + m[2]) / 3.0f;
// Scale each channel so its mean equals the global average
if (m[0] > 0.01f) ch[0] *= (avg / m[0]); // B
if (m[1] > 0.01f) ch[1] *= (avg / m[1]); // G
if (m[2] > 0.01f) ch[2] *= (avg / m[2]); // R
// Merge and convert back
cv::merge(ch, imgf);
imgf.convertTo(img, CV_8UC3);
// Write output
cv::imwrite(output, img);
// Metadata to stderr (runner reads pure base64 from stdout)
fprintf(stderr, "OK:%dx%d:M=%.2f,%.2f,%.2f:S=%.4f,%.4f,%.4f\n",
w, h,
m[2], m[1], m[0], // RGB means
avg / m[2], avg / m[1], avg / m[0]); // RGB scales
return 0;
}
File diff suppressed because one or more lines are too long
BIN
View File
Binary file not shown.
+33
View File
@@ -0,0 +1,33 @@
# the minimum version of CMake.
cmake_minimum_required(VERSION 3.5.0)
project(urologist)
set(NATIVERENDER_ROOT_PATH ${CMAKE_CURRENT_SOURCE_DIR})
if(DEFINED PACKAGE_FIND_FILE)
include(${PACKAGE_FIND_FILE})
endif()
list(APPEND CMAKE_PREFIX_PATH ${NATIVERENDER_ROOT_PATH}/thirdparty/opencv/${OHOS_ARCH}/lib/cmake/opencv4)
set(OpenCV_DIR ${CMAKE_CURRENT_SOURCE_DIR}/thirdparty/opencv/${OHOS_ARCH}/lib/cmake/opencv4)
find_package(OpenCV REQUIRED)
include_directories(${NATIVERENDER_ROOT_PATH}
${NATIVERENDER_ROOT_PATH}/thirdparty/opencv/${OHOS_ARCH}/include/opencv4)
add_library(
entry
SHARED
napi_init.cpp
PaperRecognizer.cpp
PaperConfig.cpp
ColorReader.cpp)
#add_library(entry SHARED napi_init.cpp)
#target_link_libraries(entry PUBLIC libace_napi.z.so)
#target_link_libraries(entry PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/thirdparty/xxx/${OHOS_ARCH}/lib/libxxx.so)
target_link_libraries(entry PUBLIC libace_napi.z.so libhilog_ndk.z.so libpixelmap_ndk.z.so ${OpenCV_LIBS})
+177
View File
@@ -0,0 +1,177 @@
#include "ColorReader.h"
#include "GlobalParams.h"
#include <cmath>
#include <algorithm>
ColorReader::ColorReader() {
}
ColorReader::~ColorReader() {
}
std::vector<ColorResult> ColorReader::readColors(
const cv::Mat& image,
const std::vector<cv::Point2f>& centers,
bool smallLocatorOnLeft,
bool applyWB) {
std::vector<ColorResult> results;
if (image.empty() || centers.empty()) {
return results;
}
// 复制图像用于颜色读取(避免绘图干扰)
cv::Mat colorImage = image.clone();
// 白平衡处理(可选)
if (applyWB) {
applyWhiteBalance(colorImage);
}
// 确定采样半径
int radius = GlobalParams::DEFAULT_SAMPLE_RADIUS;
// 采样每个色块的颜色
for (size_t i = 0; i < centers.size(); ++i) {
ColorResult result;
result.id = static_cast<int>(i);
// 采样颜色
cv::Scalar bgr = sampleColor(colorImage, centers[i], radius);
// 转换颜色空间
convertColorSpaces(bgr, result.h, result.s, result.v,
result.l, result.a, result.b);
result.r = bgr[2];
result.g = bgr[1];
result.b_val = bgr[0];
results.push_back(result);
}
// 调整顺序(确保从靠近小定位块的色块开始)
results = adjustOrder(results, smallLocatorOnLeft);
return results;
}
void ColorReader::applyWhiteBalance(cv::Mat& image) {
if (image.empty()) {
return;
}
// 确保是BGR格式
cv::Mat bgrImage;
if (image.channels() == 4) {
cv::cvtColor(image, bgrImage, cv::COLOR_BGRA2BGR);
} else if (image.channels() == 1) {
cv::cvtColor(image, bgrImage, cv::COLOR_GRAY2BGR);
} else {
bgrImage = image;
}
// 计算各通道均值
cv::Scalar sumChannels = cv::sum(bgrImage);
int totalPixels = bgrImage.rows * bgrImage.cols;
if (totalPixels == 0) {
return;
}
double avgB = sumChannels[0] / totalPixels;
double avgG = sumChannels[1] / totalPixels;
double avgR = sumChannels[2] / totalPixels;
// 避免除零
if (avgB < 1) avgB = 1;
if (avgG < 1) avgG = 1;
if (avgR < 1) avgR = 1;
// 计算目标均值(三通道平均)
double targetAvg = (avgB + avgG + avgR) / 3.0;
if (targetAvg < 1) {
return;
}
// 各通道缩放校正
std::vector<cv::Mat> channels(3);
cv::split(bgrImage, channels);
channels[0] = channels[0] * (targetAvg / avgB);
channels[1] = channels[1] * (targetAvg / avgG);
channels[2] = channels[2] * (targetAvg / avgR);
cv::merge(channels, bgrImage);
bgrImage.convertTo(image, CV_8UC3);
}
cv::Scalar ColorReader::sampleColor(const cv::Mat& image, const cv::Point2f& center, int radius) {
int cx = static_cast<int>(center.x);
int cy = static_cast<int>(center.y);
// 确保采样区域在图像内
int x1 = std::max(0, cx - radius);
int y1 = std::max(0, cy - radius);
int x2 = std::min(image.cols - 1, cx + radius);
int y2 = std::min(image.rows - 1, cy + radius);
if (x2 <= x1 || y2 <= y1) {
return cv::Scalar(0, 0, 0);
}
// 创建采样掩码(圆形)
cv::Mat roi = image(cv::Rect(x1, y1, x2 - x1 + 1, y2 - y1 + 1));
cv::Mat mask = cv::Mat::zeros(roi.size(), CV_8U);
cv::Point maskCenter(cx - x1, cy - y1);
cv::circle(mask, maskCenter, radius, cv::Scalar(255), -1);
// 计算掩码区域的平均颜色
cv::Scalar avgColor = cv::mean(roi, mask);
return avgColor;
}
void ColorReader::convertColorSpaces(const cv::Scalar& bgr,
double& h, double& s, double& v,
double& l, double& a, double& b) {
// 创建1x1像素的图像进行颜色转换
cv::Mat bgrMat(1, 1, CV_8UC3, cv::Scalar(bgr[0], bgr[1], bgr[2]));
// 转换到HSV
cv::Mat hsvMat;
cv::cvtColor(bgrMat, hsvMat, cv::COLOR_BGR2HSV);
cv::Vec3b hsv = hsvMat.at<cv::Vec3b>(0, 0);
h = hsv[0] * 2.0; // OpenCV的H范围是0-180,转换为0-360
s = hsv[1];
v = hsv[2];
// 转换到LAB
cv::Mat labMat;
cv::cvtColor(bgrMat, labMat, cv::COLOR_BGR2Lab);
cv::Vec3b lab = labMat.at<cv::Vec3b>(0, 0);
l = lab[0];
a = static_cast<double>(lab[1]) - 128.0; // a范围从0-255转为-128到127
b = static_cast<double>(lab[2]) - 128.0; // b范围从0-255转为-128到127
}
std::vector<ColorResult> ColorReader::adjustOrder(
std::vector<ColorResult>& colors,
bool smallLocatorOnLeft) {
// 如果小定位块在左边,颜色顺序已经是正确的
// 如果小定位块在右边,需要反转顺序
if (!smallLocatorOnLeft && colors.size() > 1) {
std::reverse(colors.begin(), colors.end());
}
// 重新编号
for (size_t i = 0; i < colors.size(); ++i) {
colors[i].id = static_cast<int>(i);
}
return colors;
}
+37
View File
@@ -0,0 +1,37 @@
#pragma once
#include "Types.h"
#include <opencv2/opencv.hpp>
#include <vector>
class ColorReader {
public:
ColorReader();
~ColorReader();
// 执行颜色读取
std::vector<ColorResult> readColors(
const cv::Mat& image,
const std::vector<cv::Point2f>& centers,
bool smallLocatorOnLeft,
bool applyWB = true // 是否应用白平衡
);
private:
// 白平衡处理
void applyWhiteBalance(cv::Mat& image);
// 采样颜色
cv::Scalar sampleColor(const cv::Mat& image, const cv::Point2f& center, int radius);
// 颜色空间转换
void convertColorSpaces(const cv::Scalar& bgr,
double& h, double& s, double& v,
double& l, double& a, double& b);
// 调整顺序(始终从靠近小定位块开始)
std::vector<ColorResult> adjustOrder(
std::vector<ColorResult>& colors,
bool smallLocatorOnLeft
);
};
+159
View File
@@ -0,0 +1,159 @@
#include "PaperConfig.h"
#include "GlobalParams.h"
#include <cmath>
PaperConfigManager& PaperConfigManager::getInstance() {
static PaperConfigManager instance;
return instance;
}
PaperConfigManager::PaperConfigManager() {
initConfigs();
}
void PaperConfigManager::initConfigs() {
// PST_14_S: 14联, 10mm大定位块
PaperConfig config14s;
config14s.type = PaperSpatialType::PST_14_S;
config14s.blockItems = BlockItems::BI_14;
config14s.locatorType = BigLocatorType::BLT_SHORT;
config14s.itemsNum = 14;
config14s.slicesNum = 50;
config14s.firstPos = 2.25;
config14s.step = 3.5;
config14s.distTimes = 21.3;
config14s.whBigRatio = GlobalParams::BIG_LOCATOR_RATIO_SHORT;
configs_[PaperSpatialType::PST_14_S] = config14s;
// PST_14_L: 14联, 15mm大定位块
PaperConfig config14l;
config14l.type = PaperSpatialType::PST_14_L;
config14l.blockItems = BlockItems::BI_14;
config14l.locatorType = BigLocatorType::BLT_LONG;
config14l.itemsNum = 14;
config14l.slicesNum = 50;
config14l.firstPos = 2.25;
config14l.step = 3.5;
config14l.distTimes = 21.8;
config14l.whBigRatio = GlobalParams::BIG_LOCATOR_RATIO_LONG;
configs_[PaperSpatialType::PST_14_L] = config14l;
// PST_11_S: 11联, 10mm大定位块
PaperConfig config11s;
config11s.type = PaperSpatialType::PST_11_S;
config11s.blockItems = BlockItems::BI_11;
config11s.locatorType = BigLocatorType::BLT_SHORT;
config11s.itemsNum = 11;
config11s.slicesNum = 39;
config11s.firstPos = 2.25;
config11s.step = 3.5;
config11s.distTimes = 17.6;
config11s.whBigRatio = GlobalParams::BIG_LOCATOR_RATIO_SHORT;
configs_[PaperSpatialType::PST_11_S] = config11s;
// PST_11_L: 11联, 15mm大定位块
PaperConfig config11l;
config11l.type = PaperSpatialType::PST_11_L;
config11l.blockItems = BlockItems::BI_11;
config11l.locatorType = BigLocatorType::BLT_LONG;
config11l.itemsNum = 11;
config11l.slicesNum = 39;
config11l.firstPos = 2.25;
config11l.step = 3.5;
config11l.distTimes = 18.1;
config11l.whBigRatio = GlobalParams::BIG_LOCATOR_RATIO_LONG;
configs_[PaperSpatialType::PST_11_L] = config11l;
// PST_8_S: 8联, 10mm大定位块
PaperConfig config8s;
config8s.type = PaperSpatialType::PST_8_S;
config8s.blockItems = BlockItems::BI_8;
config8s.locatorType = BigLocatorType::BLT_SHORT;
config8s.itemsNum = 8;
config8s.slicesNum = 29;
config8s.firstPos = 2.25;
config8s.step = 3.5;
config8s.distTimes = 12.9;
config8s.whBigRatio = GlobalParams::BIG_LOCATOR_RATIO_SHORT;
configs_[PaperSpatialType::PST_8_S] = config8s;
// PST_8_L: 8联, 15mm大定位块
PaperConfig config8l;
config8l.type = PaperSpatialType::PST_8_L;
config8l.blockItems = BlockItems::BI_8;
config8l.locatorType = BigLocatorType::BLT_LONG;
config8l.itemsNum = 8;
config8l.slicesNum = 29;
config8l.firstPos = 2.25;
config8l.step = 3.5;
config8l.distTimes = 13.4;
config8l.whBigRatio = GlobalParams::BIG_LOCATOR_RATIO_LONG;
configs_[PaperSpatialType::PST_8_L] = config8l;
}
const PaperConfig* PaperConfigManager::getConfig(PaperSpatialType type) const {
auto it = configs_.find(type);
if (it != configs_.end()) {
return &it->second;
}
return nullptr;
}
PaperSpatialType PaperConfigManager::matchConfig(double distRatio, double areaRatio,
BigLocatorType locatorType) const {
// 参照老代码 ImgLocateDryChemistry::verifyPaperConfig 的逻辑
// 先根据面积比判断大/小规格,再根据距离比判断具体联数
// 面积比范围定义(参照老代码)
double Min_AreaRadio_S = 2.6;
double Max_AreaRadio_S = 4;
double Min_AreaRadio_L = 4.2;
double Max_AreaRadio_L = 6.0;
// 距离比范围定义(基于实际数据统计)
// S型(10mm大定位块)
// 14S典型值: 20.5-24.0(考虑不同拍摄距离), 11S典型值: 16.5-18.5
// 临界区域 [18.5, 19.0] 作为空白缓冲,避免误判
double Min_DistRadio_14S = 19.0, Max_DistRadio_14S = 26.5; // 14S: 19.0-26.5(扩大上限覆盖远距离拍摄)
double Min_DistRadio_11S = 15.5, Max_DistRadio_11S = 18.5; // 11S: 15.5-18.5(与14S留0.5缓冲)
double Min_DistRadio_8S = 11.6, Max_DistRadio_8S = 14.1;
// L型(15mm大定位块)
double Min_DistRadio_14L = 19.0, Max_DistRadio_14L = 26.98; // 同步调整
double Min_DistRadio_11L = 15.5, Max_DistRadio_11L = 18.5; // 同步调整
double Min_DistRadio_8L = 12.0, Max_DistRadio_8L = 14.5;
// 检查面积比是否在有效范围内
if (areaRatio < Min_AreaRadio_S || areaRatio > Max_AreaRadio_L) {
return PaperSpatialType::PST_UNKNOWN;
}
// 空白区间 [4.0, 4.2] 不做判断,避免误判
if (areaRatio > Max_AreaRadio_S && areaRatio < Min_AreaRadio_L) {
return PaperSpatialType::PST_UNKNOWN;
}
// 根据面积比判断大/小规格
bool isSmallType = (areaRatio <= Max_AreaRadio_S);
if (isSmallType) {
// S型(10mm大定位块)
if (distRatio > Min_DistRadio_14S && distRatio < Max_DistRadio_14S) {
return PaperSpatialType::PST_14_S;
} else if (distRatio > Min_DistRadio_11S && distRatio < Max_DistRadio_11S) {
return PaperSpatialType::PST_11_S;
} else if (distRatio > Min_DistRadio_8S && distRatio < Max_DistRadio_8S) {
return PaperSpatialType::PST_8_S;
}
} else {
// L型(15mm大定位块)
if (distRatio > Min_DistRadio_14L && distRatio < Max_DistRadio_14L) {
return PaperSpatialType::PST_14_L;
} else if (distRatio > Min_DistRadio_11L && distRatio < Max_DistRadio_11L) {
return PaperSpatialType::PST_11_L;
} else if (distRatio > Min_DistRadio_8L && distRatio < Max_DistRadio_8L) {
return PaperSpatialType::PST_8_L;
}
}
return PaperSpatialType::PST_UNKNOWN;
}
+42
View File
@@ -0,0 +1,42 @@
#pragma once
#include "Types.h"
#include <map>
// 单个试纸规格配置
struct PaperConfig {
PaperSpatialType type; // 试纸类型
BlockItems blockItems; // 联数
BigLocatorType locatorType; // 大定位块类型
int itemsNum; // 反应色块数量
int slicesNum; // 最小分割单位数量
double firstPos; // 第一个色块位置倍数
double step; // 步长倍数
double distTimes; // 定位块中心距离/试纸高度
double whBigRatio; // 大定位块宽高比
PaperConfig() : type(PaperSpatialType::PST_UNKNOWN),
blockItems(BlockItems::BI_14),
locatorType(BigLocatorType::BLT_SHORT),
itemsNum(0), slicesNum(0), firstPos(0),
step(0), distTimes(0), whBigRatio(0) {}
};
// 试纸配置管理器
class PaperConfigManager {
public:
static PaperConfigManager& getInstance();
const PaperConfig* getConfig(PaperSpatialType type) const;
PaperSpatialType matchConfig(double distRatio, double areaRatio,
BigLocatorType locatorType) const;
private:
PaperConfigManager();
PaperConfigManager(const PaperConfigManager&) = delete;
PaperConfigManager& operator=(const PaperConfigManager&) = delete;
void initConfigs();
std::map<PaperSpatialType, PaperConfig> configs_;
};
File diff suppressed because it is too large Load Diff
+87
View File
@@ -0,0 +1,87 @@
#pragma once
#include "Types.h"
#include "PaperConfig.h"
#include <opencv2/opencv.hpp>
#include <vector>
class PaperRecognizer {
public:
PaperRecognizer();
~PaperRecognizer();
// 主识别函数
RecognitionResult recognize(const cv::Mat& image);
// 获取裁剪后的试纸图片(NAPI用)
cv::Mat getPaperImg() const;
private:
// 定位块配对结果
struct LocatorPair {
bool isValid;
LocatorInfo* big;
LocatorInfo* small;
};
// ===== 第一步:图像预处理 =====
void preprocessImage(const cv::Mat& image);
// ===== 第二步:定位块检测 =====
bool detectLocators();
void findCandidateContours(std::vector<std::vector<cv::Point>>& contours);
bool filterLocatorCandidates(const std::vector<std::vector<cv::Point>>& contours);
bool validateLocatorPair();
// 多条试纸处理
std::vector<std::vector<LocatorInfo*>> groupCandidatesByY(std::vector<LocatorInfo>& candidates);
LocatorPair findValidLocatorPairInGroup(std::vector<LocatorInfo*>& group);
// ===== 第三步:颜色判断 =====
LocatorColor determineLocatorColor(const cv::Rect& roi);
LocatorColor determineLocatorColorByCenter(const cv::RotatedRect& rotatedRect);
double calculateColorDistance(const cv::Scalar& c1, const cv::Scalar& c2);
cv::Scalar getAverageColor(const cv::Rect& roi);
cv::Scalar getCenterRegionColor(const cv::RotatedRect& rotatedRect);
// ===== 第四步:规格匹配 =====
bool matchPaperSpec();
// ===== 第五步:色块位置计算 =====
void calculateColorBlockCenters();
// ===== 第六步:绘制标注 =====
void drawAnnotations();
// ===== 第七步:保存试纸图片 =====
void savePaperImg();
// ===== 辅助函数 =====
double calculateAspectRatio(const cv::RotatedRect& rect);
bool isPotentialLocator(const cv::RotatedRect& rect, double area, double& aspectRatio);
void sortLocatorsByPosition();
BigLocatorType determineBigLocatorType(double areaRatio);
// ===== 清晰度评估 =====
BlurLevel assessLocatorBlur(const cv::Mat& gray, const cv::RotatedRect& locator, double& score);
private:
cv::Mat originalImage_; // 原图
cv::Mat processImage_; // 处理用图像
cv::Mat hsvImage_; // HSV图像
cv::Mat grayImage_; // 灰度图像
LocatorInfo locator1_; // 第一个定位块
LocatorInfo locator2_; // 第二个定位块
bool hasLocator1_;
bool hasLocator2_;
LocatorInfo* bigLocator_; // 大定位块指针
LocatorInfo* smallLocator_; // 小定位块指针
std::vector<cv::Point2f> centers_; // 色块中心点
const PaperConfig* config_; // 匹配的配置
RecognitionResult result_; // 结果
cv::Mat paperImg_; // 裁剪后的试纸图片(RGBA格式)
size_t candidateCount_; // 候选定位块数量(用于杂物检测)
};
+92
View File
@@ -0,0 +1,92 @@
#pragma once
#include <opencv2/opencv.hpp>
#include <vector>
#include <string>
// 试纸联数
enum class BlockItems {
BI_14 = 14,
BI_11 = 11,
BI_8 = 8
};
// 大定位块类型
enum class BigLocatorType {
BLT_SHORT = 0, // 10mm
BLT_LONG = 1 // 15mm
};
// 定位块颜色分类
enum class LocatorColor {
UNKNOWN = 0,
RED = 1,
GREEN = 2,
BLUE = 3
};
// 试纸空间类型
enum class PaperSpatialType {
PST_UNKNOWN = 0,
PST_14_S = 1, // 14联, 10mm大定位块
PST_14_L = 2, // 14联, 15mm大定位块
PST_11_S = 3, // 11联, 10mm大定位块
PST_11_L = 4, // 11联, 15mm大定位块
PST_8_S = 5, // 8联, 10mm大定位块
PST_8_L = 6 // 8联, 15mm大定位块
};
// 图像清晰度等级
enum class BlurLevel {
BLUR_NONE = 0, // 清晰
BLUR_SLIGHT = 1, // 轻微模糊
BLUR_SEVERE = 2 // 严重模糊
};
// 定位块信息
struct LocatorInfo {
cv::RotatedRect rotatedRect; // 旋转矩形
cv::Point2f center; // 中心点
double area; // 面积
double width; // 宽度(长边)
double height; // 高度(短边)
LocatorColor color; // 颜色
bool isBig; // 是否大定位块
cv::Scalar avgColor; // 平均RGB颜色
LocatorInfo() : area(0), width(0), height(0),
color(LocatorColor::UNKNOWN), isBig(false) {}
};
// 色块颜色结果
struct ColorResult {
int id; // 编号(从靠近小定位块开始)
double h, s, v; // HSV值
double l, a, b; // LAB值
double r, g, b_val; // RGB值
ColorResult() : id(0), h(0), s(0), v(0), l(0), a(0), b(0),
r(0), g(0), b_val(0) {}
};
// 识别结果
struct RecognitionResult {
bool success; // 是否成功
int errorCode; // 错误码
std::string errorMessage; // 错误信息
PaperSpatialType paperType; // 试纸类型
LocatorColor locatorColor; // 定位块颜色
int itemCount; // 色块数量
std::vector<cv::Point2f> colorBlockCenters; // 色块中心点
std::vector<ColorResult> colors; // 颜色结果
cv::Mat annotatedImage; // 标注后的图片
bool smallLocatorOnLeft; // 小定位块是否在左边
BlurLevel blurLevel; // 图像清晰度等级
double blurScore; // 清晰度分数(方差值)
RecognitionResult() : success(false), errorCode(-1),
paperType(PaperSpatialType::PST_UNKNOWN),
locatorColor(LocatorColor::UNKNOWN),
itemCount(0), smallLocatorOnLeft(true),
blurLevel(BlurLevel::BLUR_NONE), blurScore(0) {}
};
+765
View File
@@ -0,0 +1,765 @@
# 鸿蒙尿检试纸识别算法 - 技术方案
> 文档创建时间:2026-03-31
> 最后更新:2026-04-03
> 版本:v2.1
> 状态:已实现并集成到 NAPI,包含清晰度评估和杂物检测
---
## 一、技术架构
### 1.1 整体架构图
```
┌─────────────────────────────────────────────────────────────┐
│ 鸿蒙上层 (ArkTS/TS) │
│ ↓ N-API │
├─────────────────────────────────────────────────────────────┤
│ napi_init.cpp │
│ └── SayHello() 入口函数 │
│ ├── 解析输入参数 (ArrayBuffer) │
│ ├── 调用核心算法 │
│ └── 封装返回结果 │
├─────────────────────────────────────────────────────────────┤
│ 核心算法模块 │
│ ├── PaperRecognizer (试纸识别器) │
│ │ ├── 定位块检测 │
│ │ ├── 试纸规格匹配 │
│ │ └── 色块位置计算 │
│ ├── ColorReader (颜色读取器) │
│ │ ├── 白平衡处理 │
│ │ └── 颜色采样与转换 │
│ └── Config (配置模块) │
│ ├── PaperConfig (试纸规格配置) │
│ └── GlobalParams (全局误差参数) │
├─────────────────────────────────────────────────────────────┤
│ OpenCV 库 │
│ ├── imgproc (图像处理) │
│ ├── core (核心数据结构) │
│ └── imgcodecs (图像编解码) │
└─────────────────────────────────────────────────────────────┘
```
### 1.2 文件结构
```
entry/src/main/cpp/
├── docs/ # 文档目录
│ ├── 沟通日志.md
│ ├── 技术方案.md
│ └── 详细设计文档.md # 新增:详细设计文档
├── images/ # 测试图片(27张)
├── reference/ # 参考代码(老代码)
│ ├── ImgLocateDryChemistry.cpp # 老代码-参考
│ ├── ImageLocation.cpp
│ └── ...
├── test/ # Windows测试程序
│ ├── test_main.cpp # 测试入口
│ ├── build/ # 构建目录
│ └── CMakeLists.txt # 测试构建配置
├── napi_init.cpp # N-API 入口(已集成新代码)
├── napi_init_old.cpp # N-API 备份(老代码)
├── PaperRecognizer.h # 试纸识别器头文件
├── PaperRecognizer.cpp # 试纸识别器实现(核心)
├── ColorReader.h # 颜色读取器头文件
├── ColorReader.cpp # 颜色读取器实现
├── PaperConfig.h # 试纸配置定义
├── PaperConfig.cpp # 试纸配置实现
├── Types.h # 类型定义
└── CMakeLists.txt # 鸿蒙构建配置
```
---
## 二、核心模块设计
### 2.1 类型定义 (Types.h)
```cpp
#pragma once
#include <opencv2/opencv.hpp>
#include <vector>
#include <string>
// 试纸联数
enum class BlockItems {
BI_14 = 14,
BI_11 = 11,
BI_8 = 8
};
// 大定位块类型
enum class BigLocatorType {
BLT_SHORT = 0, // 10mm
BLT_LONG = 1 // 15mm
};
// 定位块颜色分类
enum class LocatorColor {
UNKNOWN = 0,
RED = 1,
GREEN = 2,
BLUE = 3
};
// 试纸空间类型
enum class PaperSpatialType {
PST_UNKNOWN = 0,
PST_14_S = 1, // 14联, 10mm大定位块
PST_14_L = 2, // 14联, 15mm大定位块
PST_11_S = 3, // 11联, 10mm大定位块
PST_11_L = 4, // 11联, 15mm大定位块
PST_8_S = 5, // 8联, 10mm大定位块
PST_8_L = 6 // 8联, 15mm大定位块
};
// 图像清晰度等级(新增)
enum class BlurLevel {
BLUR_NONE = 0, // 清晰
BLUR_SLIGHT = 1, // 轻微模糊
BLUR_SEVERE = 2 // 严重模糊
};
// 定位块信息
struct LocatorInfo {
cv::RotatedRect rotatedRect; // 旋转矩形
cv::Point2f center; // 中心点
double area; // 面积
double width; // 宽度(长边)
double height; // 高度(短边)
LocatorColor color; // 颜色
bool isBig; // 是否大定位块
cv::Scalar avgColor; // 平均RGB颜色
};
// 色块颜色结果
struct ColorResult {
int id; // 编号(从靠近小定位块开始)
double h, s, v; // HSV值
double l, a, b; // LAB值
double r, g, b_val; // RGB值
};
// 识别结果
struct RecognitionResult {
bool success; // 是否成功
int errorCode; // 错误码
std::string errorMessage; // 错误信息
PaperSpatialType paperType; // 试纸类型
LocatorColor locatorColor; // 定位块颜色
int itemCount; // 色块数量
std::vector<cv::Point2f> colorBlockCenters; // 色块中心点
std::vector<ColorResult> colors; // 颜色结果
cv::Mat annotatedImage; // 标注后的图片
bool smallLocatorOnLeft; // 小定位块是否在左边
BlurLevel blurLevel; // 图像清晰度等级(新增)
double blurScore; // 清晰度分数(新增)
};
```
### 2.2 全局参数 (GlobalParams.h)
```cpp
#pragma once
namespace GlobalParams {
// ========== 颜色误差 ==========
// RGB欧式距离阈值,判断两个定位块颜色是否相同
const double COLOR_DISTANCE_THRESHOLD = 20.0;
// ========== 面积误差 ==========
// 面积误差比例(10%)
const double AREA_ERROR_RATIO = 0.10;
// ========== 距离误差 ==========
// 距离误差比例(10%)
const double DISTANCE_ERROR_RATIO = 0.10;
// ========== 角度误差 ==========
// 角度误差(度)
const double ANGLE_ERROR_DEGREE = 15.0;
// ========== 定位块尺寸比例 ==========
// 大定位块(10mm)宽高比:10/5 = 2.0
const double BIG_LOCATOR_RATIO_SHORT = 2.0;
// 大定位块(15mm)宽高比:15/5 = 3.0
const double BIG_LOCATOR_RATIO_LONG = 3.0;
// 小定位块宽高比:5/3 ≈ 1.667(高度是长边)
const double SMALL_LOCATOR_RATIO = 1.667;
// ========== 面积比例 ==========
// 大定位块(10mm)与小定位块面积比:10/3 ≈ 3.33
const double AREA_RATIO_SHORT = 3.33;
// 大定位块(15mm)与小定位块面积比:15/3 = 5.0
const double AREA_RATIO_LONG = 5.0;
// ========== 采样参数 ==========
// 最小采样像素数
const int MIN_SAMPLE_PIXELS = 25;
// 默认采样半径
const int DEFAULT_SAMPLE_RADIUS = 3;
}
```
### 2.3 试纸配置 (PaperConfig)
```cpp
#pragma once
#include "Types.h"
#include <map>
// 单个试纸规格配置
struct PaperConfig {
PaperSpatialType type; // 试纸类型
BlockItems blockItems; // 联数
BigLocatorType locatorType; // 大定位块类型
int itemsNum; // 反应色块数量
int slicesNum; // 最小分割单位数量
double firstPos; // 第一个色块位置倍数
double step; // 步长倍数
double distTimes; // 定位块中心距离/试纸高度
double whBigRatio; // 大定位块宽高比
double whSmallRatio; // 小定位块宽高比
};
// 试纸配置管理器
class PaperConfigManager {
public:
static PaperConfigManager& getInstance();
const PaperConfig* getConfig(PaperSpatialType type) const;
PaperSpatialType matchConfig(double distRatio, double areaRatio) const;
private:
PaperConfigManager();
void initConfigs();
std::map<PaperSpatialType, PaperConfig> configs_;
};
```
### 2.4 试纸识别器 (PaperRecognizer)
```cpp
#pragma once
#include "Types.h"
#include "PaperConfig.h"
#include <opencv2/opencv.hpp>
class PaperRecognizer {
public:
PaperRecognizer();
~PaperRecognizer();
// 主识别函数
RecognitionResult recognize(const cv::Mat& image);
private:
// ===== 第一步:图像预处理 =====
void preprocessImage(const cv::Mat& image);
// ===== 第二步:定位块检测 =====
bool detectLocators();
void findCandidateContours();
bool filterLocatorCandidates();
bool validateLocatorPair();
// ===== 第三步:颜色判断 =====
LocatorColor determineLocatorColor(const cv::Mat& roi);
double calculateColorDistance(const cv::Scalar& c1, const cv::Scalar& c2);
// ===== 第四步:规格匹配 =====
bool matchPaperSpec();
// ===== 第五步:色块位置计算 =====
void calculateColorBlockCenters();
// ===== 第六步:绘制标注 =====
void drawAnnotations();
// ===== 辅助函数 =====
double calculateAspectRatio(const cv::RotatedRect& rect);
bool isBigLocator(const cv::RotatedRect& rect, double height);
private:
cv::Mat originalImage_; // 原图
cv::Mat processImage_; // 处理用图像
cv::Mat hsvImage_; // HSV图像
LocatorInfo leftLocator_; // 左定位块
LocatorInfo rightLocator_; // 右定位块
bool leftIsBig_; // 左边是否是大定位块
std::vector<cv::Point> centers_; // 色块中心点
const PaperConfig* config_; // 匹配的配置
RecognitionResult result_; // 结果
};
```
### 2.5 颜色读取器 (ColorReader)
```cpp
#pragma once
#include "Types.h"
#include <opencv2/opencv.hpp>
class ColorReader {
public:
ColorReader();
~ColorReader();
// 执行颜色读取
std::vector<ColorResult> readColors(
const cv::Mat& image,
const std::vector<cv::Point>& centers,
bool smallLocatorOnLeft
);
private:
// 白平衡处理
void applyWhiteBalance(cv::Mat& image);
// 采样颜色
cv::Scalar sampleColor(const cv::Mat& image, const cv::Point& center);
// 颜色空间转换
void convertColorSpaces(const cv::Scalar& bgr,
double& h, double& s, double& v,
double& l, double& a, double& b);
// 调整顺序(始终从靠近小定位块开始)
std::vector<ColorResult> adjustOrder(
std::vector<ColorResult>& colors,
bool smallLocatorOnLeft
);
};
```
---
## 三、核心算法流程
### 3.1 主流程图
```
输入图像
│
▼
┌─────────────────┐
│ 1. 图像预处理 │
│ - 颜色空间转换 │
│ - 高斯滤波 │
└────────┬────────┘
│
▼
┌─────────────────┐
│ 2. 轮廓检测 │
│ - 二值化 │
│ - findContours│
└────────┬────────┘
│
▼
┌─────────────────┐
│ 3. 定位块筛选 │
│ - 面积过滤 │
│ - 宽高比过滤 │
│ - 矩形度过滤 │
└────────┬────────┘
│
▼
┌─────────────────┐
│ 4. 定位块配对 │
│ - 面积比验证 │
│ - 颜色一致性 │
│ - 距离验证 │
└────────┬────────┘
│
┌────┴────┐
│ 候选数=2? │
└────┬────┘
│
┌────┼────┐
Yes No
│ │
▼ ▼
继续 返回错误
│
▼
┌─────────────────┐
│ 5. 规格匹配 │
│ - 计算距离比 │
│ - 匹配PaperType│
└────────┬────────┘
│
▼
┌─────────────────┐
│ 6. 色块位置计算 │
│ - 计算斜率 │
│ - 计算各中心点 │
└────────┬────────┘
│
▼
┌─────────────────┐
│ 7. 颜色读取 │
│ - 白平衡 │
│ - 采样颜色 │
│ - HSV/LAB转换 │
└────────┬────────┘
│
▼
┌─────────────────┐
│ 8. 结果封装 │
│ - 绘制标注 │
│ - JSON格式 │
└─────────────────┘
│
▼
返回结果
```
### 3.2 定位块检测算法
```
输入: 二值化图像
输出: 2个定位块信息
Step 1: findContours 获取所有轮廓
Step 2: 遍历轮廓,计算几何特征
- 面积: contourArea()
- 最小外接矩形: minAreaRect()
- 宽高比: max(w,h) / min(w,h)
- 矩形度: area / (w * h)
Step 3: 过滤轮廓
- 面积范围: [minArea, maxArea]
- 宽高比: 大定位块 ≈ 2.0 或 3.0, 小定位块 ≈ 1.667
- 矩形度: > 0.85
Step 4: 分组配对
- 按面积分组: 大/小
- 验证面积比: 大/小 ≈ 3.33 或 5.0
- 验证颜色一致性: RGB欧式距离 < 20
Step 5: 验证距离
- 两定位块中心距离
- 与PaperConfig中distTimes匹配
返回: 两个定位块信息
```
### 3.3 色块位置计算算法
```cpp
// 伪代码
void calculateColorBlockCenters() {
// 1. 获取两个定位块中心点
Point p1 = leftLocator_.center;
Point p2 = rightLocator_.center;
// 2. 计算方向向量和距离
Point vec = p2 - p1;
double totalDist = norm(vec);
Point unitVec = vec / totalDist;
// 3. 计算基准单位(像素/毫米)
// 试纸高度 = 两定位块高度的均值
double paperHeight = (leftLocator_.height + rightLocator_.height) / 2;
// 4. 根据 PaperConfig 计算每个色块位置
double avgDist = totalDist / config_->slicesNum;
double firstOffset = avgDist * config_->firstPos;
double stepOffset = avgDist * config_->step;
// 5. 计算各色块中心点
Point firstCenter = p1 + unitVec * firstOffset;
for (int i = 0; i < config_->itemsNum; i++) {
Point center = firstCenter + unitVec * (stepOffset * i);
centers_.push_back(center);
}
}
```
### 3.4 白平衡算法
参考 `reference/ColorCorrection.cpp`:
```cpp
void applyWhiteBalance(cv::Mat& image) {
// 1. 计算各通道均值
Scalar sumChannels = sum(image);
int totalPixels = image.rows * image.cols;
double avgB = sumChannels[0] / totalPixels;
double avgG = sumChannels[1] / totalPixels;
double avgR = sumChannels[2] / totalPixels;
// 2. 计算目标均值(三通道平均)
double targetAvg = (avgB + avgG + avgR) / 3.0;
// 3. 各通道缩放校正
std::vector<Mat> channels(3);
split(image, channels);
channels[0] = channels[0] * (targetAvg / avgB);
channels[1] = channels[1] * (targetAvg / avgG);
channels[2] = channels[2] * (targetAvg / avgR);
merge(channels, image);
image.convertTo(image, CV_8UC3);
}
```
---
## 四、PaperConfig 参数表
### 4.1 支持的试纸规格
| 类型 | 联数 | 大定位块 | itemsNum | slicesNum | firstPos | step | distTimes | whBigRatio |
|------|------|----------|----------|-----------|----------|------|-----------|------------|
| PST_14_S | 14 | 10mm | 14 | 50 | 2.25 | 3.5 | 21.3 | 2.0 |
| PST_14_L | 14 | 15mm | 14 | 50 | 2.25 | 3.5 | 21.8 | 3.0 |
| PST_11_S | 11 | 10mm | 11 | 39 | 2.25 | 3.5 | 17.6 | 2.0 |
| PST_11_L | 11 | 15mm | 11 | 39 | 2.25 | 3.5 | 17.6 | 3.0 |
| PST_8_S | 8 | 10mm | 8 | 29 | 2.25 | 3.5 | 12.9 | 2.0 |
| PST_8_L | 8 | 15mm | 8 | 29 | 2.25 | 3.5 | 13.4 | 3.0 |
### 4.2 试纸物理尺寸参考
以 PST_14_S (14联,10mm大定位块) 为例:
- 小定位块:3mm × 5mm
- 大定位块:10mm × 5mm
- 反应色块:14个 × (5mm × 5mm)
- 间隔:15个 × 2mm
- 总长度:3 + 14×5 + 15×2 + 10 = 3 + 70 + 30 + 10 = 113mm
- 定位块中心距离:113 - 1.5 - 5 = 106.5mm
- distTimes = 106.5 / 5 = 21.3
---
## 五、测试计划
### 5.1 测试环境
- 操作系统:Windows
- 编译器:MSVC 或 MinGW
- OpenCV:D:\opencv(官方预编译库)
- 测试框架:自定义简单测试框架
### 5.2 测试图片
| 文件名 | 类型 | 说明 |
|--------|------|------|
| 11green_1.png | 11联绿色 | 设计参考图 |
| 11green_2.png | 11联绿色 | 摄像头采集 |
| 14blue_1.png | 14联蓝色 | 设计参考图 |
| 14blue_2.png | 14联蓝色 | 摄像头采集 |
| 14red_1.png | 14联红色 | 设计参考图 |
| 14red_2.png | 14联红色 | 摄像头采集 |
| 65686153160b075c3d7eb019f9eecdb4.jpg | 实拍 | 实际采集 |
| be3af2fa7372464a98172a91fa52adb8.jpg | 实拍 | 实际采集 |
| ... | ... | 其他实拍图片 |
### 5.3 测试输出
每个测试图片生成以下输出:
1. **标注图片**:`{filename}_annotated.jpg`
- 在定位块周围画红框
- 在色块中心画红圈(半径5像素)
- 标注色块编号
2. **颜色数据文件**:`{filename}_colors.txt`
```
Paper Type: PST_14_S
Locator Color: RED
Item Count: 14
Status: SUCCESS
Color Results:
[0] HSV(12.34, 56.78, 90.12) LAB(23.45, 67.89, 12.34) RGB(255, 128, 64)
[1] HSV(...)
...
```
3. **还原模拟图**:`{filename}_restored.jpg`
- 200px × 1000px
- 试纸居中
- 定位块颜色按识别结果
- 色块颜色按读取结果
### 5.4 测试用例
| 用例ID | 描述 | 预期结果 |
|--------|------|----------|
| TC001 | 14联红色试纸 | 正确识别PST_14_S/R,返回14个颜色值 |
| TC002 | 14联蓝色试纸 | 正确识别PST_14_S/B,返回14个颜色值 |
| TC003 | 11联绿色试纸 | 正确识别PST_11_S/G,返回11个颜色值 |
| TC004 | 8联试纸 | 正确识别PST_8_x,返回8个颜色值 |
| TC005 | 15mm大定位块 | 正确识别PST_14_L等L型规格 |
| TC006 | 倾斜试纸 | 正确处理倾斜,色块位置准确 |
| TC007 | 小定位块在右 | 颜色顺序正确(从右往左读,结果从左往右排)|
| TC008 | 无效图片 | 返回错误码,不崩溃 |
---
## 六、实现步骤
### 阶段一:基础框架(Day 1)
1. 创建文件结构
2. 实现 Types.h 类型定义
3. 实现 GlobalParams.h 全局参数
4. 实现 PaperConfig 配置模块
### 阶段二:核心算法(Day 2-3)
1. 实现 PaperRecognizer
- 图像预处理
- 轮廓检测
- 定位块筛选与配对
- 规格匹配
- 色块位置计算
2. 实现 ColorReader
- 白平衡处理
- 颜色采样
- 颜色空间转换
### 阶段三:测试程序(Day 4)
1. 创建 test/ 目录
2. 实现测试入口和运行器
3. 配置 CMakeLists.txt(链接 D:\opencv)
4. 编写测试输出逻辑
### 阶段四:调试优化(Day 5)
1. 运行测试,查看结果
2. 调整误差参数
3. 优化识别率
4. 处理边界情况
### 阶段五:NAPI 集成(已完成 ✅)
1. ✅ 修改 napi_init.cpp 的 SayHello 函数
2. ✅ 更新 CMakeLists.txt(移除老文件,添加新文件)
3. ✅ 编译 HAP 成功
4. ✅ 实现白平衡处理
5. ✅ 实现自适应 ROI 颜色采样
6. ✅ 实现试纸裁剪保存(getPaperData)
### 阶段六:优化迭代(已完成 ✅)
1. ✅ **颜色显示修复**:BGRA→BGR 颜色空间转换修复
2. ✅ **规格匹配优化**:调整 distRatio 范围,添加缓冲区间
3. ✅ **颜色一致性验证**:配对定位块必须颜色相同
4. ✅ **宽高比过滤**:最大宽高比限制4.0,容差收紧至0.35
5. ✅ **清晰度评估**:Laplacian方差法检测图像模糊(新增)
6. ✅ **杂物检测**:试纸高度<30px或候选>15个时拒绝识别(新增)
7. ✅ **NAPI参数扩展**:返回locatorColor、paperType、blurLevel、blurScore
---
## 七、编译与运行
### 7.1 测试程序编译
```bash
# Windows PowerShell
cd E:\Qoder\urologist\entry\src\main\cpp\test
mkdir build
cd build
cmake .. -DOpenCV_DIR=D:\opencv\build
cmake --build . --config Release
```
### 7.2 运行测试
```bash
# 运行单个图片测试
.\Release\test_urine_analyzer.exe ..\..\images\14red_1.png
# 运行批量测试
.\Release\test_urine_analyzer.exe ..\..\images\
```
### 7.3 查看结果
```bash
# 结果输出在 test_output/ 目录
cd ..\..\test_output
dir
```
---
## 八、风险与对策
| 风险 | 影响 | 对策 |
|------|------|------|
| 光照变化大 | 定位块识别失败 | 增加颜色判断的鲁棒性,提供可调参数 |
| 试纸倾斜过大 | 色块位置计算偏差 | 使用斜率补偿计算 |
| 图片质量差 | 轮廓检测失败 | 预处理增强(滤波、形态学操作)|
| 颜色相近 | 定位块配对错误 | 多重验证条件(面积、距离、颜色)|
---
## 九、附录
### A. SayHello 返回值结构
```javascript
{
"byteLength": 123456, // 图像数据长度
"buffer": ArrayBuffer, // 图像数据(RGBA格式)
"width": 1920, // 图像宽度
"height": 1080, // 图像高度
"strLength": 567, // 颜色字符串长度
"strBuffer": ArrayBuffer, // 颜色JSON字符串
"locatorColor": 1, // 定位块颜色:0=未知,1=红,2=绿,3=蓝(新增)
"paperType": 1, // 试纸类型:1=PST_14_S,2=PST_14_L,...(新增)
"blurLevel": 0, // 清晰度等级:0=清晰,1=轻微模糊,2=严重模糊(新增)
"blurScore": 450.5, // 清晰度分数(Laplacian方差值,新增)
"statusCode": 0 // 状态码:0成功,其他失败
}
```
### B. 状态码定义
| 状态码 | 含义 | 说明 |
|--------|------|------|
| 0 | 成功 | 识别成功,返回完整结果 |
| 1 | 未检测到定位块 | 没有找到符合条件的定位块轮廓 |
| 2 | 定位块颜色识别失败 | 定位块颜色无法识别(非红/绿/蓝)|
| 3 | 定位块配对失败 | 未找到一大一小的有效配对 |
| 4 | 试纸规格匹配失败 | distRatio 不在任何规格范围内 |
| 5 | 色块位置计算失败 | 内部计算错误 |
| 6 | 检测到多条试纸 | 图片中存在多条试纸,需单独拍摄 |
| 7 | 图像处理错误 | 图像格式或处理异常 |
| 8 | 输入图像为空 | 传入的图像数据为空 |
| 9 | 试纸太小/杂物 | 试纸高度<30px,可能是杂物干扰(新增)|
| 10 | 场景杂乱 | 候选定位块>15个,场景过于杂乱(新增)|
| 100 | 未知错误 | 其他未分类错误 |
### C. 色块编号规则
**核心原则:编号 0 始终是靠近小定位块的色块**
无论试纸如何放置(水平、垂直、旋转),返回的颜色数据中:
- `id=0` 始终是小定位块旁边的第一个反应色块
- `id=1,2,3...` 依次远离小定位块
**实现方式**:
- `calculateColorBlockCenters()` 始终从小定位块边缘开始计算
- 方向向量始终从小定位块指向大定位块
- 这样 `centers_[0]` 始终靠近小定位块
---
*文档结束*
+301
View File
@@ -0,0 +1,301 @@
# 鸿蒙算法改造 - 需求沟通日志
> 文档创建时间:2026-03-31
> 参与方:用户 & AI助手
---
## 一、项目背景
### 1.1 项目概述
- **项目名称**:鸿蒙尿检试纸识别算法改造
- **改造范围**:仅限于 `E:\Qoder\urologist\entry\src\main\cpp` 目录下的 C++ 代码
- **技术栈**:C++ + OpenCV,通过 N-API 与鸿蒙上层交互
### 1.2 核心功能
1. 从上层 NAPI 接收图片
2. 试纸定位:根据定位块特征找到定位块,匹配 PaperConfig 规格
3. 获取反应色块的中心点位置列表
4. 读取每个化学反应色块的颜色值(HSV、LAB)
---
## 二、第一轮需求澄清(2026-03-31)
### Q1:定位块的识别特征
**问题**:
- 定位块颜色范围(HSV边界值)是什么?
- 定位块形状特征?最小/最大面积限制?
- 如何区分定位块和反应色块?
**用户答复**:
- 定位块颜色使用 RGB 分量比例判断,不用 HSV 边界值(效率考虑)
- 定位块有两种:
- 小定位块:3mm × 5mm(宽×高)
- 大定位块:10mm × 5mm 或 15mm × 5mm
- 定位块是矩形,反应色块是正方形(5mm × 5mm)
- **关键点**:反应色块可能是白色,通过 `findContours` 可能找不到轮廓
- 定位块在试纸两端,中间是反应色块
### Q2:定位失败的处理
**问题**:
- 找不到两个定位块时返回什么错误码?
- 找到多个候选定位块如何选择?
**用户答复**:
- 反应色块和定位块是对齐在一条线上的
- 找不到两个定位块 → 返回错误(参考现有 SayHello 代码)
- 找到多于2个定位块 → 直接返回错误,不做进一步处理
### Q3:试纸角度处理
**问题**:
- 最大倾斜角度?
- 是否需要图像旋转校正?
**用户答复**:
- 不限制倾斜角度
- **不做图像旋转处理**,通过几何公式和斜率计算位置
---
## 三、第二轮需求澄清(2026-03-31)
### Q4:slicesNum 计算方式
**问题**:
- "2毫米"如何转换?是否用定位块高度作为基准?
**用户答复**:
- 这是比例问题,生产单位(毫米)需转换为像素比例
- 例如:10mm×5mm 的定位块在图像中可能是 100px×50px,则放大了10倍
- PaperConfig 中的值需要乘以这个比例因子
### Q5:firstPos 和 step 的单位
**问题**:
- 这两个值是以"2毫米"还是"最小分割单位"为单位?
**用户答复**:
- firstPos = 2.25 表示:(2 + 2.5) / 2 = 2.25
- 2mm = 定位块边缘到第一个反应色块的距离
- 2.5mm = 反应色块中心到边缘的距离(5mm色块的一半)
- 本质上 firstPos 和 step 是倍数关系
- 计算时基于像素,不是毫米
### Q6:白平衡算法
**问题**:
- 使用 `Whitebalance` 还是 `WhitebalancePR`?
- 白平衡参考点如何选取?
**用户答复**:
- 参考 `reference` 目录下的代码
- **白平衡基于整个图片进行**
### Q7:颜色返回顺序
**问题**:
- 小定位块在右边时,是否从右往左编号?
**用户答复**:
- **颜色选取顺序始终从靠近小定位块的反应色块开始**
- 无论试纸怎么摆放
### Q8:采样区域大小
**问题**:
- 5毫米对应多少像素?
- 是否需要动态计算采样半径?
**用户答复**:
- 最少取 25 个像素的均值
- 可以尝试动态计算半径
### Q9:SayHello 返回值格式
**问题**:
- 返回值是否完全保持一致?
- strBuffer 格式?
**用户答复**:
- **完全一致**,上层代码不改
- strBuffer 格式:`{"id":0,"value":"h,s,v,l,a,b"}`
### Q10:测试代码运行环境
**问题**:
- Windows 独立测试程序?
- 是否支持命令行参数?
**用户答复**:
- Windows 独立命令行程序
- 告知如何运行测试程序
### Q11:误差范围参数
**问题**:
- 具体有哪些误差参数?
**用户答复**:
| 参数 | 范围 | 说明 |
|------|------|------|
| 颜色误差 | RGB欧式距离 ≤ 20 | 判断两个定位块颜色是否相同 |
| 面积误差 | 10% | 上下限范围 |
| 距离误差 | 10% | 上下限范围 |
| 角度误差 | 15° | 倾斜判断 |
- 这些参数用全局变量定义,方便调节
### Q12:需要支持的试纸规格
**问题**:
- 支持哪些规格?
**用户答复**:
- **只支持 14联、11联、8联**
- 每种联数支持两种大定位块(10mm 和 15mm)
- 共 6 种 PaperConfig
### Q13:PaperConfig 参数
**问题**:
- 其他规格参数如何计算?
**用户答复**:
- 参考现有 `initPaperConfig` 函数
- 只实现 14、11、8 联的支持代码
---
## 四、第三轮需求澄清(2026-03-31)
### Q14:图片格式和来源
**问题**:
- 是否支持 NV21/YUV 格式?
- 测试程序是否需要支持 NV21?
**用户答复**:
- **不需要支持其他格式**
- 测试只用 PNG/JPG
- 没有 NV21 测试数据
### Q15:ROI 区域
**问题**:
- 是否需要从大图自动搜索试纸位置?
**用户答复**:
- 输入已经是裁剪好的 ROI
- `11green_1.png` 等文件大小与实际传递的图片差不多
- 裁剪来源:`savePaperImg` 函数产生的外包矩形
### Q16:白色反应色块的处理
**问题**:
- 白平衡后白色色块的 RGB 如何解释?
**用户答复**:
- 澄清:反应色块可能颜色很浅,findContours 可能找不到轮廓
- **核心原则**:化学反应色块的位置通过两个定位块**计算得出**,与反应色块本身的位置无关
- 计算的位置可能与实际中心点有偏差
### Q17:定位块的亮度变化
**问题**:
- 深红 vs 浅红如何判断?
**用户答复**:
- 深红、浅红都是红色 → R分量 > G、B分量
- **增加欧式距离判断**:
- 如果两个定位块一个是"深红"、一个是"浅红"
- 欧式距离可能超过20 → 判定为不符合要求
### Q18:颜色输出格式确认
**问题**:
- HSV 和 LAB 精度?
**用户答复**:
- **保留2位小数点**
### Q19:还原图片的格式
**问题**:
- 图片格式?分辨率?
**用户答复**:
- 格式:JPG
- 分辨率示例(14项红色试纸):
- 试纸高度:50像素
- 小定位块:30px宽 × 50px高
- 反应色块:50px × 50px
- 大定位块:100px宽
- 间隔:20像素
- 总图:200px高 × 1000px宽,试纸居中
---
## 五、最终需求确认
### 5.1 核心业务规则
| 规则 | 内容 |
|------|------|
| 定位块识别 | RGB分量比例判断 + 欧式距离验证(≤20)|
| 试纸规格 | 仅支持 14联、11联、8联,各有大小定位块两种 |
| 颜色顺序 | 始终从靠近小定位块的色块开始编号 |
| 采样方式 | 以计算的中心点取至少25像素均值 |
| 白平衡 | 基于整图进行 |
### 5.2 全局误差参数
```cpp
// 颜色误差:RGB欧式距离阈值
const double COLOR_DISTANCE_THRESHOLD = 20.0;
// 面积误差:10%
const double AREA_ERROR_RATIO = 0.10;
// 距离误差:10%
const double DISTANCE_ERROR_RATIO = 0.10;
// 角度误差:15度
const double ANGLE_ERROR_DEGREE = 15.0;
```
### 5.3 返回值格式
- 保持与现有 SayHello 完全一致
- strBuffer JSON格式:`{"id":0,"value":"h,s,v,l,a,b"}`
- 颜色值保留2位小数
### 5.4 测试要求
- Windows 独立命令行程序
- 测试图片:`images/` 目录下的 PNG/JPG 文件
- 输出:
1. 识别成功/失败状态
2. 标注定位块和色块中心的红圈图片
3. 颜色值文本文件(HSV、LAB、RGB)
4. 还原生成的试纸模拟图片(JPG,200×1000像素)
---
## 六、沟通结论
**需求已完全澄清,双方达成一致意见。**
下一步:
1. AI 编写技术方案文档
2. 逐步实现代码
3. 编写测试程序
4. 验证测试结果
---
*文档结束*
+748
View File
@@ -0,0 +1,748 @@
# 尿检试纸识别系统 - 详细设计文档
> 文档创建时间:2026-03-30
> 最后更新:2026-04-03
> 版本:v1.1
> 适用范围:PaperRecognizer、ColorReader、PaperConfig、NAPI接口
---
## 一、系统概述
### 1.1 功能描述
尿检试纸识别系统用于从拍摄的试纸图片中自动识别:
1. **定位块**:试纸两端的红色/绿色/蓝色定位块
2. **试纸规格**:根据定位块特征判断8/11/14联及S/L型
3. **反应色块位置**:计算每个反应色块的精确中心坐标
4. **颜色数据**:读取每个反应色块的HSV+LAB颜色值
### 1.2 设计原则
- **模块化**:识别器、颜色读取器、配置管理器独立设计
- **鲁棒性**:多级过滤、多重验证、容错处理
- **可维护性**:清晰的代码结构、详细的注释
- **可扩展性**:支持新增试纸规格
### 1.3 支持的试纸规格
| 类型 | 联数 | 大定位块 | 面积比 | 距离比范围 |
|------|------|----------|--------|-----------|
| PST_14_S | 14 | 10mm | ~3.33 | 19.5-23.5 |
| PST_14_L | 14 | 15mm | ~5.0 | 20.0-24.0 |
| PST_11_S | 11 | 10mm | ~3.33 | 15.9-19.2 |
| PST_11_L | 11 | 15mm | ~5.0 | 16.2-19.6 |
| PST_8_S | 8 | 10mm | ~3.33 | 11.6-14.1 |
| PST_8_L | 8 | 15mm | ~5.0 | 12.0-14.6 |
---
## 二、核心模块设计
### 2.1 PaperRecognizer(试纸识别器)
#### 2.1.1 类图
```
┌─────────────────────────────────────────────────────┐
│ PaperRecognizer │
├─────────────────────────────────────────────────────┤
│ - originalImage_: cv::Mat // 原图 │
│ - processImage_: cv::Mat // 处理用图像 │
│ - hsvImage_: cv::Mat // HSV图像 │
│ - grayImage_: cv::Mat // 灰度图像 │
│ - locator1_: LocatorInfo // 定位块1 │
│ - locator2_: LocatorInfo // 定位块2 │
│ - bigLocator_: LocatorInfo* // 大定位块指针 │
│ - smallLocator_: LocatorInfo* // 小定位块指针 │
│ - centers_: vector<Point2f> // 色块中心点 │
│ - config_: PaperConfig* // 匹配的配置 │
│ - result_: RecognitionResult // 识别结果 │
│ - paperImg_: cv::Mat // 裁剪的试纸图 │
│ - candidateCount_: size_t // 候选数量(新增) │
├─────────────────────────────────────────────────────┤
│ + recognize(image): RecognitionResult │
│ + getPaperImg(): cv::Mat │
├─────────────────────────────────────────────────────┤
│ - preprocessImage(image) │
│ - detectLocators(): bool │
│ - filterLocatorCandidates(contours): bool │
│ - validateLocatorPair(): bool │
│ - groupCandidatesByY(candidates): vector<vector<*>> │
│ - findValidLocatorPairInGroup(group): LocatorPair │
│ - determineLocatorColorByCenter(rect): LocatorColor │
│ - matchPaperSpec(): bool │
│ - calculateColorBlockCenters() │
│ - drawAnnotations() │
│ - savePaperImg() │
│ - assessLocatorBlur(gray, locator, score): BlurLevel│ // 清晰度评估(新增)
└─────────────────────────────────────────────────────┘
```
#### 2.1.2 主流程
```cpp
RecognitionResult PaperRecognizer::recognize(const cv::Mat& image) {
// Step 1: 图像预处理
preprocessImage(image);
// Step 2: 定位块检测(包含多试纸处理)
if (!detectLocators()) {
return result_; // 返回错误
}
// Step 3: 颜色判断(在 validateLocatorPair 中完成)
// Step 3.5: 清晰度评估(新增)
double bigScore, smallScore;
BlurLevel bigBlur = assessLocatorBlur(grayImage_, bigLocator_->rotatedRect, bigScore);
BlurLevel smallBlur = assessLocatorBlur(grayImage_, smallLocator_->rotatedRect, smallScore);
result_.blurLevel = max(bigBlur, smallBlur);
result_.blurScore = min(bigScore, smallScore);
// Step 3.6: 杂物检测(新增)
if (bigLocator_->height < 30.0) { // 试纸高度太小
result_.errorCode = 9;
return result_;
}
if (candidateCount_ > 15) { // 候选太多,场景杂乱
result_.errorCode = 10;
return result_;
}
// Step 4: 规格匹配
if (!matchPaperSpec()) {
return result_;
}
// Step 5: 计算色块中心点
calculateColorBlockCenters();
// Step 6: 绘制标注
drawAnnotations();
// Step 7: 保存试纸图片(NAPI用)
savePaperImg();
result_.success = true;
return result_;
}
```
#### 2.1.3 定位块检测流程
```
输入: 原图
输出: 两个定位块信息 (bigLocator_, smallLocator_)
1. 图像预处理
- 颜色空间转换: BGR -> HSV, GRAY
- 高斯滤波: 5x5 核
2. 轮廓检测
- 二值化: THRESH_OTSU
- findContours: RETR_EXTERNAL
3. 候选过滤
for each contour:
- 面积过滤: 100 < area < 10000
- 正方形过滤: aspectRatio 不在 [0.9, 1.1]
- 宽高比过滤:
- 大定位块: 2.0 ± 0.5 或 3.0 ± 0.5
- 小定位块: 1.667 ± 0.5
- 计算颜色(延迟到最后)
4. Y坐标聚类(多试纸处理)
- 按 Y坐标排序候选
- 相邻候选 Y差 < 3*平均高度 -> 同组
- 得到多个组
5. 组内配对验证
for each group:
- 找一大一小配对
- 验证面积比: ~3.33 或 ~5.0
- 验证颜色一致: RGB欧式距离 < 阈值
- 验证 Y坐标相近: 差 < 阈值
6. 结果判定
- validPairs.size() == 1: 成功
- validPairs.size() > 1: errorCode = 6 (多条试纸)
- validPairs.size() == 0: errorCode = 1 (未检测到)
```
#### 2.1.4 定位块颜色判断
```cpp
LocatorColor PaperRecognizer::determineLocatorColorByCenter(
const cv::RotatedRect& rotatedRect) {
// 获取旋转矩形中心30%区域的颜色
cv::Scalar avgColor = getCenterRegionColor(rotatedRect);
double r = avgColor[2];
double g = avgColor[1];
double b = avgColor[0];
// RGB 主判断
double maxChannel = std::max({r, g, b});
// 红色: R最大且优势明显
if (r == maxChannel && r > g + 20 && r > b + 20) {
return LocatorColor::RED;
}
// 绿色: G最大
if (g == maxChannel && g > r + 10 && g > b + 10) {
return LocatorColor::GREEN;
}
// 蓝色: B最大且优势明显(降低阈值处理深蓝)
if (b == maxChannel && b > r + 15 && b > g + 15) {
return LocatorColor::BLUE;
}
// HSV 辅助判断
// ... 省略 HSV 计算细节
return LocatorColor::UNKNOWN;
}
```
#### 2.1.5 色块位置计算
**核心原则:`centers_[0]` 始终靠近小定位块**
```cpp
void PaperRecognizer::calculateColorBlockCenters() {
// 判断小定位块位置
result_.smallLocatorOnLeft = !locator1_.isBig;
// 计算定位块边缘中心点
cv::Point2f bigEdgeCenter, smallEdgeCenter;
// 情况1: 小定位块在左边
if (result_.smallLocatorOnLeft) {
// 方向向量: 从小指向大
double dx = bigLocator_->center.x - smallLocator_->center.x;
double dy = bigLocator_->center.y - smallLocator_->center.y;
// ...
// smallEdgeCenter = 小定位块右边缘中心
// bigEdgeCenter = 大定位块左边缘中心
}
// 情况2: 小定位块在右边
else {
// 方向向量: 仍然从小指向大(反向计算)
double dx = bigLocator_->center.x - smallLocator_->center.x;
double dy = bigLocator_->center.y - smallLocator_->center.y;
// ...
// smallEdgeCenter = 小定位块左边缘中心
// bigEdgeCenter = 大定位块右边缘中心
}
// 计算步长
double effectiveDist = distance(bigEdgeCenter, smallEdgeCenter);
double avgDist = effectiveDist / config_->slicesNum;
double firstOffset = avgDist * config_->firstPos;
double stepOffset = avgDist * config_->step;
// 始终从小定位块边缘开始计算
for (int i = 0; i < config_->itemsNum; ++i) {
double offset = firstOffset + stepOffset * i;
cv::Point2f center = smallEdgeCenter + offset * unitVector;
centers_.push_back(center);
}
// 结果: centers_[0] 靠近小定位块
}
```
### 2.2 ColorReader(颜色读取器)
#### 2.2.1 类设计
```cpp
class ColorReader {
public:
// 读取颜色(可选择是否应用白平衡)
std::vector<ColorResult> readColors(
const cv::Mat& image,
const std::vector<cv::Point2f>& centers,
bool smallLocatorOnLeft,
bool applyWB = true // 白平衡开关
);
private:
// 白平衡处理(灰度世界算法)
void applyWhiteBalance(cv::Mat& image);
// 计算自适应ROI大小
int calculateAdaptiveROI(double locatorHeight);
// 采样颜色
cv::Scalar sampleColor(const cv::Mat& image,
const cv::Point2f& center,
int roiSize);
};
```
#### 2.2.2 白平衡算法
```cpp
void ColorReader::applyWhiteBalance(cv::Mat& image) {
// 灰度世界算法
cv::Scalar avgScalar = cv::mean(image);
double avgGray = (avgScalar[0] + avgScalar[1] + avgScalar[2]) / 3.0;
std::vector<cv::Mat> channels;
cv::split(image, channels);
// 各通道缩放
for (int i = 0; i < 3; ++i) {
double scale = avgGray / avgScalar[i];
channels[i] = channels[i] * scale;
}
cv::merge(channels, image);
image.convertTo(image, CV_8UC3);
}
```
#### 2.2.3 自适应ROI
```cpp
// ROI大小: 定位块高度的0.6倍
// 范围限制: 5x5 到 15x15 像素
int calculateAdaptiveROI(double locatorHeight) {
int halfSize = static_cast<int>(locatorHeight * 0.6);
halfSize = std::max(5, std::min(15, halfSize));
return halfSize * 2 + 1; // 11, 13, ..., 31
}
```
### 2.3 PaperConfig(试纸配置)
#### 2.3.1 配置参数
```cpp
struct PaperConfig {
PaperSpatialType type; // 试纸类型
BlockItems blockItems; // 联数
BigLocatorType locatorType; // 大定位块类型
int itemsNum; // 反应色块数量
int slicesNum; // 最小分割单位数量
double firstPos; // 第一个色块位置倍数
double step; // 步长倍数
};
```
#### 2.3.2 规格匹配
```cpp
PaperSpatialType PaperConfigManager::matchConfig(
double distRatio, double areaRatio) const {
// distRatio = 定位块中心距离 / 试纸高度
// areaRatio = 大定位块面积 / 小定位块面积
// S型 (10mm大定位块, areaRatio < 4.0)
if (areaRatio < 4.0) {
if (distRatio > 19.5 && distRatio < 23.5) return PST_14_S;
if (distRatio > 15.9 && distRatio < 19.2) return PST_11_S;
if (distRatio > 11.6 && distRatio < 14.1) return PST_8_S;
}
// L型 (15mm大定位块, areaRatio >= 4.0)
else {
if (distRatio > 20.0 && distRatio < 24.0) return PST_14_L;
if (distRatio > 16.2 && distRatio < 19.6) return PST_11_L;
if (distRatio > 12.0 && distRatio < 14.6) return PST_8_L;
}
return PST_UNKNOWN;
}
```
---
## 三、NAPI 接口设计
### 3.1 接口列表
| 函数名 | 功能 | 输入 | 输出 |
|--------|------|------|------|
| sayHello | 试纸识别 | ArrayBuffer(YUV), width, height, format | Object |
| getPaperData | 获取裁剪试纸图 | 无 | Object |
| add | 测试用 | num1, num2 | sum |
| analysisImage | 测试用 | PixelMap | PixelMap |
### 3.2 sayHello 详细设计
#### 3.2.1 输入参数
```javascript
// ArkTS 调用示例
let result = nativeModule.sayHello(
yuvBuffer, // ArrayBuffer: NV21格式的YUV数据
width, // number: 图像宽度
height, // number: 图像高度
format // number: 格式代码 (8)
);
```
#### 3.2.2 处理流程
```cpp
static napi_value SayHello(napi_env env, napi_callback_info info) {
// 1. 解析参数
napi_get_arraybuffer_info(env, args[0], &inputBuffer, &length);
napi_get_value_int32(env, args[1], &width);
napi_get_value_int32(env, args[2], &height);
// 2. YUV -> RGBA
cv::Mat yuv_mat(height + height/2, width, CV_8UC1, inputBuffer);
cv::cvtColor(yuv_mat, t, cv::COLOR_YUV2RGBA_NV21);
cv::rotate(t, rgb_mat, cv::ROTATE_90_CLOCKWISE);
// 3. 调用识别器
cvtColor(rgb_mat, bgra_mat, COLOR_RGBA2BGRA);
RecognitionResult recResult = g_paperRecognizer.recognize(bgra_mat);
// 4. 处理结果
if (recResult.success) {
// 读取颜色(白平衡)
// 自适应ROI采样
// 生成JSON字符串
}
// 5. 封装返回对象
napi_create_object(env, &object);
napi_set_named_property(env, object, "byteLength", ...);
napi_set_named_property(env, object, "buffer", ...);
napi_set_named_property(env, object, "width", ...);
napi_set_named_property(env, object, "height", ...);
napi_set_named_property(env, object, "strLength", ...);
napi_set_named_property(env, object, "strBuffer", ...);
napi_set_named_property(env, object, "statusCode", ...);
return object;
}
```
#### 3.2.3 输出格式
```javascript
{
byteLength: 123456, // 标注图像数据长度
buffer: ArrayBuffer, // 标注图像数据 (RGBA)
width: 1920, // 图像宽度
height: 1080, // 图像高度
strLength: 567, // 颜色JSON字符串长度
strBuffer: ArrayBuffer, // 颜色JSON字符串
statusCode: 0 // 状态码
}
```
#### 3.2.4 颜色JSON格式
```json
[
{"id":0, "value":"52.0,255.0,179.0,163.0,-3.0,68.0"},
{"id":1, "value":"42.0,58.0,175.0,172.0,0.0,16.0"},
...
]
// value格式: H,S,V,L,A,B
```
### 3.3 getPaperData 详细设计
```cpp
static napi_value GetPaperData(napi_env env, napi_callback_info info) {
// 获取裁剪后的试纸图片(RGBA格式)
cv::Mat paperMat = g_paperRecognizer.getPaperImg();
// 封装返回
napi_create_object(env, &object);
napi_set_named_property(env, object, "byteLength", paperMat.total()*4);
napi_set_named_property(env, object, "buffer", ...);
napi_set_named_property(env, object, "width", paperMat.cols);
napi_set_named_property(env, object, "height", paperMat.rows);
return object;
}
```
---
## 四、数据流程图
```
┌─────────────────────────────────────────────────────────────────┐
│ 上层应用 (ArkTS) │
│ ┌────────────────┐ │
│ │ 摄像头采集 │ → NV21数据 │
│ └────────────────┘ │
└────────────────────────────┬────────────────────────────────────┘
│ N-API
▼
┌─────────────────────────────────────────────────────────────────┐
│ napi_init.cpp │
│ ┌────────────────────────────────────────────────────────┐ │
│ │ SayHello │ │
│ │ 1. 解析NV21 → RGBA │ │
│ │ 2. 调用 PaperRecognizer │ │
│ │ 3. 读取颜色 (ColorReader) │ │
│ │ 4. 封装返回结果 │ │
│ └────────────────────────────────────────────────────────┘ │
└────────────────────────────┬────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────┐
│ PaperRecognizer │
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
│ │ 图像预处理 │→│ 定位块检测 │→│ 规格匹配 │→│ 色块计算 │ │
│ └──────────┘ └──────────┘ └──────────┘ └──────────┘ │
│ │ │ │ │ │
│ ▼ ▼ ▼ ▼ │
│ 高斯滤波 轮廓检测 distRatio 中心点坐标 │
│ 颜色转换 多试纸处理 areaRatio │
│ 颜色判断 │
└────────────────────────────┬────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────┐
│ ColorReader │
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
│ │ 白平衡 │→│ ROI采样 │→│ 颜色转换 │ │
│ └──────────┘ └──────────┘ └──────────┘ │
│ │ │ │ │
│ ▼ ▼ ▼ │
│ 灰度世界 自适应大小 HSV + LAB │
└────────────────────────────┬────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────┐
│ 返回结果 │
│ { │
│ statusCode: 0, │
│ buffer: 标注图像(RGBA), │
│ strBuffer: [{id:0, value:"H,S,V,L,A,B"}, ...] │
│ } │
└─────────────────────────────────────────────────────────────────┘
```
---
## 五、错误处理
### 5.1 错误码定义
```cpp
// napi_init.cpp
// ========== 错误码定义 ==========
// 0 - 成功
// 1 - 未检测到定位块
// 2 - 定位块颜色识别失败
// 3 - 定位块配对失败(一大一小)
// 4 - 试纸规格匹配失败
// 5 - 色块位置计算失败
// 6 - 检测到多条试纸
// 7 - 图像处理错误
// 8 - 输入图像为空
// 9 - 试纸太小/杂物(试纸高度<30px)
// 10 - 场景杂乱(候选定位块>15个)
// 100 - 未知错误
// ===============================
```
### 5.2 错误处理策略
| 错误场景 | 处理方式 | 返回内容 |
|----------|----------|----------|
| 未检测到定位块 | 返回原图 + statusCode=1 | buffer=原图, strBuffer=空 |
| 多条试纸 | 返回原图 + statusCode=6 | 提示用户单独拍摄 |
| 规格不匹配 | 返回原图 + statusCode=4 | 可能是非标准试纸 |
| 试纸太小/杂物 | 返回原图 + statusCode=9 | 提示用户靠近拍摄 |
| 场景杂乱 | 返回原图 + statusCode=10 | 提示用户简化背景 |
| 其他错误 | 返回原图 + statusCode=100 | 检查日志 |
---
## 六、关键算法参数
### 6.1 定位块检测参数
```cpp
// 面积过滤
const double MIN_LOCATOR_AREA = 100;
const double MAX_LOCATOR_AREA = 10000;
// 宽高比
const double BIG_LOCATOR_RATIO_SHORT = 2.0; // 10mm
const double BIG_LOCATOR_RATIO_LONG = 3.0; // 15mm
const double SMALL_LOCATOR_RATIO = 1.667;
const double ASPECT_RATIO_TOLERANCE = 0.35; // 收紧容差(原为0.5)
const double MAX_ASPECT_RATIO = 4.0; // 最大宽高比限制(新增)
// 面积比
const double AREA_RATIO_SHORT = 3.33; // 10mm大定位块
const double AREA_RATIO_LONG = 5.0; // 15mm大定位块
const double AREA_RATIO_TOLERANCE = 1.0;
// 颜色距离阈值
const double COLOR_DISTANCE_THRESHOLD = 20.0;
// 杂物检测阈值(新增)
const double MIN_PAPER_HEIGHT = 30.0; // 最小试纸高度(像素)
const size_t MAX_CANDIDATE_COUNT = 15; // 最大候选定位块数量
```
### 6.2 清晰度评估参数(新增)
```cpp
// Laplacian 方差法阈值
const double BLUR_THRESHOLD_NONE = 300; // >300: 清晰
const double BLUR_THRESHOLD_SLIGHT = 100; // 100-300: 轻微模糊
// <100: 严重模糊
const double BLUR_ROI_EXPAND_RATIO = 0.15; // ROI外扩15%
```
### 6.3 颜色判断阈值
```cpp
// RGB 阈值
const int RED_MARGIN = 20; // R > G+20 && R > B+20
const int GREEN_MARGIN = 10; // G > R+10 && G > B+10
const int BLUE_MARGIN = 15; // B > R+15 && B > G+15 (降低处理深蓝)
// HSV 范围
// 红色: H < 10 或 H > 170
// 绿色: 35 < H < 85
// 蓝色: H >= 95
```
### 6.4 自适应ROI参数
```cpp
// ROI大小 = 定位块高度 × 0.6
// 范围: 5×5 到 15×15 像素
// 对应 ROI: 11×11 到 31×31 像素
```
### 6.5 清晰度评估算法(新增)
**算法**:Laplacian 方差法
**步骤**:
1. 提取定位块 ROI(外扩15%)
2. 计算 Laplacian 算子
3. 计算方差(标准差的平方)
4. 根据阈值判断清晰度等级
**优势**:
- 只在定位块局部区域计算,不受图像大小影响
- 计算量小,实时性好
- 对运动模糊敏感
---
## 七、性能优化
### 7.1 已实现的优化
1. **早期过滤**
- 正方形轮廓早期排除(宽高比0.9-1.1)
- 面积过小/过大早期排除
2. **延迟计算**
- 颜色计算延迟到候选确定后
- 从N次减少到2次
3. **位置排序策略**
- 按X坐标排序,只取最左最右
- 中间轮廓不参与定位
### 7.2 内存管理
- 使用引用避免不必要的拷贝
- 及时释放中间变量
- `cv::Mat` 使用 `clone()` 显式拷贝
---
## 八、测试与验证
### 8.1 Windows测试程序
```bash
# 编译
cd entry/src/main/cpp/test/build
cmake --build . --config Debug
# 运行
.\bin\Debug\urine_test.exe ..\..\images\14red_1.png
```
### 8.2 输出文件
| 文件 | 说明 |
|------|------|
| *_annotated.jpg | 标注图(定位块框+色块圈+编号)|
| *_paper.jpg | 裁剪的试纸图 |
| *_colors.txt | 颜色结果文本 |
| *_restored_no_wb.jpg | 无白平衡还原图 |
| *_restored_with_wb.jpg | 有白平衡还原图 |
| *_colors_no_wb.json | 无白平衡颜色JSON |
| *_colors_with_wb.json | 有白平衡颜色JSON |
### 8.3 鸿蒙HAP构建
```powershell
$env:DEVECO_SDK_HOME = "D:\DevEco Studio\sdk"
cd E:\Qoder\urologist
& "D:\DevEco Studio\tools\hvigor\bin\hvigorw.bat" assembleHap --mode module -p product=default --no-daemon
```
---
## 九、附录
### A. 文件依赖关系
```
napi_init.cpp
├── PaperRecognizer.h
│ ├── Types.h
│ ├── PaperConfig.h
│ └── ColorReader.h
│ └── Types.h
└── OpenCV
```
### B. CMakeLists.txt 配置
```cmake
add_library(
entry
SHARED
napi_init.cpp
PaperRecognizer.cpp
PaperConfig.cpp
ColorReader.cpp
)
target_link_libraries(entry PUBLIC
libace_napi.z.so
libhilog_ndk.z.so
libpixelmap_ndk.z.so
${OpenCV_LIBS}
)
```
### C. 版本历史
| 版本 | 日期 | 变更说明 |
|------|------|----------|
| v1.0 | 2026-03-31 | 初始设计 |
| v2.0 | 2026-03-30 | NAPI集成完成,多试纸处理,色块编号修正 |
---
*文档结束*
Binary file not shown.

After

Width:  |  Height:  |  Size: 342 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 420 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 331 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 293 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 33 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 402 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 507 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.1 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

+462
View File
@@ -0,0 +1,462 @@
#include "hilog/log.h"
#include "napi/native_api.h"
#include <multimedia/image_framework/image_mdk.h>
#include <multimedia/image_framework/image_mdk_common.h>
#include <multimedia/image_framework/image_pixel_map_mdk.h>
#include <multimedia/image_framework/image_pixel_map_napi.h>
#include "PaperRecognizer.h"
#include "ColorReader.h"
#include "Types.h"
#include <bits/alltypes.h>
#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/opencv.hpp>
#include <vector>
using namespace cv;
using namespace std;
// ========== 错误码定义 ==========
// 0 - 成功
// 1 - 未检测到定位块
// 2 - 定位块颜色识别失败
// 3 - 定位块配对失败(一大一小)
// 4 - 试纸规格匹配失败
// 5 - 色块位置计算失败
// 6 - 检测到多条试纸
// 7 - 图像处理错误
// 100 - 未知错误
// ===============================
// https://developer.huawei.com/consumer/cn/doc/harmonyos-faqs/faqs-image-18
// https://segmentfault.com/q/1010000045355419
// https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/image-pixelmap-operation-native
// https://developer.huawei.com/consumer/cn/doc/harmonyos-guides-V5/image-pixelmap-operation-native-V5
//// 假设你有一个cv::Mat对象
// cv::Mat cvMat = ...; // 假设这是你的OpenCV图像
//
//// 转换为PixelMap(伪代码)
//// 注意:以下代码需要根据你的实际环境进行调整
// PixelMap* pixelMap = new PixelMap();
//
//// 设置PixelMap的大小和格式
//// 例如,如果cvMat是RGBA格式,你可能需要设置PixelMap的pixelFormat为PixelFormat::RGBA_8888
// pixelMap->SetSize(cvMat.cols, cvMat.rows);
//// 设置PixelFormat等(根据cvMat的类型调整)
//
//// 分配PixelMap的像素缓冲区(具体API根据HarmonyOS版本而定)
//// 假设有一个SetPixelBytes方法来设置像素数据
// uint8_t* buffer = new uint8_t[cvMat.total() * cvMat.elemSize()];
// memcpy(buffer, cvMat.data, cvMat.total() * cvMat.elemSize());
// pixelMap->SetPixelBytes(buffer, cvMat.total() * cvMat.elemSize());
//
//// 注意:上面的SetPixelBytes方法是伪造的,你需要根据HarmonyOS的API来实现
//
//// 现在,pixelMap包含了cvMat的图像数据,可以在ArkTS中使用
//// ... 将pixelMap传递给ArkTS的UI组件 ...
//
//// 清理资源
// delete[] buffer;
static napi_value AnalysisImage(napi_env env, napi_callback_info info) {
napi_value udfVar = nullptr;
napi_value pixelMap = nullptr;
size_t argc = 2;
napi_value args[2] = {nullptr};
napi_get_cb_info(env, info, &argc, args, nullptr, nullptr);
// napi_value error;
// napi_create_int32(env, -1, &error);
NativePixelMap *native = OH_PixelMap_InitNativePixelMap(env, args[0]);
if (native == nullptr) {
return udfVar;
}
struct OhosPixelMapInfos pixelMapInfos;
if (OH_PixelMap_GetImageInfo(native, &pixelMapInfos) != IMAGE_RESULT_SUCCESS) {
OH_LOG_Print(LOG_APP, LOG_ERROR, 0xFF00, "Test", "Pure : -1");
return udfVar;
}
void *pixel;
// 获取NativePixelMap对象的内存地址并锁定内存
OH_PixelMap_AccessPixels(native, &pixel);
// 转换成Mat,注意对齐,所以要传入rowSize
cv::Mat originMat(pixelMapInfos.height, pixelMapInfos.width, CV_8UC4, pixel, pixelMapInfos.rowSize);
if (!originMat.data) {
OH_LOG_Print(LOG_APP, LOG_ERROR, 0xFF00, "Read Image", "Pure : -1");
return udfVar;
}
OH_PixelMap_UnAccessPixels(native);
cv::Mat saveMat;
cv::cvtColor(originMat, saveMat, cv::COLOR_BGRA2RGBA);
cv::Mat grayMat;
cv::cvtColor(saveMat, grayMat, cv::COLOR_RGB2GRAY);
struct OhosPixelMapCreateOps createOps;
createOps.width = grayMat.cols;
createOps.height = grayMat.rows;
createOps.pixelFormat = 1; // 4
createOps.alphaType = 0;
size_t bufferSize = createOps.width * createOps.height * 1; // 4
void *buff = malloc(bufferSize);
if (buff == nullptr) {
return udfVar;
}
memcpy(buff, grayMat.data, grayMat.total() * grayMat.elemSize());
int32_t res = OH_PixelMap_CreatePixelMap(env, createOps, (uint8_t *)buff, bufferSize, &pixelMap);
free(buff);
if (res != IMAGE_RESULT_SUCCESS || pixelMap == nullptr) {
return udfVar;
}
return pixelMap;
// opencv默认bgra或bgr,若pixelmap创建时未指定为这两种格式,需要进行格式转换
// cv::Mat saveMat;
// cv::cvtColor(originMat, saveMat, cv::COLOR_BGRA2RGBA);
// char pathArray[1024];
// size_t length;
// napi_get_value_string_utf8(env, args[1], pathArray, 1024, &length);
// std::string path(pathArray);
// path += "/access.jpg";
// if (!cv::imwrite(path, saveMat)) {
// OH_LOG_Print(LOG_APP, LOG_ERROR, 0xFF00, "Write Image", "Pure : -1");
// return error;
// }
//
// napi_value res;
// napi_create_int32(env, 1, &res);
// return res;
}
// ImageLocation imgLoc;
static napi_value Add(napi_env env, napi_callback_info info) {
size_t argc = 2;
napi_value args[2] = {nullptr};
napi_get_cb_info(env, info, &argc, args, nullptr, nullptr);
napi_valuetype valuetype0;
napi_typeof(env, args[0], &valuetype0);
napi_valuetype valuetype1;
napi_typeof(env, args[1], &valuetype1);
double value0;
napi_get_value_double(env, args[0], &value0);
double value1;
napi_get_value_double(env, args[1], &value1);
cv::Scalar scalar(10);
scalar[0] = 1;
scalar[1] = 10;
scalar[2] = 100;
scalar[3] = 1000;
napi_value sum;
napi_create_double(env, value0 + value1 + scalar[2], &sum);
return sum;
}
// 全局 PaperRecognizer 实例
PaperRecognizer g_paperRecognizer;
static napi_value GetPaperData(napi_env env, napi_callback_info info) {
// Create a result object.
napi_value object = nullptr;
napi_create_object(env, &object);
size_t length;
Mat paperMat = g_paperRecognizer.getPaperImg();
length = paperMat.total() * paperMat.elemSize();
// parameter 1: byteLength
napi_value byteLengthValue = nullptr;
napi_create_uint32(env, length, &byteLengthValue);
napi_set_named_property(env, object, "byteLength", byteLengthValue);
// parameter 2: buffer
napi_value bufferData;
void *output_ptr = nullptr;
napi_create_arraybuffer(env, length, &output_ptr, &bufferData);
if (output_ptr != nullptr) {
memcpy(output_ptr, paperMat.data, length);
} else {
OH_LOG_ERROR(LOG_APP, "Failed to allocate memory for ArrayBuffer");
return nullptr;
}
napi_set_named_property(env, object, "buffer", bufferData);
// parameter 3: width
napi_value widthValue = nullptr;
napi_create_uint32(env, paperMat.cols, &widthValue);
napi_set_named_property(env, object, "width", widthValue);
// parameter 4: height
napi_value heightValue = nullptr;
napi_create_uint32(env, paperMat.rows, &heightValue);
napi_set_named_property(env, object, "height", heightValue);
return object;
}
// 计算自适应ROI大小(基于定位块高度)
static int calculateAdaptiveROI(double locatorHeight) {
// 使用定位块高度的0.6倍作为ROI半边长
int halfSize = static_cast<int>(locatorHeight * 0.6);
// 限制在5-15像素范围内(即ROI大小25-225像素)
halfSize = std::max(5, std::min(15, halfSize));
return halfSize * 2 + 1; // 返回奇数边长(如11, 21, 31)
}
static napi_value SayHello(napi_env env, napi_callback_info info) {
size_t argc = 4;
napi_value args[4] = {nullptr};
napi_get_cb_info(env, info, &argc, args, nullptr, nullptr);
napi_valuetype valuetype0;
napi_typeof(env, args[1], &valuetype0);
napi_valuetype valuetype1;
napi_typeof(env, args[2], &valuetype1);
napi_valuetype valuetype2;
napi_typeof(env, args[3], &valuetype2);
int width;
napi_get_value_int32(env, args[1], &width); // width
int height;
napi_get_value_int32(env, args[2], &height); // height
int format;
napi_get_value_int32(env, args[3], &format); // 8
bool result = false;
napi_status status = napi_is_arraybuffer(env, args[0], &result);
if (status != napi_ok) {
napi_throw_error(env, nullptr, "Node-API napi_is_arraybuffer fail");
return nullptr;
}
void *inputBuffer;
size_t length;
napi_get_arraybuffer_info(env, args[0], &inputBuffer, &length);
// 转换 NV21 到 RGBA
cv::Mat yuv_mat(height + height / 2, width, CV_8UC1, inputBuffer);
cv::Mat rgb_full_mat;//rgb_mat
cv::Mat t;
cv::cvtColor(yuv_mat, t, cv::COLOR_YUV2RGBA_NV21);
cv::rotate(t, rgb_full_mat, cv::ROTATE_90_CLOCKWISE);//rgb_mat
//只使用原始图片的一半高度,进行识别,这样会提高识别效率和显示流畅性
cv::Rect rc_half;
rc_half.width = rgb_full_mat.cols;
rc_half.height = rgb_full_mat.rows / 2;
Mat rgb_mat(rgb_full_mat, rc_half);
// 转换为 BGRA 供 PaperRecognizer 使用
cv::Mat bgra_mat;
cvtColor(rgb_mat, bgra_mat, COLOR_RGBA2BGRA);
// 使用新代码进行识别
RecognitionResult recResult = g_paperRecognizer.recognize(bgra_mat);
// 准备输出图像(标注图)
cv::Mat outputImage;
if (!recResult.annotatedImage.empty()) {
cvtColor(recResult.annotatedImage, outputImage, COLOR_BGRA2RGBA);
} else {
outputImage = rgb_mat.clone();
}
// 准备颜色结果字符串
string strResult = "";
uint32_t statusCode = 100; // 默认未知错误
if (recResult.success) {
statusCode = 0; // 成功
// 使用 ColorReader 读取颜色(白平衡已应用)
ColorReader colorReader;
std::vector<ColorResult> colors = colorReader.readColors(
bgra_mat, recResult.colorBlockCenters, recResult.smallLocatorOnLeft, true);
// 计算自适应ROI大小
double locatorHeight = 0;
if (!recResult.colorBlockCenters.empty()) {
// 使用定位块高度估算(从色块中心点间距推算)
if (recResult.colorBlockCenters.size() >= 2) {
float dy = std::abs(recResult.colorBlockCenters[1].y - recResult.colorBlockCenters[0].y);
locatorHeight = dy * 0.8; // 估算定位块高度
}
}
int roiSize = calculateAdaptiveROI(locatorHeight);
int halfRoi = roiSize / 2;
// 重新读取颜色(使用自适应ROI)
// 注意:PaperRecognizer::calculateColorBlockCenters 已保证
// centers_[0] 始终靠近小定位块,无论试纸如何放置
colors.clear();
for (size_t i = 0; i < recResult.colorBlockCenters.size(); i++) {
const cv::Point2f& center = recResult.colorBlockCenters[i];
cv::Rect roi(center.x - halfRoi, center.y - halfRoi, roiSize, roiSize);
// 边界检查
roi.x = std::max(0, roi.x);
roi.y = std::max(0, roi.y);
roi.width = std::min(roi.width, bgra_mat.cols - roi.x);
roi.height = std::min(roi.height, bgra_mat.rows - roi.y);
if (roi.width > 0 && roi.height > 0) {
Mat roiMat(bgra_mat, roi);
Mat rgbRoi;
cvtColor(roiMat, rgbRoi, COLOR_BGRA2RGB);
Mat hsvMat, labMat;
cvtColor(rgbRoi, hsvMat, COLOR_RGB2HSV);
cvtColor(rgbRoi, labMat, COLOR_RGB2Lab);
Scalar avgHsv = mean(hsvMat);
Scalar avgLab = mean(labMat);
char buf[100];
sprintf(buf, "{\"id\":%d,\"value\":\"%.1f,%.1f,%.1f,%.1f,%.1f,%.1f\"}",
(int)i,
avgHsv.val[0], avgHsv.val[1], avgHsv.val[2],
avgLab.val[0], avgLab.val[1], avgLab.val[2]);
if (i > 0) strResult += ",";
strResult += buf;
}
}
strResult = "[" + strResult + "]";
} else {
// 映射错误码
statusCode = recResult.errorCode;
if (statusCode == 0 && !recResult.success) {
statusCode = 100; // 未知错误
}
}
length = outputImage.total() * outputImage.elemSize();
// Create a result object.
napi_value object = nullptr;
napi_create_object(env, &object);
// parameter 1: byteLength
napi_value byteLengthValue = nullptr;
napi_create_uint32(env, length, &byteLengthValue);
napi_set_named_property(env, object, "byteLength", byteLengthValue);
// parameter 2: buffer
napi_value bufferData;
void *output_ptr = nullptr;
napi_create_arraybuffer(env, length, &output_ptr, &bufferData);
if (output_ptr != nullptr) {
memcpy(output_ptr, outputImage.data, length);
} else {
OH_LOG_ERROR(LOG_APP, "Failed to allocate memory for ArrayBuffer");
return nullptr;
}
napi_set_named_property(env, object, "buffer", bufferData);
// parameter 3: width
napi_value widthValue = nullptr;
napi_create_uint32(env, outputImage.cols, &widthValue);
napi_set_named_property(env, object, "width", widthValue);
// parameter 4: height
napi_value heightValue = nullptr;
napi_create_uint32(env, outputImage.rows, &heightValue);
napi_set_named_property(env, object, "height", heightValue);
// parameter 5: strLength
napi_value strLengthValue = nullptr;
napi_create_uint32(env, strResult.length(), &strLengthValue);
napi_set_named_property(env, object, "strLength", strLengthValue);
// parameter 6: strBuffer
if (strResult.length() > 10) {
napi_value bufferStrData;
void *output_str_ptr = nullptr;
napi_create_arraybuffer(env, strResult.length(), &output_str_ptr, &bufferStrData);
if (output_str_ptr != nullptr) {
memcpy(output_str_ptr, strResult.data(), strResult.length());
} else {
OH_LOG_ERROR(LOG_APP, "Failed to allocate memory for ArrayBuffer 2");
return nullptr;
}
napi_set_named_property(env, object, "strBuffer", bufferStrData);
}
// parameter 7: locatorColor (定位块颜色)
napi_value locatorColorValue = nullptr;
napi_create_uint32(env, static_cast<uint32_t>(recResult.locatorColor), &locatorColorValue);
napi_set_named_property(env, object, "locatorColor", locatorColorValue);
// parameter 8: paperType (试纸空间类型)
napi_value paperTypeValue = nullptr;
napi_create_uint32(env, static_cast<uint32_t>(recResult.paperType), &paperTypeValue);
napi_set_named_property(env, object, "paperType", paperTypeValue);
// parameter 9: blurLevel (清晰度等级)
napi_value blurLevelValue = nullptr;
napi_create_uint32(env, static_cast<uint32_t>(recResult.blurLevel), &blurLevelValue);
napi_set_named_property(env, object, "blurLevel", blurLevelValue);
// parameter 10: blurScore (清晰度分数)
napi_value blurScoreValue = nullptr;
napi_create_double(env, recResult.blurScore, &blurScoreValue);
napi_set_named_property(env, object, "blurScore", blurScoreValue);
// parameter 11: status code
napi_value statusValue = nullptr;
napi_create_uint32(env, statusCode, &statusValue);
napi_set_named_property(env, object, "statusCode", statusValue);
return object;
}
EXTERN_C_START
static napi_value Init(napi_env env, napi_value exports) {
napi_property_descriptor desc[] = {
{"analysisImage", nullptr, AnalysisImage, nullptr, nullptr, nullptr, napi_default, nullptr},
{"sayHello", nullptr, SayHello, nullptr, nullptr, nullptr, napi_default, nullptr},
{"add", nullptr, Add, nullptr, nullptr, nullptr, napi_default, nullptr},
{"getPaperData", nullptr, GetPaperData, nullptr, nullptr, nullptr, napi_default, nullptr}};
napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc);
return exports;
}
EXTERN_C_END
static napi_module demoModule = {
.nm_version = 1,
.nm_flags = 0,
.nm_filename = nullptr,
.nm_register_func = Init,
.nm_modname = "entry",
.nm_priv = ((void *)0),
.reserved = {0},
};
extern "C" __attribute__((constructor)) void RegisterEntryModule(void) { napi_module_register(&demoModule); }
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,48 @@
/*M///////////////////////////////////////////////////////////////////////////////////////
//
// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
//
// By downloading, copying, installing or using the software you agree to this license.
// If you do not agree to this license, do not download, install,
// copy or use the software.
//
//
// License Agreement
// For Open Source Computer Vision Library
//
// Copyright (C) 2000-2008, Intel Corporation, all rights reserved.
// Copyright (C) 2009, Willow Garage Inc., all rights reserved.
// Copyright (C) 2013, OpenCV Foundation, all rights reserved.
// Third party copyrights are property of their respective owners.
//
// Redistribution and use in source and binary forms, with or without modification,
// are permitted provided that the following conditions are met:
//
// * Redistribution's of source code must retain the above copyright notice,
// this list of conditions and the following disclaimer.
//
// * Redistribution's in binary form must reproduce the above copyright notice,
// this list of conditions and the following disclaimer in the documentation
// and/or other materials provided with the distribution.
//
// * The name of the copyright holders may not be used to endorse or promote products
// derived from this software without specific prior written permission.
//
// This software is provided by the copyright holders and contributors "as is" and
// any express or implied warranties, including, but not limited to, the implied
// warranties of merchantability and fitness for a particular purpose are disclaimed.
// In no event shall the Intel Corporation or contributors be liable for any direct,
// indirect, incidental, special, exemplary, or consequential damages
// (including, but not limited to, procurement of substitute goods or services;
// loss of use, data, or profits; or business interruption) however caused
// and on any theory of liability, whether in contract, strict liability,
// or tort (including negligence or otherwise) arising in any way out of
// the use of this software, even if advised of the possibility of such damage.
//
//M*/
#ifdef __OPENCV_BUILD
#error this is a compatibility header which should not be used inside the OpenCV library
#endif
#include "opencv2/calib3d.hpp"
@@ -0,0 +1,150 @@
/*M///////////////////////////////////////////////////////////////////////////////////////
//
// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
//
// By downloading, copying, installing or using the software you agree to this license.
// If you do not agree to this license, do not download, install,
// copy or use the software.
//
//
// License Agreement
// For Open Source Computer Vision Library
//
// Copyright (C) 2000-2008, Intel Corporation, all rights reserved.
// Copyright (C) 2009, Willow Garage Inc., all rights reserved.
// Copyright (C) 2013, OpenCV Foundation, all rights reserved.
// Third party copyrights are property of their respective owners.
//
// Redistribution and use in source and binary forms, with or without modification,
// are permitted provided that the following conditions are met:
//
// * Redistribution's of source code must retain the above copyright notice,
// this list of conditions and the following disclaimer.
//
// * Redistribution's in binary form must reproduce the above copyright notice,
// this list of conditions and the following disclaimer in the documentation
// and/or other materials provided with the distribution.
//
// * The name of the copyright holders may not be used to endorse or promote products
// derived from this software without specific prior written permission.
//
// This software is provided by the copyright holders and contributors "as is" and
// any express or implied warranties, including, but not limited to, the implied
// warranties of merchantability and fitness for a particular purpose are disclaimed.
// In no event shall the Intel Corporation or contributors be liable for any direct,
// indirect, incidental, special, exemplary, or consequential damages
// (including, but not limited to, procurement of substitute goods or services;
// loss of use, data, or profits; or business interruption) however caused
// and on any theory of liability, whether in contract, strict liability,
// or tort (including negligence or otherwise) arising in any way out of
// the use of this software, even if advised of the possibility of such damage.
//
//M*/
#ifndef OPENCV_CALIB3D_C_H
#define OPENCV_CALIB3D_C_H
#include "opencv2/core/types_c.h"
#ifdef __cplusplus
extern "C" {
#endif
/* Calculates fundamental matrix given a set of corresponding points */
#define CV_FM_7POINT 1
#define CV_FM_8POINT 2
#define CV_LMEDS 4
#define CV_RANSAC 8
#define CV_FM_LMEDS_ONLY CV_LMEDS
#define CV_FM_RANSAC_ONLY CV_RANSAC
#define CV_FM_LMEDS CV_LMEDS
#define CV_FM_RANSAC CV_RANSAC
enum
{
CV_ITERATIVE = 0,
CV_EPNP = 1, // F.Moreno-Noguer, V.Lepetit and P.Fua "EPnP: Efficient Perspective-n-Point Camera Pose Estimation"
CV_P3P = 2, // X.S. Gao, X.-R. Hou, J. Tang, H.-F. Chang; "Complete Solution Classification for the Perspective-Three-Point Problem"
CV_DLS = 3 // Joel A. Hesch and Stergios I. Roumeliotis. "A Direct Least-Squares (DLS) Method for PnP"
};
#define CV_CALIB_CB_ADAPTIVE_THRESH 1
#define CV_CALIB_CB_NORMALIZE_IMAGE 2
#define CV_CALIB_CB_FILTER_QUADS 4
#define CV_CALIB_CB_FAST_CHECK 8
#define CV_CALIB_USE_INTRINSIC_GUESS 1
#define CV_CALIB_FIX_ASPECT_RATIO 2
#define CV_CALIB_FIX_PRINCIPAL_POINT 4
#define CV_CALIB_ZERO_TANGENT_DIST 8
#define CV_CALIB_FIX_FOCAL_LENGTH 16
#define CV_CALIB_FIX_K1 32
#define CV_CALIB_FIX_K2 64
#define CV_CALIB_FIX_K3 128
#define CV_CALIB_FIX_K4 2048
#define CV_CALIB_FIX_K5 4096
#define CV_CALIB_FIX_K6 8192
#define CV_CALIB_RATIONAL_MODEL 16384
#define CV_CALIB_THIN_PRISM_MODEL 32768
#define CV_CALIB_FIX_S1_S2_S3_S4 65536
#define CV_CALIB_TILTED_MODEL 262144
#define CV_CALIB_FIX_TAUX_TAUY 524288
#define CV_CALIB_FIX_TANGENT_DIST 2097152
#define CV_CALIB_NINTRINSIC 18
#define CV_CALIB_FIX_INTRINSIC 256
#define CV_CALIB_SAME_FOCAL_LENGTH 512
#define CV_CALIB_ZERO_DISPARITY 1024
/* stereo correspondence parameters and functions */
#define CV_STEREO_BM_NORMALIZED_RESPONSE 0
#define CV_STEREO_BM_XSOBEL 1
#ifdef __cplusplus
} // extern "C"
//////////////////////////////////////////////////////////////////////////////////////////
class CV_EXPORTS CvLevMarq
{
public:
CvLevMarq();
CvLevMarq( int nparams, int nerrs, CvTermCriteria criteria=
cvTermCriteria(CV_TERMCRIT_EPS+CV_TERMCRIT_ITER,30,DBL_EPSILON),
bool completeSymmFlag=false );
~CvLevMarq();
void init( int nparams, int nerrs, CvTermCriteria criteria=
cvTermCriteria(CV_TERMCRIT_EPS+CV_TERMCRIT_ITER,30,DBL_EPSILON),
bool completeSymmFlag=false );
bool update( const CvMat*& param, CvMat*& J, CvMat*& err );
bool updateAlt( const CvMat*& param, CvMat*& JtJ, CvMat*& JtErr, double*& errNorm );
void clear();
void step();
enum { DONE=0, STARTED=1, CALC_J=2, CHECK_ERR=3 };
cv::Ptr<CvMat> mask;
cv::Ptr<CvMat> prevParam;
cv::Ptr<CvMat> param;
cv::Ptr<CvMat> J;
cv::Ptr<CvMat> err;
cv::Ptr<CvMat> JtJ;
cv::Ptr<CvMat> JtJN;
cv::Ptr<CvMat> JtErr;
cv::Ptr<CvMat> JtJV;
cv::Ptr<CvMat> JtJW;
double prevErrNorm, errNorm;
int lambdaLg10;
CvTermCriteria criteria;
int state;
int iters;
bool completeSymmFlag;
int solveMethod;
};
#endif
#endif /* OPENCV_CALIB3D_C_H */
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,155 @@
#ifndef OPENCV_CVCONFIG_H_INCLUDED
#define OPENCV_CVCONFIG_H_INCLUDED
/* OpenCV compiled as static or dynamic libs */
#define BUILD_SHARED_LIBS
/* OpenCV intrinsics optimized code */
#define CV_ENABLE_INTRINSICS
/* OpenCV additional optimized code */
/* #undef CV_DISABLE_OPTIMIZATION */
/* Compile for 'real' NVIDIA GPU architectures */
#define CUDA_ARCH_BIN ""
/* NVIDIA GPU features are used */
#define CUDA_ARCH_FEATURES ""
/* Compile for 'virtual' NVIDIA PTX architectures */
#define CUDA_ARCH_PTX ""
/* AMD's Basic Linear Algebra Subprograms Library*/
/* #undef HAVE_CLAMDBLAS */
/* AMD's OpenCL Fast Fourier Transform Library*/
/* #undef HAVE_CLAMDFFT */
/* Clp support */
/* #undef HAVE_CLP */
/* NVIDIA CUDA Runtime API*/
/* #undef HAVE_CUDA */
/* NVIDIA CUDA Basic Linear Algebra Subprograms (BLAS) API*/
/* #undef HAVE_CUBLAS */
/* NVIDIA CUDA Deep Neural Network (cuDNN) API*/
/* #undef HAVE_CUDNN */
/* NVIDIA CUDA Fast Fourier Transform (FFT) API*/
/* #undef HAVE_CUFFT */
/* DirectX */
/* #undef HAVE_DIRECTX */
/* #undef HAVE_DIRECTX_NV12 */
/* #undef HAVE_D3D11 */
/* #undef HAVE_D3D10 */
/* #undef HAVE_D3D9 */
/* Eigen Matrix & Linear Algebra Library */
/* #undef HAVE_EIGEN */
/* Geospatial Data Abstraction Library */
/* #undef HAVE_GDAL */
/* Halide support */
/* #undef HAVE_HALIDE */
/* Vulkan support */
/* #undef HAVE_VULKAN */
/* Define to 1 if you have the <inttypes.h> header file. */
#define HAVE_INTTYPES_H 1
/* Intel Integrated Performance Primitives */
/* #undef HAVE_IPP */
/* #undef HAVE_IPP_ICV */
/* #undef HAVE_IPP_IW */
/* #undef HAVE_IPP_IW_LL */
/* JPEG-2000 codec */
#define HAVE_OPENJPEG
/* #undef HAVE_JASPER */
/* AVIF codec */
/* #undef HAVE_AVIF */
/* IJG JPEG codec */
#define HAVE_JPEG
/* libpng/png.h needs to be included */
/* #undef HAVE_LIBPNG_PNG_H */
/* GDCM DICOM codec */
/* #undef HAVE_GDCM */
/* NVIDIA Video Decoding API*/
/* #undef HAVE_NVCUVID */
/* #undef HAVE_NVCUVID_HEADER */
/* #undef HAVE_DYNLINK_NVCUVID_HEADER */
/* NVIDIA Video Encoding API*/
/* #undef HAVE_NVCUVENC */
/* OpenCL Support */
#define HAVE_OPENCL
/* #undef HAVE_OPENCL_STATIC */
/* #undef HAVE_OPENCL_SVM */
/* NVIDIA OpenCL D3D Extensions support */
/* #undef HAVE_OPENCL_D3D11_NV */
/* OpenEXR codec */
/* #undef HAVE_OPENEXR */
/* OpenGL support*/
/* #undef HAVE_OPENGL */
/* PNG codec */
#define HAVE_PNG
/* PNG codec */
/* #undef HAVE_SPNG */
/* Posix threads (pthreads) */
#define HAVE_PTHREAD
/* parallel_for with pthreads */
#define HAVE_PTHREADS_PF
/* Intel Threading Building Blocks */
/* #undef HAVE_TBB */
/* Ste||ar Group High Performance ParallelX */
/* #undef HAVE_HPX */
/* TIFF codec */
#define HAVE_TIFF
/* Define if your processor stores words with the most significant byte
first (like Motorola and SPARC, unlike Intel and VAX). */
/* #undef WORDS_BIGENDIAN */
/* VA library (libva) */
/* #undef HAVE_VA */
/* Intel VA-API/OpenCL */
/* #undef HAVE_VA_INTEL */
/* Lapack */
/* #undef HAVE_LAPACK */
/* Library was compiled with functions instrumentation */
/* #undef ENABLE_INSTRUMENTATION */
/* OpenVX */
/* #undef HAVE_OPENVX */
/* OpenCV trace utilities */
#define OPENCV_TRACE
/* Library QR-code decoding */
#define HAVE_QUIRC
#endif // OPENCV_CVCONFIG_H_INCLUDED
@@ -0,0 +1,78 @@
/*M///////////////////////////////////////////////////////////////////////////////////////
//
// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
//
// By downloading, copying, installing or using the software you agree to this license.
// If you do not agree to this license, do not download, install,
// copy or use the software.
//
//
// License Agreement
// For Open Source Computer Vision Library
//
// Copyright (C) 2013, OpenCV Foundation, all rights reserved.
// Third party copyrights are property of their respective owners.
//
// Redistribution and use in source and binary forms, with or without modification,
// are permitted provided that the following conditions are met:
//
// * Redistribution's of source code must retain the above copyright notice,
// this list of conditions and the following disclaimer.
//
// * Redistribution's in binary form must reproduce the above copyright notice,
// this list of conditions and the following disclaimer in the documentation
// and/or other materials provided with the distribution.
//
// * The name of the copyright holders may not be used to endorse or promote products
// derived from this software without specific prior written permission.
//
// This software is provided by the copyright holders and contributors "as is" and
// any express or implied warranties, including, but not limited to, the implied
// warranties of merchantability and fitness for a particular purpose are disclaimed.
// In no event shall the Intel Corporation or contributors be liable for any direct,
// indirect, incidental, special, exemplary, or consequential damages
// (including, but not limited to, procurement of substitute goods or services;
// loss of use, data, or profits; or business interruption) however caused
// and on any theory of liability, whether in contract, strict liability,
// or tort (including negligence or otherwise) arising in any way out of
// the use of this software, even if advised of the possibility of such damage.
//
//M*/
#ifndef OPENCV_DNN_HPP
#define OPENCV_DNN_HPP
// This is an umbrella header to include into you project.
// We are free to change headers layout in dnn subfolder, so please include
// this header for future compatibility
/** @defgroup dnn Deep Neural Network module
@{
This module contains:
- API for new layers creation, layers are building bricks of neural networks;
- set of built-in most-useful Layers;
- API to construct and modify comprehensive neural networks from layers;
- functionality for loading serialized networks models from different frameworks.
Functionality of this module is designed only for forward pass computations (i.e. network testing).
A network training is in principle not supported.
@}
*/
/** @example samples/dnn/classification.cpp
Check @ref tutorial_dnn_googlenet "the corresponding tutorial" for more details
*/
/** @example samples/dnn/colorization.cpp
*/
/** @example samples/dnn/object_detection.cpp
Check @ref tutorial_dnn_yolo "the corresponding tutorial" for more details
*/
/** @example samples/dnn/openpose.cpp
*/
/** @example samples/dnn/segmentation.cpp
*/
/** @example samples/dnn/text_detection.cpp
*/
#include <opencv2/dnn/dnn.hpp>
#endif /* OPENCV_DNN_HPP */
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,160 @@
/*M///////////////////////////////////////////////////////////////////////////////////////
//
// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
//
// By downloading, copying, installing or using the software you agree to this license.
// If you do not agree to this license, do not download, install,
// copy or use the software.
//
//
// License Agreement
// For Open Source Computer Vision Library
//
// Copyright (C) 2013, OpenCV Foundation, all rights reserved.
// Third party copyrights are property of their respective owners.
//
// Redistribution and use in source and binary forms, with or without modification,
// are permitted provided that the following conditions are met:
//
// * Redistribution's of source code must retain the above copyright notice,
// this list of conditions and the following disclaimer.
//
// * Redistribution's in binary form must reproduce the above copyright notice,
// this list of conditions and the following disclaimer in the documentation
// and/or other materials provided with the distribution.
//
// * The name of the copyright holders may not be used to endorse or promote products
// derived from this software without specific prior written permission.
//
// This software is provided by the copyright holders and contributors "as is" and
// any express or implied warranties, including, but not limited to, the implied
// warranties of merchantability and fitness for a particular purpose are disclaimed.
// In no event shall the Intel Corporation or contributors be liable for any direct,
// indirect, incidental, special, exemplary, or consequential damages
// (including, but not limited to, procurement of substitute goods or services;
// loss of use, data, or profits; or business interruption) however caused
// and on any theory of liability, whether in contract, strict liability,
// or tort (including negligence or otherwise) arising in any way out of
// the use of this software, even if advised of the possibility of such damage.
//
//M*/
#include <opencv2/core.hpp>
#include <map>
#include <ostream>
#include <opencv2/dnn/dnn.hpp>
#ifndef OPENCV_DNN_DNN_DICT_HPP
#define OPENCV_DNN_DNN_DICT_HPP
namespace cv {
namespace dnn {
CV__DNN_INLINE_NS_BEGIN
//! @addtogroup dnn
//! @{
/** @brief This struct stores the scalar value (or array) of one of the following type: double, cv::String or int64.
* @todo Maybe int64 is useless because double type exactly stores at least 2^52 integers.
*/
struct CV_EXPORTS_W DictValue
{
DictValue(const DictValue &r);
explicit DictValue(bool i) : type(Param::INT), pi(new AutoBuffer<int64,1>) { (*pi)[0] = i ? 1 : 0; } //!< Constructs integer scalar
explicit DictValue(int64 i = 0) : type(Param::INT), pi(new AutoBuffer<int64,1>) { (*pi)[0] = i; } //!< Constructs integer scalar
CV_WRAP explicit DictValue(int i) : type(Param::INT), pi(new AutoBuffer<int64,1>) { (*pi)[0] = i; } //!< Constructs integer scalar
explicit DictValue(unsigned p) : type(Param::INT), pi(new AutoBuffer<int64,1>) { (*pi)[0] = p; } //!< Constructs integer scalar
CV_WRAP explicit DictValue(double p) : type(Param::REAL), pd(new AutoBuffer<double,1>) { (*pd)[0] = p; } //!< Constructs floating point scalar
CV_WRAP explicit DictValue(const String &s) : type(Param::STRING), ps(new AutoBuffer<String,1>) { (*ps)[0] = s; } //!< Constructs string scalar
explicit DictValue(const char *s) : type(Param::STRING), ps(new AutoBuffer<String,1>) { (*ps)[0] = s; } //!< @overload
template<typename TypeIter>
static DictValue arrayInt(TypeIter begin, int size); //!< Constructs integer array
template<typename TypeIter>
static DictValue arrayReal(TypeIter begin, int size); //!< Constructs floating point array
template<typename TypeIter>
static DictValue arrayString(TypeIter begin, int size); //!< Constructs array of strings
template<typename T>
T get(int idx = -1) const; //!< Tries to convert array element with specified index to requested type and returns its.
int size() const;
CV_WRAP bool isInt() const;
CV_WRAP bool isString() const;
CV_WRAP bool isReal() const;
CV_WRAP int getIntValue(int idx = -1) const;
CV_WRAP double getRealValue(int idx = -1) const;
CV_WRAP String getStringValue(int idx = -1) const;
DictValue &operator=(const DictValue &r);
friend std::ostream &operator<<(std::ostream &stream, const DictValue &dictv);
~DictValue();
private:
Param type;
union
{
AutoBuffer<int64, 1> *pi;
AutoBuffer<double, 1> *pd;
AutoBuffer<String, 1> *ps;
void *pv;
};
DictValue(Param _type, void *_p) : type(_type), pv(_p) {}
void release();
};
/** @brief This class implements name-value dictionary, values are instances of DictValue. */
class CV_EXPORTS Dict
{
typedef std::map<String, DictValue> _Dict;
_Dict dict;
public:
//! Checks a presence of the @p key in the dictionary.
bool has(const String &key) const;
//! If the @p key in the dictionary then returns pointer to its value, else returns NULL.
DictValue *ptr(const String &key);
/** @overload */
const DictValue *ptr(const String &key) const;
//! If the @p key in the dictionary then returns its value, else an error will be generated.
const DictValue &get(const String &key) const;
/** @overload */
template <typename T>
T get(const String &key) const;
//! If the @p key in the dictionary then returns its value, else returns @p defaultValue.
template <typename T>
T get(const String &key, const T &defaultValue) const;
//! Sets new @p value for the @p key, or adds new key-value pair into the dictionary.
template<typename T>
const T &set(const String &key, const T &value);
//! Erase @p key from the dictionary.
void erase(const String &key);
friend std::ostream &operator<<(std::ostream &stream, const Dict &dict);
std::map<String, DictValue>::const_iterator begin() const;
std::map<String, DictValue>::const_iterator end() const;
};
//! @}
CV__DNN_INLINE_NS_END
}
}
#endif
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,412 @@
/*M///////////////////////////////////////////////////////////////////////////////////////
//
// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
//
// By downloading, copying, installing or using the software you agree to this license.
// If you do not agree to this license, do not download, install,
// copy or use the software.
//
//
// License Agreement
// For Open Source Computer Vision Library
//
// Copyright (C) 2013, OpenCV Foundation, all rights reserved.
// Third party copyrights are property of their respective owners.
//
// Redistribution and use in source and binary forms, with or without modification,
// are permitted provided that the following conditions are met:
//
// * Redistribution's of source code must retain the above copyright notice,
// this list of conditions and the following disclaimer.
//
// * Redistribution's in binary form must reproduce the above copyright notice,
// this list of conditions and the following disclaimer in the documentation
// and/or other materials provided with the distribution.
//
// * The name of the copyright holders may not be used to endorse or promote products
// derived from this software without specific prior written permission.
//
// This software is provided by the copyright holders and contributors "as is" and
// any express or implied warranties, including, but not limited to, the implied
// warranties of merchantability and fitness for a particular purpose are disclaimed.
// In no event shall the Intel Corporation or contributors be liable for any direct,
// indirect, incidental, special, exemplary, or consequential damages
// (including, but not limited to, procurement of substitute goods or services;
// loss of use, data, or profits; or business interruption) however caused
// and on any theory of liability, whether in contract, strict liability,
// or tort (including negligence or otherwise) arising in any way out of
// the use of this software, even if advised of the possibility of such damage.
//
//M*/
#ifndef OPENCV_DNN_DNN_INL_HPP
#define OPENCV_DNN_DNN_INL_HPP
#include <opencv2/dnn.hpp>
namespace cv {
namespace dnn {
CV__DNN_INLINE_NS_BEGIN
template<typename TypeIter>
DictValue DictValue::arrayInt(TypeIter begin, int size)
{
DictValue res(Param::INT, new AutoBuffer<int64, 1>(size));
for (int j = 0; j < size; begin++, j++)
(*res.pi)[j] = *begin;
return res;
}
template<typename TypeIter>
DictValue DictValue::arrayReal(TypeIter begin, int size)
{
DictValue res(Param::REAL, new AutoBuffer<double, 1>(size));
for (int j = 0; j < size; begin++, j++)
(*res.pd)[j] = *begin;
return res;
}
template<typename TypeIter>
DictValue DictValue::arrayString(TypeIter begin, int size)
{
DictValue res(Param::STRING, new AutoBuffer<String, 1>(size));
for (int j = 0; j < size; begin++, j++)
(*res.ps)[j] = *begin;
return res;
}
template<>
inline DictValue DictValue::get<DictValue>(int idx) const
{
CV_Assert(idx == -1);
return *this;
}
template<>
inline int64 DictValue::get<int64>(int idx) const
{
CV_Assert((idx == -1 && size() == 1) || (idx >= 0 && idx < size()));
idx = (idx == -1) ? 0 : idx;
if (type == Param::INT)
{
return (*pi)[idx];
}
else if (type == Param::REAL)
{
double doubleValue = (*pd)[idx];
double fracpart, intpart;
fracpart = std::modf(doubleValue, &intpart);
CV_Assert(fracpart == 0.0);
return (int64)doubleValue;
}
else if (type == Param::STRING)
{
return std::atoi((*ps)[idx].c_str());
}
else
{
CV_Assert(isInt() || isReal() || isString());
return 0;
}
}
template<>
inline int DictValue::get<int>(int idx) const
{
return (int)get<int64>(idx);
}
inline int DictValue::getIntValue(int idx) const
{
return (int)get<int64>(idx);
}
template<>
inline unsigned DictValue::get<unsigned>(int idx) const
{
return (unsigned)get<int64>(idx);
}
template<>
inline bool DictValue::get<bool>(int idx) const
{
return (get<int64>(idx) != 0);
}
template<>
inline double DictValue::get<double>(int idx) const
{
CV_Assert((idx == -1 && size() == 1) || (idx >= 0 && idx < size()));
idx = (idx == -1) ? 0 : idx;
if (type == Param::REAL)
{
return (*pd)[idx];
}
else if (type == Param::INT)
{
return (double)(*pi)[idx];
}
else if (type == Param::STRING)
{
return std::atof((*ps)[idx].c_str());
}
else
{
CV_Assert(isReal() || isInt() || isString());
return 0;
}
}
inline double DictValue::getRealValue(int idx) const
{
return get<double>(idx);
}
template<>
inline float DictValue::get<float>(int idx) const
{
return (float)get<double>(idx);
}
template<>
inline String DictValue::get<String>(int idx) const
{
CV_Assert(isString());
CV_Assert((idx == -1 && ps->size() == 1) || (idx >= 0 && idx < (int)ps->size()));
return (*ps)[(idx == -1) ? 0 : idx];
}
inline String DictValue::getStringValue(int idx) const
{
return get<String>(idx);
}
inline void DictValue::release()
{
switch (type)
{
case Param::INT:
delete pi;
break;
case Param::STRING:
delete ps;
break;
case Param::REAL:
delete pd;
break;
case Param::BOOLEAN:
case Param::MAT:
case Param::MAT_VECTOR:
case Param::ALGORITHM:
case Param::FLOAT:
case Param::UNSIGNED_INT:
case Param::UINT64:
case Param::UCHAR:
case Param::SCALAR:
break; // unhandled
}
}
inline DictValue::~DictValue()
{
release();
}
inline DictValue & DictValue::operator=(const DictValue &r)
{
if (&r == this)
return *this;
if (r.type == Param::INT)
{
AutoBuffer<int64, 1> *tmp = new AutoBuffer<int64, 1>(*r.pi);
release();
pi = tmp;
}
else if (r.type == Param::STRING)
{
AutoBuffer<String, 1> *tmp = new AutoBuffer<String, 1>(*r.ps);
release();
ps = tmp;
}
else if (r.type == Param::REAL)
{
AutoBuffer<double, 1> *tmp = new AutoBuffer<double, 1>(*r.pd);
release();
pd = tmp;
}
type = r.type;
return *this;
}
inline DictValue::DictValue(const DictValue &r)
: pv(NULL)
{
type = r.type;
if (r.type == Param::INT)
pi = new AutoBuffer<int64, 1>(*r.pi);
else if (r.type == Param::STRING)
ps = new AutoBuffer<String, 1>(*r.ps);
else if (r.type == Param::REAL)
pd = new AutoBuffer<double, 1>(*r.pd);
}
inline bool DictValue::isString() const
{
return (type == Param::STRING);
}
inline bool DictValue::isInt() const
{
return (type == Param::INT);
}
inline bool DictValue::isReal() const
{
return (type == Param::REAL || type == Param::INT);
}
inline int DictValue::size() const
{
switch (type)
{
case Param::INT:
return (int)pi->size();
case Param::STRING:
return (int)ps->size();
case Param::REAL:
return (int)pd->size();
case Param::BOOLEAN:
case Param::MAT:
case Param::MAT_VECTOR:
case Param::ALGORITHM:
case Param::FLOAT:
case Param::UNSIGNED_INT:
case Param::UINT64:
case Param::UCHAR:
case Param::SCALAR:
break; // unhandled
}
CV_Error_(Error::StsInternal, ("Unhandled type (%d)", static_cast<int>(type)));
}
inline std::ostream &operator<<(std::ostream &stream, const DictValue &dictv)
{
int i;
if (dictv.isInt())
{
for (i = 0; i < dictv.size() - 1; i++)
stream << dictv.get<int64>(i) << ", ";
stream << dictv.get<int64>(i);
}
else if (dictv.isReal())
{
for (i = 0; i < dictv.size() - 1; i++)
stream << dictv.get<double>(i) << ", ";
stream << dictv.get<double>(i);
}
else if (dictv.isString())
{
for (i = 0; i < dictv.size() - 1; i++)
stream << "\"" << dictv.get<String>(i) << "\", ";
stream << dictv.get<String>(i);
}
return stream;
}
/////////////////////////////////////////////////////////////////
inline bool Dict::has(const String &key) const
{
return dict.count(key) != 0;
}
inline DictValue *Dict::ptr(const String &key)
{
_Dict::iterator i = dict.find(key);
return (i == dict.end()) ? NULL : &i->second;
}
inline const DictValue *Dict::ptr(const String &key) const
{
_Dict::const_iterator i = dict.find(key);
return (i == dict.end()) ? NULL : &i->second;
}
inline const DictValue &Dict::get(const String &key) const
{
_Dict::const_iterator i = dict.find(key);
if (i == dict.end())
CV_Error(Error::StsObjectNotFound, "Required argument \"" + key + "\" not found into dictionary");
return i->second;
}
template <typename T>
inline T Dict::get(const String &key) const
{
return this->get(key).get<T>();
}
template <typename T>
inline T Dict::get(const String &key, const T &defaultValue) const
{
_Dict::const_iterator i = dict.find(key);
if (i != dict.end())
return i->second.get<T>();
else
return defaultValue;
}
template<typename T>
inline const T &Dict::set(const String &key, const T &value)
{
_Dict::iterator i = dict.find(key);
if (i != dict.end())
i->second = DictValue(value);
else
dict.insert(std::make_pair(key, DictValue(value)));
return value;
}
inline void Dict::erase(const String &key)
{
dict.erase(key);
}
inline std::ostream &operator<<(std::ostream &stream, const Dict &dict)
{
Dict::_Dict::const_iterator it;
for (it = dict.dict.begin(); it != dict.dict.end(); it++)
stream << it->first << " : " << it->second << "\n";
return stream;
}
inline std::map<String, DictValue>::const_iterator Dict::begin() const
{
return dict.begin();
}
inline std::map<String, DictValue>::const_iterator Dict::end() const
{
return dict.end();
}
CV__DNN_INLINE_NS_END
}
}
#endif
@@ -0,0 +1,78 @@
// This file is part of OpenCV project.
// It is subject to the license terms in the LICENSE file found in the top-level directory
// of this distribution and at http://opencv.org/license.html.
//
#ifndef OPENCV_DNN_LAYER_DETAILS_HPP
#define OPENCV_DNN_LAYER_DETAILS_HPP
#include <opencv2/dnn/layer.hpp>
namespace cv {
namespace dnn {
CV__DNN_INLINE_NS_BEGIN
/** @brief Registers layer constructor in runtime.
* @param type string, containing type name of the layer.
* @param constructorFunc pointer to the function of type LayerRegister::Constructor, which creates the layer.
* @details This macros must be placed inside the function code.
*/
#define CV_DNN_REGISTER_LAYER_FUNC(type, constructorFunc) \
cv::dnn::LayerFactory::registerLayer(#type, constructorFunc);
/** @brief Registers layer class in runtime.
* @param type string, containing type name of the layer.
* @param class C++ class, derived from Layer.
* @details This macros must be placed inside the function code.
*/
#define CV_DNN_REGISTER_LAYER_CLASS(type, class) \
cv::dnn::LayerFactory::registerLayer(#type, cv::dnn::details::_layerDynamicRegisterer<class>);
/** @brief Registers layer constructor on module load time.
* @param type string, containing type name of the layer.
* @param constructorFunc pointer to the function of type LayerRegister::Constructor, which creates the layer.
* @details This macros must be placed outside the function code.
*/
#define CV_DNN_REGISTER_LAYER_FUNC_STATIC(type, constructorFunc) \
static cv::dnn::details::_LayerStaticRegisterer __LayerStaticRegisterer_##type(#type, constructorFunc);
/** @brief Registers layer class on module load time.
* @param type string, containing type name of the layer.
* @param class C++ class, derived from Layer.
* @details This macros must be placed outside the function code.
*/
#define CV_DNN_REGISTER_LAYER_CLASS_STATIC(type, class) \
Ptr<Layer> __LayerStaticRegisterer_func_##type(LayerParams &params) \
{ return Ptr<Layer>(new class(params)); } \
static cv::dnn::details::_LayerStaticRegisterer __LayerStaticRegisterer_##type(#type, __LayerStaticRegisterer_func_##type);
namespace details {
template<typename LayerClass>
Ptr<Layer> _layerDynamicRegisterer(LayerParams &params)
{
return Ptr<Layer>(LayerClass::create(params));
}
//allows automatically register created layer on module load time
class _LayerStaticRegisterer
{
String type;
public:
_LayerStaticRegisterer(const String &layerType, LayerFactory::Constructor layerConstructor)
{
this->type = layerType;
LayerFactory::registerLayer(layerType, layerConstructor);
}
~_LayerStaticRegisterer()
{
LayerFactory::unregisterLayer(type);
}
};
} // namespace
CV__DNN_INLINE_NS_END
}} // namespace
#endif
@@ -0,0 +1,88 @@
/*M///////////////////////////////////////////////////////////////////////////////////////
//
// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
//
// By downloading, copying, installing or using the software you agree to this license.
// If you do not agree to this license, do not download, install,
// copy or use the software.
//
//
// License Agreement
// For Open Source Computer Vision Library
//
// Copyright (C) 2013, OpenCV Foundation, all rights reserved.
// Third party copyrights are property of their respective owners.
//
// Redistribution and use in source and binary forms, with or without modification,
// are permitted provided that the following conditions are met:
//
// * Redistribution's of source code must retain the above copyright notice,
// this list of conditions and the following disclaimer.
//
// * Redistribution's in binary form must reproduce the above copyright notice,
// this list of conditions and the following disclaimer in the documentation
// and/or other materials provided with the distribution.
//
// * The name of the copyright holders may not be used to endorse or promote products
// derived from this software without specific prior written permission.
//
// This software is provided by the copyright holders and contributors "as is" and
// any express or implied warranties, including, but not limited to, the implied
// warranties of merchantability and fitness for a particular purpose are disclaimed.
// In no event shall the Intel Corporation or contributors be liable for any direct,
// indirect, incidental, special, exemplary, or consequential damages
// (including, but not limited to, procurement of substitute goods or services;
// loss of use, data, or profits; or business interruption) however caused
// and on any theory of liability, whether in contract, strict liability,
// or tort (including negligence or otherwise) arising in any way out of
// the use of this software, even if advised of the possibility of such damage.
//
//M*/
#ifndef OPENCV_DNN_LAYER_HPP
#define OPENCV_DNN_LAYER_HPP
#include <opencv2/dnn.hpp>
namespace cv {
namespace dnn {
CV__DNN_INLINE_NS_BEGIN
//! @addtogroup dnn
//! @{
//!
//! @defgroup dnnLayerFactory Utilities for New Layers Registration
//! @{
/** @brief %Layer factory allows to create instances of registered layers. */
class CV_EXPORTS LayerFactory
{
public:
//! Each Layer class must provide this function to the factory
typedef Ptr<Layer>(*Constructor)(LayerParams &params);
//! Registers the layer class with typename @p type and specified @p constructor. Thread-safe.
static void registerLayer(const String &type, Constructor constructor);
//! Unregisters registered layer with specified type name. Thread-safe.
static void unregisterLayer(const String &type);
//! Check if layer is registered.
static bool isLayerRegistered(const std::string& type);
/** @brief Creates instance of registered layer.
* @param type type name of creating layer.
* @param params parameters which will be used for layer initialization.
* @note Thread-safe.
*/
static Ptr<Layer> createLayerInstance(const String &type, LayerParams& params);
private:
LayerFactory();
};
//! @}
//! @}
CV__DNN_INLINE_NS_END
}
}
#endif
@@ -0,0 +1,290 @@
/*M///////////////////////////////////////////////////////////////////////////////////////
//
// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
//
// By downloading, copying, installing or using the software you agree to this license.
// If you do not agree to this license, do not download, install,
// copy or use the software.
//
//
// License Agreement
// For Open Source Computer Vision Library
//
// Copyright (C) 2013, OpenCV Foundation, all rights reserved.
// Third party copyrights are property of their respective owners.
//
// Redistribution and use in source and binary forms, with or without modification,
// are permitted provided that the following conditions are met:
//
// * Redistribution's of source code must retain the above copyright notice,
// this list of conditions and the following disclaimer.
//
// * Redistribution's in binary form must reproduce the above copyright notice,
// this list of conditions and the following disclaimer in the documentation
// and/or other materials provided with the distribution.
//
// * The name of the copyright holders may not be used to endorse or promote products
// derived from this software without specific prior written permission.
//
// This software is provided by the copyright holders and contributors "as is" and
// any express or implied warranties, including, but not limited to, the implied
// warranties of merchantability and fitness for a particular purpose are disclaimed.
// In no event shall the Intel Corporation or contributors be liable for any direct,
// indirect, incidental, special, exemplary, or consequential damages
// (including, but not limited to, procurement of substitute goods or services;
// loss of use, data, or profits; or business interruption) however caused
// and on any theory of liability, whether in contract, strict liability,
// or tort (including negligence or otherwise) arising in any way out of
// the use of this software, even if advised of the possibility of such damage.
//
//M*/
#ifndef OPENCV_DNN_DNN_SHAPE_UTILS_HPP
#define OPENCV_DNN_DNN_SHAPE_UTILS_HPP
#include <opencv2/dnn/dnn.hpp>
#include <opencv2/core/types_c.h> // CV_MAX_DIM
#include <iostream>
#include <ostream>
#include <sstream>
namespace cv {
namespace dnn {
CV__DNN_INLINE_NS_BEGIN
//Slicing
struct _Range : public cv::Range
{
_Range(const Range &r) : cv::Range(r) {}
_Range(int start_, int size_ = 1) : cv::Range(start_, start_ + size_) {}
};
static inline Mat slice(const Mat &m, const _Range &r0)
{
Range ranges[CV_MAX_DIM];
for (int i = 1; i < m.dims; i++)
ranges[i] = Range::all();
ranges[0] = r0;
return m(&ranges[0]);
}
static inline Mat slice(const Mat &m, const _Range &r0, const _Range &r1)
{
CV_Assert(m.dims >= 2);
Range ranges[CV_MAX_DIM];
for (int i = 2; i < m.dims; i++)
ranges[i] = Range::all();
ranges[0] = r0;
ranges[1] = r1;
return m(&ranges[0]);
}
static inline Mat slice(const Mat &m, const _Range &r0, const _Range &r1, const _Range &r2)
{
CV_Assert(m.dims >= 3);
Range ranges[CV_MAX_DIM];
for (int i = 3; i < m.dims; i++)
ranges[i] = Range::all();
ranges[0] = r0;
ranges[1] = r1;
ranges[2] = r2;
return m(&ranges[0]);
}
static inline Mat slice(const Mat &m, const _Range &r0, const _Range &r1, const _Range &r2, const _Range &r3)
{
CV_Assert(m.dims >= 4);
Range ranges[CV_MAX_DIM];
for (int i = 4; i < m.dims; i++)
ranges[i] = Range::all();
ranges[0] = r0;
ranges[1] = r1;
ranges[2] = r2;
ranges[3] = r3;
return m(&ranges[0]);
}
static inline Mat getPlane(const Mat &m, int n, int cn)
{
CV_Assert(m.dims > 2);
int sz[CV_MAX_DIM];
for(int i = 2; i < m.dims; i++)
{
sz[i-2] = m.size.p[i];
}
return Mat(m.dims - 2, sz, m.type(), (void*)m.ptr<float>(n, cn));
}
static inline MatShape shape(const int* dims, const int n)
{
MatShape shape;
shape.assign(dims, dims + n);
return shape;
}
static inline MatShape shape(const Mat& mat)
{
return shape(mat.size.p, mat.dims);
}
static inline MatShape shape(const MatSize& sz)
{
return shape(sz.p, sz.dims());
}
static inline MatShape shape(const UMat& mat)
{
return shape(mat.size.p, mat.dims);
}
#if 0 // issues with MatExpr wrapped into InputArray
static inline
MatShape shape(InputArray input)
{
int sz[CV_MAX_DIM];
int ndims = input.sizend(sz);
return shape(sz, ndims);
}
#endif
namespace {inline bool is_neg(int i) { return i < 0; }}
static inline MatShape shape(int a0, int a1=-1, int a2=-1, int a3=-1)
{
int dims[] = {a0, a1, a2, a3};
MatShape s = shape(dims, 4);
s.erase(std::remove_if(s.begin(), s.end(), is_neg), s.end());
return s;
}
static inline int total(const MatShape& shape, int start = -1, int end = -1)
{
if (shape.empty())
return 0;
int dims = (int)shape.size();
if (start == -1) start = 0;
if (end == -1) end = dims;
CV_CheckLE(0, start, "");
CV_CheckLE(start, end, "");
CV_CheckLE(end, dims, "");
int elems = 1;
for (int i = start; i < end; i++)
{
elems *= shape[i];
}
return elems;
}
// TODO: rename to countDimsElements()
static inline int total(const Mat& mat, int start = -1, int end = -1)
{
if (mat.empty())
return 0;
int dims = mat.dims;
if (start == -1) start = 0;
if (end == -1) end = dims;
CV_CheckLE(0, start, "");
CV_CheckLE(start, end, "");
CV_CheckLE(end, dims, "");
int elems = 1;
for (int i = start; i < end; i++)
{
elems *= mat.size[i];
}
return elems;
}
static inline MatShape concat(const MatShape& a, const MatShape& b)
{
MatShape c = a;
c.insert(c.end(), b.begin(), b.end());
return c;
}
template<typename _Tp>
static inline std::string toString(const std::vector<_Tp>& shape, const String& name = "")
{
std::ostringstream ss;
if (!name.empty())
ss << name << ' ';
ss << '[';
for(size_t i = 0, n = shape.size(); i < n; ++i)
ss << ' ' << shape[i];
ss << " ]";
return ss.str();
}
template<typename _Tp>
static inline void print(const std::vector<_Tp>& shape, const String& name = "")
{
std::cout << toString(shape, name) << std::endl;
}
template<typename _Tp>
static inline std::ostream& operator<<(std::ostream &out, const std::vector<_Tp>& shape)
{
out << toString(shape);
return out;
}
/// @brief Converts axis from `[-dims; dims)` (similar to Python's slice notation) to `[0; dims)` range.
static inline
int normalize_axis(int axis, int dims)
{
CV_Check(axis, axis >= -dims && axis < dims, "");
axis = (axis < 0) ? (dims + axis) : axis;
CV_DbgCheck(axis, axis >= 0 && axis < dims, "");
return axis;
}
static inline
int normalize_axis(int axis, const MatShape& shape)
{
return normalize_axis(axis, (int)shape.size());
}
static inline
Range normalize_axis_range(const Range& r, int axisSize)
{
if (r == Range::all())
return Range(0, axisSize);
CV_CheckGE(r.start, 0, "");
Range clamped(r.start,
r.end > 0 ? std::min(r.end, axisSize) : axisSize + r.end + 1);
CV_DbgCheckGE(clamped.start, 0, "");
CV_CheckLT(clamped.start, clamped.end, "");
CV_CheckLE(clamped.end, axisSize, "");
return clamped;
}
static inline
bool isAllOnes(const MatShape &inputShape, int startPos, int endPos)
{
CV_Assert(!inputShape.empty());
CV_CheckGE((int) inputShape.size(), startPos, "");
CV_CheckGE(startPos, 0, "");
CV_CheckLE(startPos, endPos, "");
CV_CheckLE((size_t)endPos, inputShape.size(), "");
for (size_t i = startPos; i < endPos; i++)
{
if (inputShape[i] != 1)
return false;
}
return true;
}
CV__DNN_INLINE_NS_END
}
}
#endif
@@ -0,0 +1,24 @@
// This file is part of OpenCV project.
// It is subject to the license terms in the LICENSE file found in the top-level directory
// of this distribution and at http://opencv.org/license.html.
#ifndef OPENCV_DNN_UTILS_DEBUG_UTILS_HPP
#define OPENCV_DNN_UTILS_DEBUG_UTILS_HPP
#include "../dnn.hpp"
namespace cv { namespace dnn {
CV__DNN_INLINE_NS_BEGIN
/**
* @brief Skip model import after diagnostic run in readNet() functions.
* @param[in] skip Indicates whether to skip the import.
*
* This is an internal OpenCV function not intended for users.
*/
CV_EXPORTS void skipModelImport(bool skip);
CV__DNN_INLINE_NS_END
}} // namespace
#endif // OPENCV_DNN_UTILS_DEBUG_UTILS_HPP
@@ -0,0 +1,82 @@
// This file is part of OpenCV project.
// It is subject to the license terms in the LICENSE file found in the top-level directory
// of this distribution and at http://opencv.org/license.html.
//
// Copyright (C) 2018-2019, Intel Corporation, all rights reserved.
// Third party copyrights are property of their respective owners.
#ifndef OPENCV_DNN_UTILS_INF_ENGINE_HPP
#define OPENCV_DNN_UTILS_INF_ENGINE_HPP
#include "../dnn.hpp"
namespace cv { namespace dnn {
CV__DNN_INLINE_NS_BEGIN
/* Values for 'OPENCV_DNN_BACKEND_INFERENCE_ENGINE_TYPE' parameter */
/// @deprecated
#define CV_DNN_BACKEND_INFERENCE_ENGINE_NN_BUILDER_API "NN_BUILDER"
/// @deprecated
#define CV_DNN_BACKEND_INFERENCE_ENGINE_NGRAPH "NGRAPH"
/** @brief Returns Inference Engine internal backend API.
*
* See values of `CV_DNN_BACKEND_INFERENCE_ENGINE_*` macros.
*
* `OPENCV_DNN_BACKEND_INFERENCE_ENGINE_TYPE` runtime parameter (environment variable) is ignored since 4.6.0.
*
* @deprecated
*/
CV_EXPORTS_W cv::String getInferenceEngineBackendType();
/** @brief Specify Inference Engine internal backend API.
*
* See values of `CV_DNN_BACKEND_INFERENCE_ENGINE_*` macros.
*
* @returns previous value of internal backend API
*
* @deprecated
*/
CV_EXPORTS_W cv::String setInferenceEngineBackendType(const cv::String& newBackendType);
/** @brief Release a Myriad device (binded by OpenCV).
*
* Single Myriad device cannot be shared across multiple processes which uses
* Inference Engine's Myriad plugin.
*/
CV_EXPORTS_W void resetMyriadDevice();
/* Values for 'OPENCV_DNN_IE_VPU_TYPE' parameter */
#define CV_DNN_INFERENCE_ENGINE_VPU_TYPE_UNSPECIFIED ""
/// Intel(R) Movidius(TM) Neural Compute Stick, NCS (USB 03e7:2150), Myriad2 (https://software.intel.com/en-us/movidius-ncs)
#define CV_DNN_INFERENCE_ENGINE_VPU_TYPE_MYRIAD_2 "Myriad2"
/// Intel(R) Neural Compute Stick 2, NCS2 (USB 03e7:2485), MyriadX (https://software.intel.com/ru-ru/neural-compute-stick)
#define CV_DNN_INFERENCE_ENGINE_VPU_TYPE_MYRIAD_X "MyriadX"
#define CV_DNN_INFERENCE_ENGINE_CPU_TYPE_ARM_COMPUTE "ARM_COMPUTE"
#define CV_DNN_INFERENCE_ENGINE_CPU_TYPE_X86 "X86"
/** @brief Returns Inference Engine VPU type.
*
* See values of `CV_DNN_INFERENCE_ENGINE_VPU_TYPE_*` macros.
*/
CV_EXPORTS_W cv::String getInferenceEngineVPUType();
/** @brief Returns Inference Engine CPU type.
*
* Specify OpenVINO plugin: CPU or ARM.
*/
CV_EXPORTS_W cv::String getInferenceEngineCPUType();
/** @brief Release a HDDL plugin.
*/
CV_EXPORTS_W void releaseHDDLPlugin();
CV__DNN_INLINE_NS_END
}} // namespace
#endif // OPENCV_DNN_UTILS_INF_ENGINE_HPP
@@ -0,0 +1,21 @@
// This file is part of OpenCV project.
// It is subject to the license terms in the LICENSE file found in the top-level directory
// of this distribution and at http://opencv.org/license.html.
#ifndef OPENCV_DNN_VERSION_HPP
#define OPENCV_DNN_VERSION_HPP
/// Use with major OpenCV version only.
#define OPENCV_DNN_API_VERSION 20230620
#if !defined CV_DOXYGEN && !defined CV_STATIC_ANALYSIS && !defined CV_DNN_DONT_ADD_INLINE_NS
#define CV__DNN_INLINE_NS __CV_CAT(dnn4_v, OPENCV_DNN_API_VERSION)
#define CV__DNN_INLINE_NS_BEGIN namespace CV__DNN_INLINE_NS {
#define CV__DNN_INLINE_NS_END }
namespace cv { namespace dnn { namespace CV__DNN_INLINE_NS { } using namespace CV__DNN_INLINE_NS; }}
#else
#define CV__DNN_INLINE_NS_BEGIN
#define CV__DNN_INLINE_NS_END
#endif
#endif // OPENCV_DNN_VERSION_HPP
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,48 @@
/*M///////////////////////////////////////////////////////////////////////////////////////
//
// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
//
// By downloading, copying, installing or using the software you agree to this license.
// If you do not agree to this license, do not download, install,
// copy or use the software.
//
//
// License Agreement
// For Open Source Computer Vision Library
//
// Copyright (C) 2000-2008, Intel Corporation, all rights reserved.
// Copyright (C) 2009, Willow Garage Inc., all rights reserved.
// Copyright (C) 2013, OpenCV Foundation, all rights reserved.
// Third party copyrights are property of their respective owners.
//
// Redistribution and use in source and binary forms, with or without modification,
// are permitted provided that the following conditions are met:
//
// * Redistribution's of source code must retain the above copyright notice,
// this list of conditions and the following disclaimer.
//
// * Redistribution's in binary form must reproduce the above copyright notice,
// this list of conditions and the following disclaimer in the documentation
// and/or other materials provided with the distribution.
//
// * The name of the copyright holders may not be used to endorse or promote products
// derived from this software without specific prior written permission.
//
// This software is provided by the copyright holders and contributors "as is" and
// any express or implied warranties, including, but not limited to, the implied
// warranties of merchantability and fitness for a particular purpose are disclaimed.
// In no event shall the Intel Corporation or contributors be liable for any direct,
// indirect, incidental, special, exemplary, or consequential damages
// (including, but not limited to, procurement of substitute goods or services;
// loss of use, data, or profits; or business interruption) however caused
// and on any theory of liability, whether in contract, strict liability,
// or tort (including negligence or otherwise) arising in any way out of
// the use of this software, even if advised of the possibility of such damage.
//
//M*/
#ifdef __OPENCV_BUILD
#error this is a compatibility header which should not be used inside the OpenCV library
#endif
#include "opencv2/features2d.hpp"
@@ -0,0 +1,33 @@
#ifndef OPENCV_FEATURE2D_HAL_INTERFACE_H
#define OPENCV_FEATURE2D_HAL_INTERFACE_H
#include "opencv2/core/cvdef.h"
//! @addtogroup features2d_hal_interface
//! @{
//! @name Fast feature detector types
//! @sa cv::FastFeatureDetector
//! @{
#define CV_HAL_TYPE_5_8 0
#define CV_HAL_TYPE_7_12 1
#define CV_HAL_TYPE_9_16 2
//! @}
//! @name Key point
//! @sa cv::KeyPoint
//! @{
struct CV_EXPORTS cvhalKeyPoint
{
float x;
float y;
float size;
float angle;
float response;
int octave;
int class_id;
};
//! @}
//! @}
#endif
@@ -0,0 +1,629 @@
/*M///////////////////////////////////////////////////////////////////////////////////////
//
// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
//
// By downloading, copying, installing or using the software you agree to this license.
// If you do not agree to this license, do not download, install,
// copy or use the software.
//
//
// License Agreement
// For Open Source Computer Vision Library
//
// Copyright (C) 2000-2008, Intel Corporation, all rights reserved.
// Copyright (C) 2009, Willow Garage Inc., all rights reserved.
// Third party copyrights are property of their respective owners.
//
// Redistribution and use in source and binary forms, with or without modification,
// are permitted provided that the following conditions are met:
//
// * Redistribution's of source code must retain the above copyright notice,
// this list of conditions and the following disclaimer.
//
// * Redistribution's in binary form must reproduce the above copyright notice,
// this list of conditions and the following disclaimer in the documentation
// and/or other materials provided with the distribution.
//
// * The name of the copyright holders may not be used to endorse or promote products
// derived from this software without specific prior written permission.
//
// This software is provided by the copyright holders and contributors "as is" and
// any express or implied warranties, including, but not limited to, the implied
// warranties of merchantability and fitness for a particular purpose are disclaimed.
// In no event shall the Intel Corporation or contributors be liable for any direct,
// indirect, incidental, special, exemplary, or consequential damages
// (including, but not limited to, procurement of substitute goods or services;
// loss of use, data, or profits; or business interruption) however caused
// and on any theory of liability, whether in contract, strict liability,
// or tort (including negligence or otherwise) arising in any way out of
// the use of this software, even if advised of the possibility of such damage.
//
//M*/
#ifndef OPENCV_FLANN_HPP
#define OPENCV_FLANN_HPP
#include "opencv2/core.hpp"
#include "opencv2/flann/miniflann.hpp"
#include "opencv2/flann/flann_base.hpp"
/**
@defgroup flann Clustering and Search in Multi-Dimensional Spaces
This section documents OpenCV's interface to the FLANN library. FLANN (Fast Library for Approximate
Nearest Neighbors) is a library that contains a collection of algorithms optimized for fast nearest
neighbor search in large datasets and for high dimensional features. More information about FLANN
can be found in @cite Muja2009 .
*/
namespace cvflann
{
CV_EXPORTS flann_distance_t flann_distance_type();
CV_DEPRECATED CV_EXPORTS void set_distance_type(flann_distance_t distance_type, int order);
}
namespace cv
{
namespace flann
{
//! @addtogroup flann
//! @{
template <typename T> struct CvType {};
template <> struct CvType<unsigned char> { static int type() { return CV_8U; } };
template <> struct CvType<char> { static int type() { return CV_8S; } };
template <> struct CvType<unsigned short> { static int type() { return CV_16U; } };
template <> struct CvType<short> { static int type() { return CV_16S; } };
template <> struct CvType<int> { static int type() { return CV_32S; } };
template <> struct CvType<float> { static int type() { return CV_32F; } };
template <> struct CvType<double> { static int type() { return CV_64F; } };
// bring the flann parameters into this namespace
using ::cvflann::get_param;
using ::cvflann::print_params;
// bring the flann distances into this namespace
using ::cvflann::L2_Simple;
using ::cvflann::L2;
using ::cvflann::L1;
using ::cvflann::MinkowskiDistance;
using ::cvflann::MaxDistance;
using ::cvflann::HammingLUT;
using ::cvflann::Hamming;
using ::cvflann::Hamming2;
using ::cvflann::DNAmmingLUT;
using ::cvflann::DNAmming2;
using ::cvflann::HistIntersectionDistance;
using ::cvflann::HellingerDistance;
using ::cvflann::ChiSquareDistance;
using ::cvflann::KL_Divergence;
/** @brief The FLANN nearest neighbor index class. This class is templated with the type of elements for which
the index is built.
`Distance` functor specifies the metric to be used to calculate the distance between two points.
There are several `Distance` functors that are readily available:
cv::cvflann::L2_Simple - Squared Euclidean distance functor.
This is the simpler, unrolled version. This is preferable for very low dimensionality data (eg 3D points)
cv::flann::L2 - Squared Euclidean distance functor, optimized version.
cv::flann::L1 - Manhattan distance functor, optimized version.
cv::flann::MinkowskiDistance - The Minkowski distance functor.
This is highly optimised with loop unrolling.
The computation of squared root at the end is omitted for efficiency.
cv::flann::MaxDistance - The max distance functor. It computes the
maximum distance between two vectors. This distance is not a valid kdtree distance, it's not
dimensionwise additive.
cv::flann::HammingLUT - %Hamming distance functor. It counts the bit
differences between two strings using a lookup table implementation.
cv::flann::Hamming - %Hamming distance functor. Population count is
performed using library calls, if available. Lookup table implementation is used as a fallback.
cv::flann::Hamming2 - %Hamming distance functor. Population count is
implemented in 12 arithmetic operations (one of which is multiplication).
cv::flann::DNAmmingLUT - %Adaptation of the Hamming distance functor to DNA comparison.
As the four bases A, C, G, T of the DNA (or A, G, C, U for RNA) can be coded on 2 bits,
it counts the bits pairs differences between two sequences using a lookup table implementation.
cv::flann::DNAmming2 - %Adaptation of the Hamming distance functor to DNA comparison.
Bases differences count are vectorised thanks to arithmetic operations using standard
registers (AVX2 and AVX-512 should come in a near future).
cv::flann::HistIntersectionDistance - The histogram
intersection distance functor.
cv::flann::HellingerDistance - The Hellinger distance functor.
cv::flann::ChiSquareDistance - The chi-square distance functor.
cv::flann::KL_Divergence - The Kullback-Leibler divergence functor.
Although the provided implementations cover a vast range of cases, it is also possible to use
a custom implementation. The distance functor is a class whose `operator()` computes the distance
between two features. If the distance is also a kd-tree compatible distance, it should also provide an
`accum_dist()` method that computes the distance between individual feature dimensions.
In addition to `operator()` and `accum_dist()`, a distance functor should also define the
`ElementType` and the `ResultType` as the types of the elements it operates on and the type of the
result it computes. If a distance functor can be used as a kd-tree distance (meaning that the full
distance between a pair of features can be accumulated from the partial distances between the
individual dimensions) a typedef `is_kdtree_distance` should be present inside the distance functor.
If the distance is not a kd-tree distance, but it's a distance in a vector space (the individual
dimensions of the elements it operates on can be accessed independently) a typedef
`is_vector_space_distance` should be defined inside the functor. If neither typedef is defined, the
distance is assumed to be a metric distance and will only be used with indexes operating on
generic metric distances.
*/
template <typename Distance>
class GenericIndex
{
public:
typedef typename Distance::ElementType ElementType;
typedef typename Distance::ResultType DistanceType;
/** @brief Constructs a nearest neighbor search index for a given dataset.
@param features Matrix of containing the features(points) to index. The size of the matrix is
num_features x feature_dimensionality and the data type of the elements in the matrix must
coincide with the type of the index.
@param params Structure containing the index parameters. The type of index that will be
constructed depends on the type of this parameter. See the description.
@param distance
The method constructs a fast search structure from a set of features using the specified algorithm
with specified parameters, as defined by params. params is a reference to one of the following class
IndexParams descendants:
- **LinearIndexParams** When passing an object of this type, the index will perform a linear,
brute-force search. :
@code
struct LinearIndexParams : public IndexParams
{
};
@endcode
- **KDTreeIndexParams** When passing an object of this type the index constructed will consist of
a set of randomized kd-trees which will be searched in parallel. :
@code
struct KDTreeIndexParams : public IndexParams
{
KDTreeIndexParams( int trees = 4 );
};
@endcode
- **HierarchicalClusteringIndexParams** When passing an object of this type the index constructed
will be a hierarchical tree of clusters, dividing each set of points into n clusters whose centers
are picked among the points without further refinement of their position.
This algorithm fits both floating, integer and binary vectors. :
@code
struct HierarchicalClusteringIndexParams : public IndexParams
{
HierarchicalClusteringIndexParams(
int branching = 32,
flann_centers_init_t centers_init = CENTERS_RANDOM,
int trees = 4,
int leaf_size = 100);
};
@endcode
- **KMeansIndexParams** When passing an object of this type the index constructed will be a
hierarchical k-means tree (one tree by default), dividing each set of points into n clusters
whose barycenters are refined iteratively.
Note that this algorithm has been extended to the support of binary vectors as an alternative
to LSH when knn search speed is the criterium. It will also outperform LSH when processing
directly (i.e. without the use of MCA/PCA) datasets whose points share mostly the same values
for most of the dimensions. It is recommended to set more than one tree with binary data. :
@code
struct KMeansIndexParams : public IndexParams
{
KMeansIndexParams(
int branching = 32,
int iterations = 11,
flann_centers_init_t centers_init = CENTERS_RANDOM,
float cb_index = 0.2,
int trees = 1);
};
@endcode
- **CompositeIndexParams** When using a parameters object of this type the index created
combines the randomized kd-trees and the hierarchical k-means tree. :
@code
struct CompositeIndexParams : public IndexParams
{
CompositeIndexParams(
int trees = 4,
int branching = 32,
int iterations = 11,
flann_centers_init_t centers_init = CENTERS_RANDOM,
float cb_index = 0.2 );
};
@endcode
- **LshIndexParams** When using a parameters object of this type the index created uses
multi-probe LSH (by Multi-Probe LSH: Efficient Indexing for High-Dimensional Similarity Search
by Qin Lv, William Josephson, Zhe Wang, Moses Charikar, Kai Li., Proceedings of the 33rd
International Conference on Very Large Data Bases (VLDB). Vienna, Austria. September 2007).
This algorithm is designed for binary vectors. :
@code
struct LshIndexParams : public IndexParams
{
LshIndexParams(
int table_number,
int key_size,
int multi_probe_level );
};
@endcode
- **AutotunedIndexParams** When passing an object of this type the index created is
automatically tuned to offer the best performance, by choosing the optimal index type
(randomized kd-trees, hierarchical kmeans, linear) and parameters for the dataset provided. :
@code
struct AutotunedIndexParams : public IndexParams
{
AutotunedIndexParams(
float target_precision = 0.9,
float build_weight = 0.01,
float memory_weight = 0,
float sample_fraction = 0.1 );
};
@endcode
- **SavedIndexParams** This object type is used for loading a previously saved index from the
disk. :
@code
struct SavedIndexParams : public IndexParams
{
SavedIndexParams( String filename );
};
@endcode
*/
GenericIndex(const Mat& features, const ::cvflann::IndexParams& params, Distance distance = Distance());
~GenericIndex();
/** @brief Performs a K-nearest neighbor search for a given query point using the index.
@param query The query point
@param indices Vector that will contain the indices of the K-nearest neighbors found. It must have
at least knn size.
@param dists Vector that will contain the distances to the K-nearest neighbors found. It must have
at least knn size.
@param knn Number of nearest neighbors to search for.
@param params SearchParams
*/
void knnSearch(const std::vector<ElementType>& query, std::vector<int>& indices,
std::vector<DistanceType>& dists, int knn, const ::cvflann::SearchParams& params);
void knnSearch(const Mat& queries, Mat& indices, Mat& dists, int knn, const ::cvflann::SearchParams& params);
/** @brief Performs a radius nearest neighbor search for a given query point using the index.
@param query The query point.
@param indices Vector that will contain the indices of the nearest neighbors found.
@param dists Vector that will contain the distances to the nearest neighbors found. It has the same
number of elements as indices.
@param radius The search radius.
@param params SearchParams
This function returns the number of nearest neighbors found.
*/
int radiusSearch(const std::vector<ElementType>& query, std::vector<int>& indices,
std::vector<DistanceType>& dists, DistanceType radius, const ::cvflann::SearchParams& params);
int radiusSearch(const Mat& query, Mat& indices, Mat& dists,
DistanceType radius, const ::cvflann::SearchParams& params);
void save(String filename) { nnIndex->save(filename); }
int veclen() const { return nnIndex->veclen(); }
int size() const { return (int)nnIndex->size(); }
::cvflann::IndexParams getParameters() { return nnIndex->getParameters(); }
CV_DEPRECATED const ::cvflann::IndexParams* getIndexParameters() { return nnIndex->getIndexParameters(); }
private:
::cvflann::Index<Distance>* nnIndex;
Mat _dataset;
};
//! @cond IGNORED
#define FLANN_DISTANCE_CHECK \
if ( ::cvflann::flann_distance_type() != cvflann::FLANN_DIST_L2) { \
printf("[WARNING] You are using cv::flann::Index (or cv::flann::GenericIndex) and have also changed "\
"the distance using cvflann::set_distance_type. This is no longer working as expected "\
"(cv::flann::Index always uses L2). You should create the index templated on the distance, "\
"for example for L1 distance use: GenericIndex< L1<float> > \n"); \
}
template <typename Distance>
GenericIndex<Distance>::GenericIndex(const Mat& dataset, const ::cvflann::IndexParams& params, Distance distance)
: _dataset(dataset)
{
CV_Assert(dataset.type() == CvType<ElementType>::type());
CV_Assert(dataset.isContinuous());
::cvflann::Matrix<ElementType> m_dataset((ElementType*)_dataset.ptr<ElementType>(0), _dataset.rows, _dataset.cols);
nnIndex = new ::cvflann::Index<Distance>(m_dataset, params, distance);
FLANN_DISTANCE_CHECK
nnIndex->buildIndex();
}
template <typename Distance>
GenericIndex<Distance>::~GenericIndex()
{
delete nnIndex;
}
template <typename Distance>
void GenericIndex<Distance>::knnSearch(const std::vector<ElementType>& query, std::vector<int>& indices, std::vector<DistanceType>& dists, int knn, const ::cvflann::SearchParams& searchParams)
{
::cvflann::Matrix<ElementType> m_query((ElementType*)&query[0], 1, query.size());
::cvflann::Matrix<int> m_indices(&indices[0], 1, indices.size());
::cvflann::Matrix<DistanceType> m_dists(&dists[0], 1, dists.size());
FLANN_DISTANCE_CHECK
nnIndex->knnSearch(m_query,m_indices,m_dists,knn,searchParams);
}
template <typename Distance>
void GenericIndex<Distance>::knnSearch(const Mat& queries, Mat& indices, Mat& dists, int knn, const ::cvflann::SearchParams& searchParams)
{
CV_Assert(queries.type() == CvType<ElementType>::type());
CV_Assert(queries.isContinuous());
::cvflann::Matrix<ElementType> m_queries((ElementType*)queries.ptr<ElementType>(0), queries.rows, queries.cols);
CV_Assert(indices.type() == CV_32S);
CV_Assert(indices.isContinuous());
::cvflann::Matrix<int> m_indices((int*)indices.ptr<int>(0), indices.rows, indices.cols);
CV_Assert(dists.type() == CvType<DistanceType>::type());
CV_Assert(dists.isContinuous());
::cvflann::Matrix<DistanceType> m_dists((DistanceType*)dists.ptr<DistanceType>(0), dists.rows, dists.cols);
FLANN_DISTANCE_CHECK
nnIndex->knnSearch(m_queries,m_indices,m_dists,knn, searchParams);
}
template <typename Distance>
int GenericIndex<Distance>::radiusSearch(const std::vector<ElementType>& query, std::vector<int>& indices, std::vector<DistanceType>& dists, DistanceType radius, const ::cvflann::SearchParams& searchParams)
{
::cvflann::Matrix<ElementType> m_query((ElementType*)&query[0], 1, query.size());
::cvflann::Matrix<int> m_indices(&indices[0], 1, indices.size());
::cvflann::Matrix<DistanceType> m_dists(&dists[0], 1, dists.size());
FLANN_DISTANCE_CHECK
return nnIndex->radiusSearch(m_query,m_indices,m_dists,radius,searchParams);
}
template <typename Distance>
int GenericIndex<Distance>::radiusSearch(const Mat& query, Mat& indices, Mat& dists, DistanceType radius, const ::cvflann::SearchParams& searchParams)
{
CV_Assert(query.type() == CvType<ElementType>::type());
CV_Assert(query.isContinuous());
::cvflann::Matrix<ElementType> m_query((ElementType*)query.ptr<ElementType>(0), query.rows, query.cols);
CV_Assert(indices.type() == CV_32S);
CV_Assert(indices.isContinuous());
::cvflann::Matrix<int> m_indices((int*)indices.ptr<int>(0), indices.rows, indices.cols);
CV_Assert(dists.type() == CvType<DistanceType>::type());
CV_Assert(dists.isContinuous());
::cvflann::Matrix<DistanceType> m_dists((DistanceType*)dists.ptr<DistanceType>(0), dists.rows, dists.cols);
FLANN_DISTANCE_CHECK
return nnIndex->radiusSearch(m_query,m_indices,m_dists,radius,searchParams);
}
/**
* @deprecated Use GenericIndex class instead
*/
template <typename T>
class Index_
{
public:
typedef typename L2<T>::ElementType ElementType;
typedef typename L2<T>::ResultType DistanceType;
CV_DEPRECATED Index_(const Mat& dataset, const ::cvflann::IndexParams& params)
{
printf("[WARNING] The cv::flann::Index_<T> class is deperecated, use cv::flann::GenericIndex<Distance> instead\n");
CV_Assert(dataset.type() == CvType<ElementType>::type());
CV_Assert(dataset.isContinuous());
::cvflann::Matrix<ElementType> m_dataset((ElementType*)dataset.ptr<ElementType>(0), dataset.rows, dataset.cols);
if ( ::cvflann::flann_distance_type() == cvflann::FLANN_DIST_L2 ) {
nnIndex_L1 = NULL;
nnIndex_L2 = new ::cvflann::Index< L2<ElementType> >(m_dataset, params);
}
else if ( ::cvflann::flann_distance_type() == cvflann::FLANN_DIST_L1 ) {
nnIndex_L1 = new ::cvflann::Index< L1<ElementType> >(m_dataset, params);
nnIndex_L2 = NULL;
}
else {
printf("[ERROR] cv::flann::Index_<T> only provides backwards compatibility for the L1 and L2 distances. "
"For other distance types you must use cv::flann::GenericIndex<Distance>\n");
CV_Assert(0);
}
if (nnIndex_L1) nnIndex_L1->buildIndex();
if (nnIndex_L2) nnIndex_L2->buildIndex();
}
CV_DEPRECATED ~Index_()
{
if (nnIndex_L1) delete nnIndex_L1;
if (nnIndex_L2) delete nnIndex_L2;
}
CV_DEPRECATED void knnSearch(const std::vector<ElementType>& query, std::vector<int>& indices, std::vector<DistanceType>& dists, int knn, const ::cvflann::SearchParams& searchParams)
{
::cvflann::Matrix<ElementType> m_query((ElementType*)&query[0], 1, query.size());
::cvflann::Matrix<int> m_indices(&indices[0], 1, indices.size());
::cvflann::Matrix<DistanceType> m_dists(&dists[0], 1, dists.size());
if (nnIndex_L1) nnIndex_L1->knnSearch(m_query,m_indices,m_dists,knn,searchParams);
if (nnIndex_L2) nnIndex_L2->knnSearch(m_query,m_indices,m_dists,knn,searchParams);
}
CV_DEPRECATED void knnSearch(const Mat& queries, Mat& indices, Mat& dists, int knn, const ::cvflann::SearchParams& searchParams)
{
CV_Assert(queries.type() == CvType<ElementType>::type());
CV_Assert(queries.isContinuous());
::cvflann::Matrix<ElementType> m_queries((ElementType*)queries.ptr<ElementType>(0), queries.rows, queries.cols);
CV_Assert(indices.type() == CV_32S);
CV_Assert(indices.isContinuous());
::cvflann::Matrix<int> m_indices((int*)indices.ptr<int>(0), indices.rows, indices.cols);
CV_Assert(dists.type() == CvType<DistanceType>::type());
CV_Assert(dists.isContinuous());
::cvflann::Matrix<DistanceType> m_dists((DistanceType*)dists.ptr<DistanceType>(0), dists.rows, dists.cols);
if (nnIndex_L1) nnIndex_L1->knnSearch(m_queries,m_indices,m_dists,knn, searchParams);
if (nnIndex_L2) nnIndex_L2->knnSearch(m_queries,m_indices,m_dists,knn, searchParams);
}
CV_DEPRECATED int radiusSearch(const std::vector<ElementType>& query, std::vector<int>& indices, std::vector<DistanceType>& dists, DistanceType radius, const ::cvflann::SearchParams& searchParams)
{
::cvflann::Matrix<ElementType> m_query((ElementType*)&query[0], 1, query.size());
::cvflann::Matrix<int> m_indices(&indices[0], 1, indices.size());
::cvflann::Matrix<DistanceType> m_dists(&dists[0], 1, dists.size());
if (nnIndex_L1) return nnIndex_L1->radiusSearch(m_query,m_indices,m_dists,radius,searchParams);
if (nnIndex_L2) return nnIndex_L2->radiusSearch(m_query,m_indices,m_dists,radius,searchParams);
}
CV_DEPRECATED int radiusSearch(const Mat& query, Mat& indices, Mat& dists, DistanceType radius, const ::cvflann::SearchParams& searchParams)
{
CV_Assert(query.type() == CvType<ElementType>::type());
CV_Assert(query.isContinuous());
::cvflann::Matrix<ElementType> m_query((ElementType*)query.ptr<ElementType>(0), query.rows, query.cols);
CV_Assert(indices.type() == CV_32S);
CV_Assert(indices.isContinuous());
::cvflann::Matrix<int> m_indices((int*)indices.ptr<int>(0), indices.rows, indices.cols);
CV_Assert(dists.type() == CvType<DistanceType>::type());
CV_Assert(dists.isContinuous());
::cvflann::Matrix<DistanceType> m_dists((DistanceType*)dists.ptr<DistanceType>(0), dists.rows, dists.cols);
if (nnIndex_L1) return nnIndex_L1->radiusSearch(m_query,m_indices,m_dists,radius,searchParams);
if (nnIndex_L2) return nnIndex_L2->radiusSearch(m_query,m_indices,m_dists,radius,searchParams);
}
CV_DEPRECATED void save(String filename)
{
if (nnIndex_L1) nnIndex_L1->save(filename);
if (nnIndex_L2) nnIndex_L2->save(filename);
}
CV_DEPRECATED int veclen() const
{
if (nnIndex_L1) return nnIndex_L1->veclen();
if (nnIndex_L2) return nnIndex_L2->veclen();
}
CV_DEPRECATED int size() const
{
if (nnIndex_L1) return nnIndex_L1->size();
if (nnIndex_L2) return nnIndex_L2->size();
}
CV_DEPRECATED ::cvflann::IndexParams getParameters()
{
if (nnIndex_L1) return nnIndex_L1->getParameters();
if (nnIndex_L2) return nnIndex_L2->getParameters();
}
CV_DEPRECATED const ::cvflann::IndexParams* getIndexParameters()
{
if (nnIndex_L1) return nnIndex_L1->getIndexParameters();
if (nnIndex_L2) return nnIndex_L2->getIndexParameters();
}
private:
// providing backwards compatibility for L2 and L1 distances (most common)
::cvflann::Index< L2<ElementType> >* nnIndex_L2;
::cvflann::Index< L1<ElementType> >* nnIndex_L1;
};
//! @endcond
/** @brief Clusters features using hierarchical k-means algorithm.
@param features The points to be clustered. The matrix must have elements of type
Distance::ElementType.
@param centers The centers of the clusters obtained. The matrix must have type
Distance::CentersType. The number of rows in this matrix represents the number of clusters desired,
however, because of the way the cut in the hierarchical tree is chosen, the number of clusters
computed will be the highest number of the form (branching-1)\*k+1 that's lower than the number of
clusters desired, where branching is the tree's branching factor (see description of the
KMeansIndexParams).
@param params Parameters used in the construction of the hierarchical k-means tree.
@param d Distance to be used for clustering.
The method clusters the given feature vectors by constructing a hierarchical k-means tree and
choosing a cut in the tree that minimizes the cluster's variance. It returns the number of clusters
found.
*/
template <typename Distance>
int hierarchicalClustering(const Mat& features, Mat& centers, const ::cvflann::KMeansIndexParams& params,
Distance d = Distance())
{
typedef typename Distance::ElementType ElementType;
typedef typename Distance::CentersType CentersType;
CV_Assert(features.type() == CvType<ElementType>::type());
CV_Assert(features.isContinuous());
::cvflann::Matrix<ElementType> m_features((ElementType*)features.ptr<ElementType>(0), features.rows, features.cols);
CV_Assert(centers.type() == CvType<CentersType>::type());
CV_Assert(centers.isContinuous());
::cvflann::Matrix<CentersType> m_centers((CentersType*)centers.ptr<CentersType>(0), centers.rows, centers.cols);
return ::cvflann::hierarchicalClustering<Distance>(m_features, m_centers, params, d);
}
//! @cond IGNORED
template <typename ELEM_TYPE, typename DIST_TYPE>
CV_DEPRECATED int hierarchicalClustering(const Mat& features, Mat& centers, const ::cvflann::KMeansIndexParams& params)
{
printf("[WARNING] cv::flann::hierarchicalClustering<ELEM_TYPE,DIST_TYPE> is deprecated, use "
"cv::flann::hierarchicalClustering<Distance> instead\n");
if ( ::cvflann::flann_distance_type() == cvflann::FLANN_DIST_L2 ) {
return hierarchicalClustering< L2<ELEM_TYPE> >(features, centers, params);
}
else if ( ::cvflann::flann_distance_type() == cvflann::FLANN_DIST_L1 ) {
return hierarchicalClustering< L1<ELEM_TYPE> >(features, centers, params);
}
else {
printf("[ERROR] cv::flann::hierarchicalClustering<ELEM_TYPE,DIST_TYPE> only provides backwards "
"compatibility for the L1 and L2 distances. "
"For other distance types you must use cv::flann::hierarchicalClustering<Distance>\n");
CV_Assert(0);
}
}
//! @endcond
//! @} flann
} } // namespace cv::flann
#endif
@@ -0,0 +1,162 @@
/***********************************************************************
* Software License Agreement (BSD License)
*
* Copyright 2008-2009 Marius Muja (mariusm@cs.ubc.ca). All rights reserved.
* Copyright 2008-2009 David G. Lowe (lowe@cs.ubc.ca). All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*************************************************************************/
#ifndef OPENCV_FLANN_ALL_INDICES_H_
#define OPENCV_FLANN_ALL_INDICES_H_
//! @cond IGNORED
#include "general.h"
#include "nn_index.h"
#include "kdtree_index.h"
#include "kdtree_single_index.h"
#include "kmeans_index.h"
#include "composite_index.h"
#include "linear_index.h"
#include "hierarchical_clustering_index.h"
#include "lsh_index.h"
#include "autotuned_index.h"
namespace cvflann
{
template<typename KDTreeCapability, typename VectorSpace, typename Distance>
struct index_creator
{
static NNIndex<Distance>* create(const Matrix<typename Distance::ElementType>& dataset, const IndexParams& params, const Distance& distance)
{
flann_algorithm_t index_type = get_param<flann_algorithm_t>(params, "algorithm");
NNIndex<Distance>* nnIndex;
switch (index_type) {
case FLANN_INDEX_LINEAR:
nnIndex = new LinearIndex<Distance>(dataset, params, distance);
break;
case FLANN_INDEX_KDTREE_SINGLE:
nnIndex = new KDTreeSingleIndex<Distance>(dataset, params, distance);
break;
case FLANN_INDEX_KDTREE:
nnIndex = new KDTreeIndex<Distance>(dataset, params, distance);
break;
case FLANN_INDEX_KMEANS:
nnIndex = new KMeansIndex<Distance>(dataset, params, distance);
break;
case FLANN_INDEX_COMPOSITE:
nnIndex = new CompositeIndex<Distance>(dataset, params, distance);
break;
case FLANN_INDEX_AUTOTUNED:
nnIndex = new AutotunedIndex<Distance>(dataset, params, distance);
break;
case FLANN_INDEX_HIERARCHICAL:
nnIndex = new HierarchicalClusteringIndex<Distance>(dataset, params, distance);
break;
case FLANN_INDEX_LSH:
nnIndex = new LshIndex<Distance>(dataset, params, distance);
break;
default:
FLANN_THROW(cv::Error::StsBadArg, "Unknown index type");
}
return nnIndex;
}
};
template<typename VectorSpace, typename Distance>
struct index_creator<False,VectorSpace,Distance>
{
static NNIndex<Distance>* create(const Matrix<typename Distance::ElementType>& dataset, const IndexParams& params, const Distance& distance)
{
flann_algorithm_t index_type = get_param<flann_algorithm_t>(params, "algorithm");
NNIndex<Distance>* nnIndex;
switch (index_type) {
case FLANN_INDEX_LINEAR:
nnIndex = new LinearIndex<Distance>(dataset, params, distance);
break;
case FLANN_INDEX_KMEANS:
nnIndex = new KMeansIndex<Distance>(dataset, params, distance);
break;
case FLANN_INDEX_HIERARCHICAL:
nnIndex = new HierarchicalClusteringIndex<Distance>(dataset, params, distance);
break;
case FLANN_INDEX_LSH:
nnIndex = new LshIndex<Distance>(dataset, params, distance);
break;
default:
FLANN_THROW(cv::Error::StsBadArg, "Unknown index type");
}
return nnIndex;
}
};
template<typename Distance>
struct index_creator<False,False,Distance>
{
static NNIndex<Distance>* create(const Matrix<typename Distance::ElementType>& dataset, const IndexParams& params, const Distance& distance)
{
flann_algorithm_t index_type = get_param<flann_algorithm_t>(params, "algorithm");
NNIndex<Distance>* nnIndex;
switch (index_type) {
case FLANN_INDEX_LINEAR:
nnIndex = new LinearIndex<Distance>(dataset, params, distance);
break;
case FLANN_INDEX_KMEANS:
nnIndex = new KMeansIndex<Distance>(dataset, params, distance);
break;
case FLANN_INDEX_HIERARCHICAL:
nnIndex = new HierarchicalClusteringIndex<Distance>(dataset, params, distance);
break;
case FLANN_INDEX_LSH:
nnIndex = new LshIndex<Distance>(dataset, params, distance);
break;
default:
FLANN_THROW(cv::Error::StsBadArg, "Unknown index type");
}
return nnIndex;
}
};
template<typename Distance>
NNIndex<Distance>* create_index_by_type(const Matrix<typename Distance::ElementType>& dataset, const IndexParams& params, const Distance& distance)
{
return index_creator<typename Distance::is_kdtree_distance,
typename Distance::is_vector_space_distance,
Distance>::create(dataset, params,distance);
}
}
//! @endcond
#endif /* OPENCV_FLANN_ALL_INDICES_H_ */
@@ -0,0 +1,196 @@
/***********************************************************************
* Software License Agreement (BSD License)
*
* Copyright 2008-2009 Marius Muja (mariusm@cs.ubc.ca). All rights reserved.
* Copyright 2008-2009 David G. Lowe (lowe@cs.ubc.ca). All rights reserved.
*
* THE BSD LICENSE
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*************************************************************************/
#ifndef OPENCV_FLANN_ALLOCATOR_H_
#define OPENCV_FLANN_ALLOCATOR_H_
//! @cond IGNORED
#include <stdlib.h>
#include <stdio.h>
namespace cvflann
{
/**
* Allocates (using C's malloc) a generic type T.
*
* Params:
* count = number of instances to allocate.
* Returns: pointer (of type T*) to memory buffer
*/
template <typename T>
T* allocate(size_t count = 1)
{
T* mem = (T*) ::malloc(sizeof(T)*count);
return mem;
}
/**
* Pooled storage allocator
*
* The following routines allow for the efficient allocation of storage in
* small chunks from a specified pool. Rather than allowing each structure
* to be freed individually, an entire pool of storage is freed at once.
* This method has two advantages over just using malloc() and free(). First,
* it is far more efficient for allocating small objects, as there is
* no overhead for remembering all the information needed to free each
* object or consolidating fragmented memory. Second, the decision about
* how long to keep an object is made at the time of allocation, and there
* is no need to track down all the objects to free them.
*
*/
const size_t WORDSIZE=16;
const size_t BLOCKSIZE=8192;
class PooledAllocator
{
/* We maintain memory alignment to word boundaries by requiring that all
allocations be in multiples of the machine wordsize. */
/* Size of machine word in bytes. Must be power of 2. */
/* Minimum number of bytes requested at a time from the system. Must be multiple of WORDSIZE. */
int remaining; /* Number of bytes left in current block of storage. */
void* base; /* Pointer to base of current block of storage. */
void* loc; /* Current location in block to next allocate memory. */
int blocksize;
public:
int usedMemory;
int wastedMemory;
/**
Default constructor. Initializes a new pool.
*/
PooledAllocator(int blockSize = BLOCKSIZE)
{
blocksize = blockSize;
remaining = 0;
base = NULL;
loc = NULL;
usedMemory = 0;
wastedMemory = 0;
}
/**
* Destructor. Frees all the memory allocated in this pool.
*/
~PooledAllocator()
{
void* prev;
while (base != NULL) {
prev = *((void**) base); /* Get pointer to prev block. */
::free(base);
base = prev;
}
}
/**
* Returns a pointer to a piece of new memory of the given size in bytes
* allocated from the pool.
*/
void* allocateMemory(int size)
{
int blockSize;
/* Round size up to a multiple of wordsize. The following expression
only works for WORDSIZE that is a power of 2, by masking last bits of
incremented size to zero.
*/
size = (size + (WORDSIZE - 1)) & ~(WORDSIZE - 1);
/* Check whether a new block must be allocated. Note that the first word
of a block is reserved for a pointer to the previous block.
*/
if (size > remaining) {
wastedMemory += remaining;
/* Allocate new storage. */
blockSize = (size + sizeof(void*) + (WORDSIZE-1) > BLOCKSIZE) ?
size + sizeof(void*) + (WORDSIZE-1) : BLOCKSIZE;
// use the standard C malloc to allocate memory
void* m = ::malloc(blockSize);
if (!m) {
fprintf(stderr,"Failed to allocate memory.\n");
return NULL;
}
/* Fill first word of new block with pointer to previous block. */
((void**) m)[0] = base;
base = m;
int shift = 0;
//int shift = (WORDSIZE - ( (((size_t)m) + sizeof(void*)) & (WORDSIZE-1))) & (WORDSIZE-1);
remaining = blockSize - sizeof(void*) - shift;
loc = ((char*)m + sizeof(void*) + shift);
}
void* rloc = loc;
loc = (char*)loc + size;
remaining -= size;
usedMemory += size;
return rloc;
}
/**
* Allocates (using this pool) a generic type T.
*
* Params:
* count = number of instances to allocate.
* Returns: pointer (of type T*) to memory buffer
*/
template <typename T>
T* allocate(size_t count = 1)
{
T* mem = (T*) this->allocateMemory((int)(sizeof(T)*count));
return mem;
}
private:
PooledAllocator(const PooledAllocator &); // copy disabled
PooledAllocator& operator=(const PooledAllocator &); // assign disabled
};
}
//! @endcond
#endif //OPENCV_FLANN_ALLOCATOR_H_
@@ -0,0 +1,332 @@
#ifndef OPENCV_FLANN_ANY_H_
#define OPENCV_FLANN_ANY_H_
/*
* (C) Copyright Christopher Diggins 2005-2011
* (C) Copyright Pablo Aguilar 2005
* (C) Copyright Kevlin Henney 2001
*
* Distributed under the Boost Software License, Version 1.0. (See
* accompanying file LICENSE_1_0.txt or copy at
* http://www.boost.org/LICENSE_1_0.txt
*
* Adapted for FLANN by Marius Muja
*/
//! @cond IGNORED
#include "defines.h"
#include <stdexcept>
#include <ostream>
#include <typeinfo>
namespace cvflann
{
namespace anyimpl
{
struct bad_any_cast
{
};
struct empty_any
{
};
inline std::ostream& operator <<(std::ostream& out, const empty_any&)
{
out << "[empty_any]";
return out;
}
struct base_any_policy
{
virtual void static_delete(void** x) = 0;
virtual void copy_from_value(void const* src, void** dest) = 0;
virtual void clone(void* const* src, void** dest) = 0;
virtual void move(void* const* src, void** dest) = 0;
virtual void* get_value(void** src) = 0;
virtual const void* get_value(void* const * src) = 0;
virtual ::size_t get_size() = 0;
virtual const std::type_info& type() = 0;
virtual void print(std::ostream& out, void* const* src) = 0;
virtual ~base_any_policy() {}
};
template<typename T>
struct typed_base_any_policy : base_any_policy
{
virtual ::size_t get_size() CV_OVERRIDE { return sizeof(T); }
virtual const std::type_info& type() CV_OVERRIDE { return typeid(T); }
};
template<typename T>
struct small_any_policy CV_FINAL : typed_base_any_policy<T>
{
virtual void static_delete(void**) CV_OVERRIDE { }
virtual void copy_from_value(void const* src, void** dest) CV_OVERRIDE
{
new (dest) T(* reinterpret_cast<T const*>(src));
}
virtual void clone(void* const* src, void** dest) CV_OVERRIDE { *dest = *src; }
virtual void move(void* const* src, void** dest) CV_OVERRIDE { *dest = *src; }
virtual void* get_value(void** src) CV_OVERRIDE { return reinterpret_cast<void*>(src); }
virtual const void* get_value(void* const * src) CV_OVERRIDE { return reinterpret_cast<const void*>(src); }
virtual void print(std::ostream& out, void* const* src) CV_OVERRIDE { out << *reinterpret_cast<T const*>(src); }
};
template<typename T>
struct big_any_policy CV_FINAL : typed_base_any_policy<T>
{
virtual void static_delete(void** x) CV_OVERRIDE
{
if (* x) delete (* reinterpret_cast<T**>(x));
*x = NULL;
}
virtual void copy_from_value(void const* src, void** dest) CV_OVERRIDE
{
*dest = new T(*reinterpret_cast<T const*>(src));
}
virtual void clone(void* const* src, void** dest) CV_OVERRIDE
{
*dest = new T(**reinterpret_cast<T* const*>(src));
}
virtual void move(void* const* src, void** dest) CV_OVERRIDE
{
(*reinterpret_cast<T**>(dest))->~T();
**reinterpret_cast<T**>(dest) = **reinterpret_cast<T* const*>(src);
}
virtual void* get_value(void** src) CV_OVERRIDE { return *src; }
virtual const void* get_value(void* const * src) CV_OVERRIDE { return *src; }
virtual void print(std::ostream& out, void* const* src) CV_OVERRIDE { out << *reinterpret_cast<T const*>(*src); }
};
template<> inline void big_any_policy<flann_centers_init_t>::print(std::ostream& out, void* const* src)
{
out << int(*reinterpret_cast<flann_centers_init_t const*>(*src));
}
template<> inline void big_any_policy<flann_algorithm_t>::print(std::ostream& out, void* const* src)
{
out << int(*reinterpret_cast<flann_algorithm_t const*>(*src));
}
template<> inline void big_any_policy<cv::String>::print(std::ostream& out, void* const* src)
{
out << (*reinterpret_cast<cv::String const*>(*src)).c_str();
}
template<typename T>
struct choose_policy
{
typedef big_any_policy<T> type;
};
template<typename T>
struct choose_policy<T*>
{
typedef small_any_policy<T*> type;
};
struct any;
/// Choosing the policy for an any type is illegal, but should never happen.
/// This is designed to throw a compiler error.
template<>
struct choose_policy<any>
{
typedef void type;
};
/// Specializations for small types.
#define SMALL_POLICY(TYPE) \
template<> \
struct choose_policy<TYPE> { typedef small_any_policy<TYPE> type; \
}
SMALL_POLICY(signed char);
SMALL_POLICY(unsigned char);
SMALL_POLICY(signed short);
SMALL_POLICY(unsigned short);
SMALL_POLICY(signed int);
SMALL_POLICY(unsigned int);
SMALL_POLICY(signed long);
SMALL_POLICY(unsigned long);
SMALL_POLICY(float);
SMALL_POLICY(bool);
#undef SMALL_POLICY
template <typename T>
class SinglePolicy
{
SinglePolicy();
SinglePolicy(const SinglePolicy& other);
SinglePolicy& operator=(const SinglePolicy& other);
public:
static base_any_policy* get_policy();
};
/// This function will return a different policy for each type.
template <typename T>
inline base_any_policy* SinglePolicy<T>::get_policy()
{
static typename choose_policy<T>::type policy;
return &policy;
}
} // namespace anyimpl
struct any
{
private:
// fields
anyimpl::base_any_policy* policy;
void* object;
public:
/// Initializing constructor.
template <typename T>
any(const T& x)
: policy(anyimpl::SinglePolicy<anyimpl::empty_any>::get_policy()), object(NULL)
{
assign(x);
}
/// Empty constructor.
any()
: policy(anyimpl::SinglePolicy<anyimpl::empty_any>::get_policy()), object(NULL)
{ }
/// Special initializing constructor for string literals.
any(const char* x)
: policy(anyimpl::SinglePolicy<anyimpl::empty_any>::get_policy()), object(NULL)
{
assign(x);
}
/// Copy constructor.
any(const any& x)
: policy(anyimpl::SinglePolicy<anyimpl::empty_any>::get_policy()), object(NULL)
{
assign(x);
}
/// Destructor.
~any()
{
policy->static_delete(&object);
}
/// Assignment function from another any.
any& assign(const any& x)
{
reset();
policy = x.policy;
policy->clone(&x.object, &object);
return *this;
}
/// Assignment function.
template <typename T>
any& assign(const T& x)
{
reset();
policy = anyimpl::SinglePolicy<T>::get_policy();
policy->copy_from_value(&x, &object);
return *this;
}
/// Assignment operator.
template<typename T>
any& operator=(const T& x)
{
return assign(x);
}
/// Assignment operator. Template-based version above doesn't work as expected. We need regular assignment operator here.
any& operator=(const any& x)
{
return assign(x);
}
/// Assignment operator, specialed for literal strings.
/// They have types like const char [6] which don't work as expected.
any& operator=(const char* x)
{
return assign(x);
}
/// Utility functions
any& swap(any& x)
{
std::swap(policy, x.policy);
std::swap(object, x.object);
return *this;
}
/// Cast operator. You can only cast to the original type.
template<typename T>
T& cast()
{
if (policy->type() != typeid(T)) throw anyimpl::bad_any_cast();
T* r = reinterpret_cast<T*>(policy->get_value(&object));
return *r;
}
/// Cast operator. You can only cast to the original type.
template<typename T>
const T& cast() const
{
if (policy->type() != typeid(T)) throw anyimpl::bad_any_cast();
const T* r = reinterpret_cast<const T*>(policy->get_value(&object));
return *r;
}
/// Returns true if the any contains no value.
bool empty() const
{
return policy->type() == typeid(anyimpl::empty_any);
}
/// Frees any allocated memory, and sets the value to NULL.
void reset()
{
policy->static_delete(&object);
policy = anyimpl::SinglePolicy<anyimpl::empty_any>::get_policy();
}
/// Returns true if the two types are the same.
bool compatible(const any& x) const
{
return policy->type() == x.policy->type();
}
/// Returns if the type is compatible with the policy
template<typename T>
bool has_type()
{
return policy->type() == typeid(T);
}
const std::type_info& type() const
{
return policy->type();
}
friend std::ostream& operator <<(std::ostream& out, const any& any_val);
};
inline std::ostream& operator <<(std::ostream& out, const any& any_val)
{
any_val.policy->print(out,&any_val.object);
return out;
}
}
//! @endcond
#endif // OPENCV_FLANN_ANY_H_
@@ -0,0 +1,594 @@
/***********************************************************************
* Software License Agreement (BSD License)
*
* Copyright 2008-2009 Marius Muja (mariusm@cs.ubc.ca). All rights reserved.
* Copyright 2008-2009 David G. Lowe (lowe@cs.ubc.ca). All rights reserved.
*
* THE BSD LICENSE
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*************************************************************************/
#ifndef OPENCV_FLANN_AUTOTUNED_INDEX_H_
#define OPENCV_FLANN_AUTOTUNED_INDEX_H_
//! @cond IGNORED
#include <sstream>
#include "nn_index.h"
#include "ground_truth.h"
#include "index_testing.h"
#include "sampling.h"
#include "kdtree_index.h"
#include "kdtree_single_index.h"
#include "kmeans_index.h"
#include "composite_index.h"
#include "linear_index.h"
#include "logger.h"
namespace cvflann
{
template<typename Distance>
NNIndex<Distance>* create_index_by_type(const Matrix<typename Distance::ElementType>& dataset, const IndexParams& params, const Distance& distance);
struct AutotunedIndexParams : public IndexParams
{
AutotunedIndexParams(float target_precision = 0.8, float build_weight = 0.01, float memory_weight = 0, float sample_fraction = 0.1)
{
(*this)["algorithm"] = FLANN_INDEX_AUTOTUNED;
// precision desired (used for autotuning, -1 otherwise)
(*this)["target_precision"] = target_precision;
// build tree time weighting factor
(*this)["build_weight"] = build_weight;
// index memory weighting factor
(*this)["memory_weight"] = memory_weight;
// what fraction of the dataset to use for autotuning
(*this)["sample_fraction"] = sample_fraction;
}
};
template <typename Distance>
class AutotunedIndex : public NNIndex<Distance>
{
public:
typedef typename Distance::ElementType ElementType;
typedef typename Distance::ResultType DistanceType;
AutotunedIndex(const Matrix<ElementType>& inputData, const IndexParams& params = AutotunedIndexParams(), Distance d = Distance()) :
dataset_(inputData), distance_(d)
{
target_precision_ = get_param(params, "target_precision",0.8f);
build_weight_ = get_param(params,"build_weight", 0.01f);
memory_weight_ = get_param(params, "memory_weight", 0.0f);
sample_fraction_ = get_param(params,"sample_fraction", 0.1f);
bestIndex_ = NULL;
speedup_ = 0;
}
AutotunedIndex(const AutotunedIndex&);
AutotunedIndex& operator=(const AutotunedIndex&);
virtual ~AutotunedIndex()
{
if (bestIndex_ != NULL) {
delete bestIndex_;
bestIndex_ = NULL;
}
}
/**
* Method responsible with building the index.
*/
virtual void buildIndex() CV_OVERRIDE
{
std::ostringstream stream;
bestParams_ = estimateBuildParams();
print_params(bestParams_, stream);
Logger::info("----------------------------------------------------\n");
Logger::info("Autotuned parameters:\n");
Logger::info("%s", stream.str().c_str());
Logger::info("----------------------------------------------------\n");
bestIndex_ = create_index_by_type(dataset_, bestParams_, distance_);
bestIndex_->buildIndex();
speedup_ = estimateSearchParams(bestSearchParams_);
stream.str(std::string());
print_params(bestSearchParams_, stream);
Logger::info("----------------------------------------------------\n");
Logger::info("Search parameters:\n");
Logger::info("%s", stream.str().c_str());
Logger::info("----------------------------------------------------\n");
}
/**
* Saves the index to a stream
*/
virtual void saveIndex(FILE* stream) CV_OVERRIDE
{
save_value(stream, (int)bestIndex_->getType());
bestIndex_->saveIndex(stream);
save_value(stream, get_param<int>(bestSearchParams_, "checks"));
}
/**
* Loads the index from a stream
*/
virtual void loadIndex(FILE* stream) CV_OVERRIDE
{
int index_type;
load_value(stream, index_type);
IndexParams params;
params["algorithm"] = (flann_algorithm_t)index_type;
bestIndex_ = create_index_by_type<Distance>(dataset_, params, distance_);
bestIndex_->loadIndex(stream);
int checks;
load_value(stream, checks);
bestSearchParams_["checks"] = checks;
}
/**
* Method that searches for nearest-neighbors
*/
virtual void findNeighbors(ResultSet<DistanceType>& result, const ElementType* vec, const SearchParams& searchParams) CV_OVERRIDE
{
int checks = get_param<int>(searchParams,"checks",FLANN_CHECKS_AUTOTUNED);
if (checks == FLANN_CHECKS_AUTOTUNED) {
bestIndex_->findNeighbors(result, vec, bestSearchParams_);
}
else {
bestIndex_->findNeighbors(result, vec, searchParams);
}
}
IndexParams getParameters() const CV_OVERRIDE
{
return bestIndex_->getParameters();
}
SearchParams getSearchParameters() const
{
return bestSearchParams_;
}
float getSpeedup() const
{
return speedup_;
}
/**
* Number of features in this index.
*/
virtual size_t size() const CV_OVERRIDE
{
return bestIndex_->size();
}
/**
* The length of each vector in this index.
*/
virtual size_t veclen() const CV_OVERRIDE
{
return bestIndex_->veclen();
}
/**
* The amount of memory (in bytes) this index uses.
*/
virtual int usedMemory() const CV_OVERRIDE
{
return bestIndex_->usedMemory();
}
/**
* Algorithm name
*/
virtual flann_algorithm_t getType() const CV_OVERRIDE
{
return FLANN_INDEX_AUTOTUNED;
}
private:
struct CostData
{
float searchTimeCost;
float buildTimeCost;
float memoryCost;
float totalCost;
IndexParams params;
};
void evaluate_kmeans(CostData& cost)
{
StartStopTimer t;
int checks;
const int nn = 1;
Logger::info("KMeansTree using params: max_iterations=%d, branching=%d\n",
get_param<int>(cost.params,"iterations"),
get_param<int>(cost.params,"branching"));
KMeansIndex<Distance> kmeans(sampledDataset_, cost.params, distance_);
// measure index build time
t.start();
kmeans.buildIndex();
t.stop();
float buildTime = (float)t.value;
// measure search time
float searchTime = test_index_precision(kmeans, sampledDataset_, testDataset_, gt_matches_, target_precision_, checks, distance_, nn);
float datasetMemory = float(sampledDataset_.rows * sampledDataset_.cols * sizeof(float));
cost.memoryCost = (kmeans.usedMemory() + datasetMemory) / datasetMemory;
cost.searchTimeCost = searchTime;
cost.buildTimeCost = buildTime;
Logger::info("KMeansTree buildTime=%g, searchTime=%g, build_weight=%g\n", buildTime, searchTime, build_weight_);
}
void evaluate_kdtree(CostData& cost)
{
StartStopTimer t;
int checks;
const int nn = 1;
Logger::info("KDTree using params: trees=%d\n", get_param<int>(cost.params,"trees"));
KDTreeIndex<Distance> kdtree(sampledDataset_, cost.params, distance_);
t.start();
kdtree.buildIndex();
t.stop();
float buildTime = (float)t.value;
//measure search time
float searchTime = test_index_precision(kdtree, sampledDataset_, testDataset_, gt_matches_, target_precision_, checks, distance_, nn);
float datasetMemory = float(sampledDataset_.rows * sampledDataset_.cols * sizeof(float));
cost.memoryCost = (kdtree.usedMemory() + datasetMemory) / datasetMemory;
cost.searchTimeCost = searchTime;
cost.buildTimeCost = buildTime;
Logger::info("KDTree buildTime=%g, searchTime=%g\n", buildTime, searchTime);
}
// struct KMeansSimpleDownhillFunctor {
//
// Autotune& autotuner;
// KMeansSimpleDownhillFunctor(Autotune& autotuner_) : autotuner(autotuner_) {}
//
// float operator()(int* params) {
//
// float maxFloat = numeric_limits<float>::max();
//
// if (params[0]<2) return maxFloat;
// if (params[1]<0) return maxFloat;
//
// CostData c;
// c.params["algorithm"] = KMEANS;
// c.params["centers-init"] = CENTERS_RANDOM;
// c.params["branching"] = params[0];
// c.params["max-iterations"] = params[1];
//
// autotuner.evaluate_kmeans(c);
//
// return c.timeCost;
//
// }
// };
//
// struct KDTreeSimpleDownhillFunctor {
//
// Autotune& autotuner;
// KDTreeSimpleDownhillFunctor(Autotune& autotuner_) : autotuner(autotuner_) {}
//
// float operator()(int* params) {
// float maxFloat = numeric_limits<float>::max();
//
// if (params[0]<1) return maxFloat;
//
// CostData c;
// c.params["algorithm"] = KDTREE;
// c.params["trees"] = params[0];
//
// autotuner.evaluate_kdtree(c);
//
// return c.timeCost;
//
// }
// };
void optimizeKMeans(std::vector<CostData>& costs)
{
Logger::info("KMEANS, Step 1: Exploring parameter space\n");
// explore kmeans parameters space using combinations of the parameters below
int maxIterations[] = { 1, 5, 10, 15 };
int branchingFactors[] = { 16, 32, 64, 128, 256 };
int kmeansParamSpaceSize = FLANN_ARRAY_LEN(maxIterations) * FLANN_ARRAY_LEN(branchingFactors);
costs.reserve(costs.size() + kmeansParamSpaceSize);
// evaluate kmeans for all parameter combinations
for (size_t i = 0; i < FLANN_ARRAY_LEN(maxIterations); ++i) {
for (size_t j = 0; j < FLANN_ARRAY_LEN(branchingFactors); ++j) {
CostData cost;
cost.params["algorithm"] = FLANN_INDEX_KMEANS;
cost.params["centers_init"] = FLANN_CENTERS_RANDOM;
cost.params["iterations"] = maxIterations[i];
cost.params["branching"] = branchingFactors[j];
evaluate_kmeans(cost);
costs.push_back(cost);
}
}
// Logger::info("KMEANS, Step 2: simplex-downhill optimization\n");
//
// const int n = 2;
// // choose initial simplex points as the best parameters so far
// int kmeansNMPoints[n*(n+1)];
// float kmeansVals[n+1];
// for (int i=0;i<n+1;++i) {
// kmeansNMPoints[i*n] = (int)kmeansCosts[i].params["branching"];
// kmeansNMPoints[i*n+1] = (int)kmeansCosts[i].params["max-iterations"];
// kmeansVals[i] = kmeansCosts[i].timeCost;
// }
// KMeansSimpleDownhillFunctor kmeans_cost_func(*this);
// // run optimization
// optimizeSimplexDownhill(kmeansNMPoints,n,kmeans_cost_func,kmeansVals);
// // store results
// for (int i=0;i<n+1;++i) {
// kmeansCosts[i].params["branching"] = kmeansNMPoints[i*2];
// kmeansCosts[i].params["max-iterations"] = kmeansNMPoints[i*2+1];
// kmeansCosts[i].timeCost = kmeansVals[i];
// }
}
void optimizeKDTree(std::vector<CostData>& costs)
{
Logger::info("KD-TREE, Step 1: Exploring parameter space\n");
// explore kd-tree parameters space using the parameters below
int testTrees[] = { 1, 4, 8, 16, 32 };
// evaluate kdtree for all parameter combinations
for (size_t i = 0; i < FLANN_ARRAY_LEN(testTrees); ++i) {
CostData cost;
cost.params["algorithm"] = FLANN_INDEX_KDTREE;
cost.params["trees"] = testTrees[i];
evaluate_kdtree(cost);
costs.push_back(cost);
}
// Logger::info("KD-TREE, Step 2: simplex-downhill optimization\n");
//
// const int n = 1;
// // choose initial simplex points as the best parameters so far
// int kdtreeNMPoints[n*(n+1)];
// float kdtreeVals[n+1];
// for (int i=0;i<n+1;++i) {
// kdtreeNMPoints[i] = (int)kdtreeCosts[i].params["trees"];
// kdtreeVals[i] = kdtreeCosts[i].timeCost;
// }
// KDTreeSimpleDownhillFunctor kdtree_cost_func(*this);
// // run optimization
// optimizeSimplexDownhill(kdtreeNMPoints,n,kdtree_cost_func,kdtreeVals);
// // store results
// for (int i=0;i<n+1;++i) {
// kdtreeCosts[i].params["trees"] = kdtreeNMPoints[i];
// kdtreeCosts[i].timeCost = kdtreeVals[i];
// }
}
/**
* Chooses the best nearest-neighbor algorithm and estimates the optimal
* parameters to use when building the index (for a given precision).
* Returns a dictionary with the optimal parameters.
*/
IndexParams estimateBuildParams()
{
std::vector<CostData> costs;
int sampleSize = int(sample_fraction_ * dataset_.rows);
int testSampleSize = std::min(sampleSize / 10, 1000);
Logger::info("Entering autotuning, dataset size: %d, sampleSize: %d, testSampleSize: %d, target precision: %g\n", dataset_.rows, sampleSize, testSampleSize, target_precision_);
// For a very small dataset, it makes no sense to build any fancy index, just
// use linear search
if (testSampleSize < 10) {
Logger::info("Choosing linear, dataset too small\n");
return LinearIndexParams();
}
// We use a fraction of the original dataset to speedup the autotune algorithm
sampledDataset_ = random_sample(dataset_, sampleSize);
// We use a cross-validation approach, first we sample a testset from the dataset
testDataset_ = random_sample(sampledDataset_, testSampleSize, true);
// We compute the ground truth using linear search
Logger::info("Computing ground truth... \n");
gt_matches_ = Matrix<int>(new int[testDataset_.rows], testDataset_.rows, 1);
StartStopTimer t;
t.start();
compute_ground_truth<Distance>(sampledDataset_, testDataset_, gt_matches_, 0, distance_);
t.stop();
CostData linear_cost;
linear_cost.searchTimeCost = (float)t.value;
linear_cost.buildTimeCost = 0;
linear_cost.memoryCost = 0;
linear_cost.params["algorithm"] = FLANN_INDEX_LINEAR;
costs.push_back(linear_cost);
// Start parameter autotune process
Logger::info("Autotuning parameters...\n");
optimizeKMeans(costs);
optimizeKDTree(costs);
float bestTimeCost = costs[0].searchTimeCost;
for (size_t i = 0; i < costs.size(); ++i) {
float timeCost = costs[i].buildTimeCost * build_weight_ + costs[i].searchTimeCost;
if (timeCost < bestTimeCost) {
bestTimeCost = timeCost;
}
}
float bestCost = costs[0].searchTimeCost / bestTimeCost;
IndexParams bestParams = costs[0].params;
if (bestTimeCost > 0) {
for (size_t i = 0; i < costs.size(); ++i) {
float crtCost = (costs[i].buildTimeCost * build_weight_ + costs[i].searchTimeCost) / bestTimeCost +
memory_weight_ * costs[i].memoryCost;
if (crtCost < bestCost) {
bestCost = crtCost;
bestParams = costs[i].params;
}
}
}
delete[] gt_matches_.data;
delete[] testDataset_.data;
delete[] sampledDataset_.data;
return bestParams;
}
/**
* Estimates the search time parameters needed to get the desired precision.
* Precondition: the index is built
* Postcondition: the searchParams will have the optimum params set, also the speedup obtained over linear search.
*/
float estimateSearchParams(SearchParams& searchParams)
{
const int nn = 1;
const size_t SAMPLE_COUNT = 1000;
CV_Assert(bestIndex_ != NULL && "Requires a valid index"); // must have a valid index
float speedup = 0;
int samples = (int)std::min(dataset_.rows / 10, SAMPLE_COUNT);
if (samples > 0) {
Matrix<ElementType> testDataset = random_sample(dataset_, samples);
Logger::info("Computing ground truth\n");
// we need to compute the ground truth first
Matrix<int> gt_matches(new int[testDataset.rows], testDataset.rows, 1);
StartStopTimer t;
t.start();
compute_ground_truth<Distance>(dataset_, testDataset, gt_matches, 1, distance_);
t.stop();
float linear = (float)t.value;
int checks;
Logger::info("Estimating number of checks\n");
float searchTime;
float cb_index;
if (bestIndex_->getType() == FLANN_INDEX_KMEANS) {
Logger::info("KMeans algorithm, estimating cluster border factor\n");
KMeansIndex<Distance>* kmeans = (KMeansIndex<Distance>*)bestIndex_;
float bestSearchTime = -1;
float best_cb_index = -1;
int best_checks = -1;
for (cb_index = 0; cb_index < 1.1f; cb_index += 0.2f) {
kmeans->set_cb_index(cb_index);
searchTime = test_index_precision(*kmeans, dataset_, testDataset, gt_matches, target_precision_, checks, distance_, nn, 1);
if ((searchTime < bestSearchTime) || (bestSearchTime == -1)) {
bestSearchTime = searchTime;
best_cb_index = cb_index;
best_checks = checks;
}
}
searchTime = bestSearchTime;
cb_index = best_cb_index;
checks = best_checks;
kmeans->set_cb_index(best_cb_index);
Logger::info("Optimum cb_index: %g\n", cb_index);
bestParams_["cb_index"] = cb_index;
}
else {
searchTime = test_index_precision(*bestIndex_, dataset_, testDataset, gt_matches, target_precision_, checks, distance_, nn, 1);
}
Logger::info("Required number of checks: %d \n", checks);
searchParams["checks"] = checks;
speedup = linear / searchTime;
delete[] gt_matches.data;
delete[] testDataset.data;
}
return speedup;
}
private:
NNIndex<Distance>* bestIndex_;
IndexParams bestParams_;
SearchParams bestSearchParams_;
Matrix<ElementType> sampledDataset_;
Matrix<ElementType> testDataset_;
Matrix<int> gt_matches_;
float speedup_;
/**
* The dataset used by this index
*/
const Matrix<ElementType> dataset_;
/**
* Index parameters
*/
float target_precision_;
float build_weight_;
float memory_weight_;
float sample_fraction_;
Distance distance_;
};
}
//! @endcond
#endif /* OPENCV_FLANN_AUTOTUNED_INDEX_H_ */
@@ -0,0 +1,197 @@
/***********************************************************************
* Software License Agreement (BSD License)
*
* Copyright 2008-2009 Marius Muja (mariusm@cs.ubc.ca). All rights reserved.
* Copyright 2008-2009 David G. Lowe (lowe@cs.ubc.ca). All rights reserved.
*
* THE BSD LICENSE
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*************************************************************************/
#ifndef OPENCV_FLANN_COMPOSITE_INDEX_H_
#define OPENCV_FLANN_COMPOSITE_INDEX_H_
//! @cond IGNORED
#include "nn_index.h"
#include "kdtree_index.h"
#include "kmeans_index.h"
namespace cvflann
{
/**
* Index parameters for the CompositeIndex.
*/
struct CompositeIndexParams : public IndexParams
{
CompositeIndexParams(int trees = 4, int branching = 32, int iterations = 11,
flann_centers_init_t centers_init = FLANN_CENTERS_RANDOM, float cb_index = 0.2 )
{
(*this)["algorithm"] = FLANN_INDEX_KMEANS;
// number of randomized trees to use (for kdtree)
(*this)["trees"] = trees;
// branching factor
(*this)["branching"] = branching;
// max iterations to perform in one kmeans clustering (kmeans tree)
(*this)["iterations"] = iterations;
// algorithm used for picking the initial cluster centers for kmeans tree
(*this)["centers_init"] = centers_init;
// cluster boundary index. Used when searching the kmeans tree
(*this)["cb_index"] = cb_index;
}
};
/**
* This index builds a kd-tree index and a k-means index and performs nearest
* neighbour search both indexes. This gives a slight boost in search performance
* as some of the neighbours that are missed by one index are found by the other.
*/
template <typename Distance>
class CompositeIndex : public NNIndex<Distance>
{
public:
typedef typename Distance::ElementType ElementType;
typedef typename Distance::ResultType DistanceType;
/**
* Index constructor
* @param inputData dataset containing the points to index
* @param params Index parameters
* @param d Distance functor
* @return
*/
CompositeIndex(const Matrix<ElementType>& inputData, const IndexParams& params = CompositeIndexParams(),
Distance d = Distance()) : index_params_(params)
{
kdtree_index_ = new KDTreeIndex<Distance>(inputData, params, d);
kmeans_index_ = new KMeansIndex<Distance>(inputData, params, d);
}
CompositeIndex(const CompositeIndex&);
CompositeIndex& operator=(const CompositeIndex&);
virtual ~CompositeIndex()
{
delete kdtree_index_;
delete kmeans_index_;
}
/**
* @return The index type
*/
flann_algorithm_t getType() const CV_OVERRIDE
{
return FLANN_INDEX_COMPOSITE;
}
/**
* @return Size of the index
*/
size_t size() const CV_OVERRIDE
{
return kdtree_index_->size();
}
/**
* \returns The dimensionality of the features in this index.
*/
size_t veclen() const CV_OVERRIDE
{
return kdtree_index_->veclen();
}
/**
* \returns The amount of memory (in bytes) used by the index.
*/
int usedMemory() const CV_OVERRIDE
{
return kmeans_index_->usedMemory() + kdtree_index_->usedMemory();
}
/**
* \brief Builds the index
*/
void buildIndex() CV_OVERRIDE
{
Logger::info("Building kmeans tree...\n");
kmeans_index_->buildIndex();
Logger::info("Building kdtree tree...\n");
kdtree_index_->buildIndex();
}
/**
* \brief Saves the index to a stream
* \param stream The stream to save the index to
*/
void saveIndex(FILE* stream) CV_OVERRIDE
{
kmeans_index_->saveIndex(stream);
kdtree_index_->saveIndex(stream);
}
/**
* \brief Loads the index from a stream
* \param stream The stream from which the index is loaded
*/
void loadIndex(FILE* stream) CV_OVERRIDE
{
kmeans_index_->loadIndex(stream);
kdtree_index_->loadIndex(stream);
}
/**
* \returns The index parameters
*/
IndexParams getParameters() const CV_OVERRIDE
{
return index_params_;
}
/**
* \brief Method that searches for nearest-neighbours
*/
void findNeighbors(ResultSet<DistanceType>& result, const ElementType* vec, const SearchParams& searchParams) CV_OVERRIDE
{
kmeans_index_->findNeighbors(result, vec, searchParams);
kdtree_index_->findNeighbors(result, vec, searchParams);
}
private:
/** The k-means index */
KMeansIndex<Distance>* kmeans_index_;
/** The kd-tree index */
KDTreeIndex<Distance>* kdtree_index_;
/** The index parameters */
const IndexParams index_params_;
};
}
//! @endcond
#endif //OPENCV_FLANN_COMPOSITE_INDEX_H_
@@ -0,0 +1,42 @@
/***********************************************************************
* Software License Agreement (BSD License)
*
* Copyright 2008-2011 Marius Muja (mariusm@cs.ubc.ca). All rights reserved.
* Copyright 2008-2011 David G. Lowe (lowe@cs.ubc.ca). All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*************************************************************************/
#ifndef OPENCV_FLANN_CONFIG_H_
#define OPENCV_FLANN_CONFIG_H_
//! @cond IGNORED
#ifdef FLANN_VERSION_
#undef FLANN_VERSION_
#endif
#define FLANN_VERSION_ "1.6.10"
//! @endcond
#endif /* OPENCV_FLANN_CONFIG_H_ */
@@ -0,0 +1,169 @@
/***********************************************************************
* Software License Agreement (BSD License)
*
* Copyright 2008-2011 Marius Muja (mariusm@cs.ubc.ca). All rights reserved.
* Copyright 2008-2011 David G. Lowe (lowe@cs.ubc.ca). All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*************************************************************************/
#ifndef OPENCV_FLANN_DEFINES_H_
#define OPENCV_FLANN_DEFINES_H_
//! @cond IGNORED
#include "config.h"
#ifdef FLANN_EXPORT
#undef FLANN_EXPORT
#endif
#ifdef _WIN32
/* win32 dll export/import directives */
#ifdef FLANN_EXPORTS
#define FLANN_EXPORT __declspec(dllexport)
#elif defined(FLANN_STATIC)
#define FLANN_EXPORT
#else
#define FLANN_EXPORT __declspec(dllimport)
#endif
#else
/* unix needs nothing */
#define FLANN_EXPORT
#endif
#undef FLANN_PLATFORM_32_BIT
#undef FLANN_PLATFORM_64_BIT
#if defined __amd64__ || defined __x86_64__ || defined _WIN64 || defined _M_X64
#define FLANN_PLATFORM_64_BIT
#else
#define FLANN_PLATFORM_32_BIT
#endif
#undef FLANN_ARRAY_LEN
#define FLANN_ARRAY_LEN(a) (sizeof(a)/sizeof(a[0]))
namespace cvflann {
/* Nearest neighbour index algorithms */
enum flann_algorithm_t
{
FLANN_INDEX_LINEAR = 0,
FLANN_INDEX_KDTREE = 1,
FLANN_INDEX_KMEANS = 2,
FLANN_INDEX_COMPOSITE = 3,
FLANN_INDEX_KDTREE_SINGLE = 4,
FLANN_INDEX_HIERARCHICAL = 5,
FLANN_INDEX_LSH = 6,
FLANN_INDEX_SAVED = 254,
FLANN_INDEX_AUTOTUNED = 255,
// deprecated constants, should use the FLANN_INDEX_* ones instead
LINEAR = 0,
KDTREE = 1,
KMEANS = 2,
COMPOSITE = 3,
KDTREE_SINGLE = 4,
SAVED = 254,
AUTOTUNED = 255
};
enum flann_centers_init_t
{
FLANN_CENTERS_RANDOM = 0,
FLANN_CENTERS_GONZALES = 1,
FLANN_CENTERS_KMEANSPP = 2,
FLANN_CENTERS_GROUPWISE = 3,
// deprecated constants, should use the FLANN_CENTERS_* ones instead
CENTERS_RANDOM = 0,
CENTERS_GONZALES = 1,
CENTERS_KMEANSPP = 2
};
enum flann_log_level_t
{
FLANN_LOG_NONE = 0,
FLANN_LOG_FATAL = 1,
FLANN_LOG_ERROR = 2,
FLANN_LOG_WARN = 3,
FLANN_LOG_INFO = 4
};
enum flann_distance_t
{
FLANN_DIST_EUCLIDEAN = 1,
FLANN_DIST_L2 = 1,
FLANN_DIST_MANHATTAN = 2,
FLANN_DIST_L1 = 2,
FLANN_DIST_MINKOWSKI = 3,
FLANN_DIST_MAX = 4,
FLANN_DIST_HIST_INTERSECT = 5,
FLANN_DIST_HELLINGER = 6,
FLANN_DIST_CHI_SQUARE = 7,
FLANN_DIST_CS = 7,
FLANN_DIST_KULLBACK_LEIBLER = 8,
FLANN_DIST_KL = 8,
FLANN_DIST_HAMMING = 9,
FLANN_DIST_DNAMMING = 10,
// deprecated constants, should use the FLANN_DIST_* ones instead
EUCLIDEAN = 1,
MANHATTAN = 2,
MINKOWSKI = 3,
MAX_DIST = 4,
HIST_INTERSECT = 5,
HELLINGER = 6,
CS = 7,
KL = 8,
KULLBACK_LEIBLER = 8
};
enum flann_datatype_t
{
FLANN_INT8 = 0,
FLANN_INT16 = 1,
FLANN_INT32 = 2,
FLANN_INT64 = 3,
FLANN_UINT8 = 4,
FLANN_UINT16 = 5,
FLANN_UINT32 = 6,
FLANN_UINT64 = 7,
FLANN_FLOAT32 = 8,
FLANN_FLOAT64 = 9
};
enum
{
FLANN_CHECKS_UNLIMITED = -1,
FLANN_CHECKS_AUTOTUNED = -2
};
}
//! @endcond
#endif /* OPENCV_FLANN_DEFINES_H_ */
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,16 @@
#ifndef OPENCV_FLANN_DUMMY_H_
#define OPENCV_FLANN_DUMMY_H_
//! @cond IGNORED
namespace cvflann
{
CV_DEPRECATED inline void dummyfunc() {}
}
//! @endcond
#endif /* OPENCV_FLANN_DUMMY_H_ */
@@ -0,0 +1,163 @@
/***********************************************************************
* Software License Agreement (BSD License)
*
* Copyright 2008-2009 Marius Muja (mariusm@cs.ubc.ca). All rights reserved.
* Copyright 2008-2009 David G. Lowe (lowe@cs.ubc.ca). All rights reserved.
*
* THE BSD LICENSE
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*************************************************************************/
/***********************************************************************
* Author: Vincent Rabaud
*************************************************************************/
#ifndef OPENCV_FLANN_DYNAMIC_BITSET_H_
#define OPENCV_FLANN_DYNAMIC_BITSET_H_
//! @cond IGNORED
#ifndef FLANN_USE_BOOST
# define FLANN_USE_BOOST 0
#endif
//#define FLANN_USE_BOOST 1
#if FLANN_USE_BOOST
#include <boost/dynamic_bitset.hpp>
typedef boost::dynamic_bitset<> DynamicBitset;
#else
#include <limits.h>
#include "dist.h"
namespace cvflann {
/** Class re-implementing the boost version of it
* This helps not depending on boost, it also does not do the bound checks
* and has a way to reset a block for speed
*/
class DynamicBitset
{
public:
/** default constructor
*/
DynamicBitset() : size_(0)
{
}
/** only constructor we use in our code
* @param sz the size of the bitset (in bits)
*/
DynamicBitset(size_t sz)
{
resize(sz);
reset();
}
/** Sets all the bits to 0
*/
void clear()
{
std::fill(bitset_.begin(), bitset_.end(), 0);
}
/** @brief checks if the bitset is empty
* @return true if the bitset is empty
*/
bool empty() const
{
return bitset_.empty();
}
/** set all the bits to 0
*/
void reset()
{
std::fill(bitset_.begin(), bitset_.end(), 0);
}
/** @brief set one bit to 0
* @param index
*/
void reset(size_t index)
{
bitset_[index / cell_bit_size_] &= ~(size_t(1) << (index % cell_bit_size_));
}
/** @brief sets a specific bit to 0, and more bits too
* This function is useful when resetting a given set of bits so that the
* whole bitset ends up being 0: if that's the case, we don't care about setting
* other bits to 0
* @param index
*/
void reset_block(size_t index)
{
bitset_[index / cell_bit_size_] = 0;
}
/** resize the bitset so that it contains at least sz bits
* @param sz
*/
void resize(size_t sz)
{
size_ = sz;
bitset_.resize(sz / cell_bit_size_ + 1);
}
/** set a bit to true
* @param index the index of the bit to set to 1
*/
void set(size_t index)
{
bitset_[index / cell_bit_size_] |= size_t(1) << (index % cell_bit_size_);
}
/** gives the number of contained bits
*/
size_t size() const
{
return size_;
}
/** check if a bit is set
* @param index the index of the bit to check
* @return true if the bit is set
*/
bool test(size_t index) const
{
return (bitset_[index / cell_bit_size_] & (size_t(1) << (index % cell_bit_size_))) != 0;
}
private:
std::vector<size_t> bitset_;
size_t size_;
static const unsigned int cell_bit_size_ = CHAR_BIT * sizeof(size_t);
};
} // namespace cvflann
#endif
//! @endcond
#endif // OPENCV_FLANN_DYNAMIC_BITSET_H_
@@ -0,0 +1,48 @@
/*M///////////////////////////////////////////////////////////////////////////////////////
//
// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
//
// By downloading, copying, installing or using the software you agree to this license.
// If you do not agree to this license, do not download, install,
// copy or use the software.
//
//
// License Agreement
// For Open Source Computer Vision Library
//
// Copyright (C) 2000-2008, Intel Corporation, all rights reserved.
// Copyright (C) 2009, Willow Garage Inc., all rights reserved.
// Copyright (C) 2013, OpenCV Foundation, all rights reserved.
// Third party copyrights are property of their respective owners.
//
// Redistribution and use in source and binary forms, with or without modification,
// are permitted provided that the following conditions are met:
//
// * Redistribution's of source code must retain the above copyright notice,
// this list of conditions and the following disclaimer.
//
// * Redistribution's in binary form must reproduce the above copyright notice,
// this list of conditions and the following disclaimer in the documentation
// and/or other materials provided with the distribution.
//
// * The name of the copyright holders may not be used to endorse or promote products
// derived from this software without specific prior written permission.
//
// This software is provided by the copyright holders and contributors "as is" and
// any express or implied warranties, including, but not limited to, the implied
// warranties of merchantability and fitness for a particular purpose are disclaimed.
// In no event shall the Intel Corporation or contributors be liable for any direct,
// indirect, incidental, special, exemplary, or consequential damages
// (including, but not limited to, procurement of substitute goods or services;
// loss of use, data, or profits; or business interruption) however caused
// and on any theory of liability, whether in contract, strict liability,
// or tort (including negligence or otherwise) arising in any way out of
// the use of this software, even if advised of the possibility of such damage.
//
//M*/
#ifdef __OPENCV_BUILD
#error this is a compatibility header which should not be used inside the OpenCV library
#endif
#include "opencv2/flann.hpp"
@@ -0,0 +1,312 @@
/***********************************************************************
* Software License Agreement (BSD License)
*
* Copyright 2008-2009 Marius Muja (mariusm@cs.ubc.ca). All rights reserved.
* Copyright 2008-2009 David G. Lowe (lowe@cs.ubc.ca). All rights reserved.
*
* THE BSD LICENSE
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*************************************************************************/
#ifndef OPENCV_FLANN_BASE_HPP_
#define OPENCV_FLANN_BASE_HPP_
//! @cond IGNORED
#include <vector>
#include <cstdio>
#include "general.h"
#include "matrix.h"
#include "params.h"
#include "saving.h"
#include "all_indices.h"
namespace cvflann
{
class FILEScopeGuard {
public:
explicit FILEScopeGuard(FILE* file) {
file_ = file;
};
~FILEScopeGuard() {
fclose(file_);
};
private:
FILE* file_;
};
/**
* Sets the log level used for all flann functions
* @param level Verbosity level
*/
inline void log_verbosity(int level)
{
if (level >= 0) {
Logger::setLevel(level);
}
}
/**
* (Deprecated) Index parameters for creating a saved index.
*/
struct SavedIndexParams : public IndexParams
{
SavedIndexParams(cv::String filename)
{
(* this)["algorithm"] = FLANN_INDEX_SAVED;
(*this)["filename"] = filename;
}
};
template<typename Distance>
NNIndex<Distance>* load_saved_index(const Matrix<typename Distance::ElementType>& dataset, const cv::String& filename, Distance distance)
{
typedef typename Distance::ElementType ElementType;
FILE* fin = fopen(filename.c_str(), "rb");
if (fin == NULL) {
return NULL;
}
FILEScopeGuard fscgd(fin);
IndexHeader header = load_header(fin);
if (header.data_type != Datatype<ElementType>::type()) {
FLANN_THROW(cv::Error::StsError, "Datatype of saved index is different than of the one to be created.");
}
if ((size_t(header.rows) != dataset.rows)||(size_t(header.cols) != dataset.cols)) {
FLANN_THROW(cv::Error::StsError, "The index saved belongs to a different dataset");
}
IndexParams params;
params["algorithm"] = header.index_type;
NNIndex<Distance>* nnIndex = create_index_by_type<Distance>(dataset, params, distance);
nnIndex->loadIndex(fin);
return nnIndex;
}
template<typename Distance>
class Index : public NNIndex<Distance>
{
public:
typedef typename Distance::ElementType ElementType;
typedef typename Distance::ResultType DistanceType;
Index(const Matrix<ElementType>& features, const IndexParams& params, Distance distance = Distance() )
:index_params_(params)
{
flann_algorithm_t index_type = get_param<flann_algorithm_t>(params,"algorithm");
loaded_ = false;
if (index_type == FLANN_INDEX_SAVED) {
nnIndex_ = load_saved_index<Distance>(features, get_param<cv::String>(params,"filename"), distance);
loaded_ = true;
}
else {
nnIndex_ = create_index_by_type<Distance>(features, params, distance);
}
}
~Index()
{
delete nnIndex_;
}
/**
* Builds the index.
*/
void buildIndex() CV_OVERRIDE
{
if (!loaded_) {
nnIndex_->buildIndex();
}
}
void save(cv::String filename)
{
FILE* fout = fopen(filename.c_str(), "wb");
if (fout == NULL) {
FLANN_THROW(cv::Error::StsError, "Cannot open file");
}
save_header(fout, *nnIndex_);
saveIndex(fout);
fclose(fout);
}
/**
* \brief Saves the index to a stream
* \param stream The stream to save the index to
*/
virtual void saveIndex(FILE* stream) CV_OVERRIDE
{
nnIndex_->saveIndex(stream);
}
/**
* \brief Loads the index from a stream
* \param stream The stream from which the index is loaded
*/
virtual void loadIndex(FILE* stream) CV_OVERRIDE
{
nnIndex_->loadIndex(stream);
}
/**
* \returns number of features in this index.
*/
size_t veclen() const CV_OVERRIDE
{
return nnIndex_->veclen();
}
/**
* \returns The dimensionality of the features in this index.
*/
size_t size() const CV_OVERRIDE
{
return nnIndex_->size();
}
/**
* \returns The index type (kdtree, kmeans,...)
*/
flann_algorithm_t getType() const CV_OVERRIDE
{
return nnIndex_->getType();
}
/**
* \returns The amount of memory (in bytes) used by the index.
*/
virtual int usedMemory() const CV_OVERRIDE
{
return nnIndex_->usedMemory();
}
/**
* \returns The index parameters
*/
IndexParams getParameters() const CV_OVERRIDE
{
return nnIndex_->getParameters();
}
/**
* \brief Perform k-nearest neighbor search
* \param[in] queries The query points for which to find the nearest neighbors
* \param[out] indices The indices of the nearest neighbors found
* \param[out] dists Distances to the nearest neighbors found
* \param[in] knn Number of nearest neighbors to return
* \param[in] params Search parameters
*/
void knnSearch(const Matrix<ElementType>& queries, Matrix<int>& indices, Matrix<DistanceType>& dists, int knn, const SearchParams& params) CV_OVERRIDE
{
nnIndex_->knnSearch(queries, indices, dists, knn, params);
}
/**
* \brief Perform radius search
* \param[in] query The query point
* \param[out] indices The indinces of the neighbors found within the given radius
* \param[out] dists The distances to the nearest neighbors found
* \param[in] radius The radius used for search
* \param[in] params Search parameters
* \returns Number of neighbors found
*/
int radiusSearch(const Matrix<ElementType>& query, Matrix<int>& indices, Matrix<DistanceType>& dists, float radius, const SearchParams& params) CV_OVERRIDE
{
return nnIndex_->radiusSearch(query, indices, dists, radius, params);
}
/**
* \brief Method that searches for nearest-neighbours
*/
void findNeighbors(ResultSet<DistanceType>& result, const ElementType* vec, const SearchParams& searchParams) CV_OVERRIDE
{
nnIndex_->findNeighbors(result, vec, searchParams);
}
/**
* \brief Returns actual index
*/
CV_DEPRECATED NNIndex<Distance>* getIndex()
{
return nnIndex_;
}
/**
* \brief Returns index parameters.
* \deprecated use getParameters() instead.
*/
CV_DEPRECATED const IndexParams* getIndexParameters()
{
return &index_params_;
}
private:
/** Pointer to actual index class */
NNIndex<Distance>* nnIndex_;
/** Indices if the index was loaded from a file */
bool loaded_;
/** Parameters passed to the index */
IndexParams index_params_;
Index(const Index &); // copy disabled
Index& operator=(const Index &); // assign disabled
};
/**
* Performs a hierarchical clustering of the points passed as argument and then takes a cut in the
* the clustering tree to return a flat clustering.
* @param[in] points Points to be clustered
* @param centers The computed cluster centres. Matrix should be preallocated and centers.rows is the
* number of clusters requested.
* @param params Clustering parameters (The same as for cvflann::KMeansIndex)
* @param d Distance to be used for clustering (eg: cvflann::L2)
* @return number of clusters computed (can be different than clusters.rows and is the highest number
* of the form (branching-1)*K+1 smaller than clusters.rows).
*/
template <typename Distance>
int hierarchicalClustering(const Matrix<typename Distance::ElementType>& points, Matrix<typename Distance::CentersType>& centers,
const KMeansIndexParams& params, Distance d = Distance())
{
KMeansIndex<Distance> kmeans(points, params, d);
kmeans.buildIndex();
int clusterNum = kmeans.getClusterCenters(centers);
return clusterNum;
}
}
//! @endcond
#endif /* OPENCV_FLANN_BASE_HPP_ */
@@ -0,0 +1,63 @@
/***********************************************************************
* Software License Agreement (BSD License)
*
* Copyright 2008-2009 Marius Muja (mariusm@cs.ubc.ca). All rights reserved.
* Copyright 2008-2009 David G. Lowe (lowe@cs.ubc.ca). All rights reserved.
*
* THE BSD LICENSE
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*************************************************************************/
#ifndef OPENCV_FLANN_GENERAL_H_
#define OPENCV_FLANN_GENERAL_H_
#if CV_VERSION_MAJOR <= 4
//! @cond IGNORED
#include "opencv2/core.hpp"
namespace cvflann
{
class FLANNException : public cv::Exception
{
public:
FLANNException(const char* message) : cv::Exception(0, message, "", __FILE__, __LINE__) { }
FLANNException(const cv::String& message) : cv::Exception(0, message, "", __FILE__, __LINE__) { }
};
}
#define FLANN_THROW(TYPE, STR) throw FLANNException(STR)
#else
#define FLANN_THROW(TYPE, STR) CV_Error(TYPE, STR)
#endif
//! @endcond
#endif /* OPENCV_FLANN_GENERAL_H_ */
@@ -0,0 +1,98 @@
/***********************************************************************
* Software License Agreement (BSD License)
*
* Copyright 2008-2009 Marius Muja (mariusm@cs.ubc.ca). All rights reserved.
* Copyright 2008-2009 David G. Lowe (lowe@cs.ubc.ca). All rights reserved.
*
* THE BSD LICENSE
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*************************************************************************/
#ifndef OPENCV_FLANN_GROUND_TRUTH_H_
#define OPENCV_FLANN_GROUND_TRUTH_H_
//! @cond IGNORED
#include "dist.h"
#include "matrix.h"
namespace cvflann
{
template <typename Distance>
void find_nearest(const Matrix<typename Distance::ElementType>& dataset, typename Distance::ElementType* query, int* matches, int nn,
int skip = 0, Distance distance = Distance())
{
typedef typename Distance::ResultType DistanceType;
int n = nn + skip;
std::vector<int> match(n);
std::vector<DistanceType> dists(n);
dists[0] = distance(dataset[0], query, dataset.cols);
match[0] = 0;
int dcnt = 1;
for (size_t i=1; i<dataset.rows; ++i) {
DistanceType tmp = distance(dataset[i], query, dataset.cols);
if (dcnt<n) {
match[dcnt] = (int)i;
dists[dcnt++] = tmp;
}
else if (tmp < dists[dcnt-1]) {
dists[dcnt-1] = tmp;
match[dcnt-1] = (int)i;
}
int j = dcnt-1;
// bubble up
while (j>=1 && dists[j]<dists[j-1]) {
std::swap(dists[j],dists[j-1]);
std::swap(match[j],match[j-1]);
j--;
}
}
for (int i=0; i<nn; ++i) {
matches[i] = match[i+skip];
}
}
template <typename Distance>
void compute_ground_truth(const Matrix<typename Distance::ElementType>& dataset, const Matrix<typename Distance::ElementType>& testset, Matrix<int>& matches,
int skip=0, Distance d = Distance())
{
for (size_t i=0; i<testset.rows; ++i) {
find_nearest<Distance>(dataset, testset[i], matches[i], (int)matches.cols, skip, d);
}
}
}
//! @endcond
#endif //OPENCV_FLANN_GROUND_TRUTH_H_
@@ -0,0 +1,235 @@
/***********************************************************************
* Software License Agreement (BSD License)
*
* Copyright 2008-2009 Marius Muja (mariusm@cs.ubc.ca). All rights reserved.
* Copyright 2008-2009 David G. Lowe (lowe@cs.ubc.ca). All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*************************************************************************/
#ifndef OPENCV_FLANN_HDF5_H_
#define OPENCV_FLANN_HDF5_H_
//! @cond IGNORED
#include <hdf5.h>
#include "matrix.h"
namespace cvflann
{
namespace
{
template<typename T>
hid_t get_hdf5_type()
{
throw FLANNException("Unsupported type for IO operations");
}
template<>
hid_t get_hdf5_type<char>() { return H5T_NATIVE_CHAR; }
template<>
hid_t get_hdf5_type<unsigned char>() { return H5T_NATIVE_UCHAR; }
template<>
hid_t get_hdf5_type<short int>() { return H5T_NATIVE_SHORT; }
template<>
hid_t get_hdf5_type<unsigned short int>() { return H5T_NATIVE_USHORT; }
template<>
hid_t get_hdf5_type<int>() { return H5T_NATIVE_INT; }
template<>
hid_t get_hdf5_type<unsigned int>() { return H5T_NATIVE_UINT; }
template<>
hid_t get_hdf5_type<long>() { return H5T_NATIVE_LONG; }
template<>
hid_t get_hdf5_type<unsigned long>() { return H5T_NATIVE_ULONG; }
template<>
hid_t get_hdf5_type<float>() { return H5T_NATIVE_FLOAT; }
template<>
hid_t get_hdf5_type<double>() { return H5T_NATIVE_DOUBLE; }
}
#define CHECK_ERROR(x,y) if ((x)<0) throw FLANNException((y));
template<typename T>
void save_to_file(const cvflann::Matrix<T>& dataset, const String& filename, const String& name)
{
#if H5Eset_auto_vers == 2
H5Eset_auto( H5E_DEFAULT, NULL, NULL );
#else
H5Eset_auto( NULL, NULL );
#endif
herr_t status;
hid_t file_id;
file_id = H5Fopen(filename.c_str(), H5F_ACC_RDWR, H5P_DEFAULT);
if (file_id < 0) {
file_id = H5Fcreate(filename.c_str(), H5F_ACC_EXCL, H5P_DEFAULT, H5P_DEFAULT);
}
CHECK_ERROR(file_id,"Error creating hdf5 file.");
hsize_t dimsf[2]; // dataset dimensions
dimsf[0] = dataset.rows;
dimsf[1] = dataset.cols;
hid_t space_id = H5Screate_simple(2, dimsf, NULL);
hid_t memspace_id = H5Screate_simple(2, dimsf, NULL);
hid_t dataset_id;
#if H5Dcreate_vers == 2
dataset_id = H5Dcreate2(file_id, name.c_str(), get_hdf5_type<T>(), space_id, H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT);
#else
dataset_id = H5Dcreate(file_id, name.c_str(), get_hdf5_type<T>(), space_id, H5P_DEFAULT);
#endif
if (dataset_id<0) {
#if H5Dopen_vers == 2
dataset_id = H5Dopen2(file_id, name.c_str(), H5P_DEFAULT);
#else
dataset_id = H5Dopen(file_id, name.c_str());
#endif
}
CHECK_ERROR(dataset_id,"Error creating or opening dataset in file.");
status = H5Dwrite(dataset_id, get_hdf5_type<T>(), memspace_id, space_id, H5P_DEFAULT, dataset.data );
CHECK_ERROR(status, "Error writing to dataset");
H5Sclose(memspace_id);
H5Sclose(space_id);
H5Dclose(dataset_id);
H5Fclose(file_id);
}
template<typename T>
void load_from_file(cvflann::Matrix<T>& dataset, const String& filename, const String& name)
{
herr_t status;
hid_t file_id = H5Fopen(filename.c_str(), H5F_ACC_RDWR, H5P_DEFAULT);
CHECK_ERROR(file_id,"Error opening hdf5 file.");
hid_t dataset_id;
#if H5Dopen_vers == 2
dataset_id = H5Dopen2(file_id, name.c_str(), H5P_DEFAULT);
#else
dataset_id = H5Dopen(file_id, name.c_str());
#endif
CHECK_ERROR(dataset_id,"Error opening dataset in file.");
hid_t space_id = H5Dget_space(dataset_id);
hsize_t dims_out[2];
H5Sget_simple_extent_dims(space_id, dims_out, NULL);
dataset = cvflann::Matrix<T>(new T[dims_out[0]*dims_out[1]], dims_out[0], dims_out[1]);
status = H5Dread(dataset_id, get_hdf5_type<T>(), H5S_ALL, H5S_ALL, H5P_DEFAULT, dataset[0]);
CHECK_ERROR(status, "Error reading dataset");
H5Sclose(space_id);
H5Dclose(dataset_id);
H5Fclose(file_id);
}
#ifdef HAVE_MPI
namespace mpi
{
/**
* Loads a the hyperslice corresponding to this processor from a hdf5 file.
* @param flann_dataset Dataset where the data is loaded
* @param filename HDF5 file name
* @param name Name of dataset inside file
*/
template<typename T>
void load_from_file(cvflann::Matrix<T>& dataset, const String& filename, const String& name)
{
MPI_Comm comm = MPI_COMM_WORLD;
MPI_Info info = MPI_INFO_NULL;
int mpi_size, mpi_rank;
MPI_Comm_size(comm, &mpi_size);
MPI_Comm_rank(comm, &mpi_rank);
herr_t status;
hid_t plist_id = H5Pcreate(H5P_FILE_ACCESS);
H5Pset_fapl_mpio(plist_id, comm, info);
hid_t file_id = H5Fopen(filename.c_str(), H5F_ACC_RDWR, plist_id);
CHECK_ERROR(file_id,"Error opening hdf5 file.");
H5Pclose(plist_id);
hid_t dataset_id;
#if H5Dopen_vers == 2
dataset_id = H5Dopen2(file_id, name.c_str(), H5P_DEFAULT);
#else
dataset_id = H5Dopen(file_id, name.c_str());
#endif
CHECK_ERROR(dataset_id,"Error opening dataset in file.");
hid_t space_id = H5Dget_space(dataset_id);
hsize_t dims[2];
H5Sget_simple_extent_dims(space_id, dims, NULL);
hsize_t count[2];
hsize_t offset[2];
hsize_t item_cnt = dims[0]/mpi_size+(dims[0]%mpi_size==0 ? 0 : 1);
hsize_t cnt = (mpi_rank<mpi_size-1 ? item_cnt : dims[0]-item_cnt*(mpi_size-1));
count[0] = cnt;
count[1] = dims[1];
offset[0] = mpi_rank*item_cnt;
offset[1] = 0;
hid_t memspace_id = H5Screate_simple(2,count,NULL);
H5Sselect_hyperslab(space_id, H5S_SELECT_SET, offset, NULL, count, NULL);
dataset.rows = count[0];
dataset.cols = count[1];
dataset.data = new T[dataset.rows*dataset.cols];
plist_id = H5Pcreate(H5P_DATASET_XFER);
H5Pset_dxpl_mpio(plist_id, H5FD_MPIO_COLLECTIVE);
status = H5Dread(dataset_id, get_hdf5_type<T>(), memspace_id, space_id, plist_id, dataset.data);
CHECK_ERROR(status, "Error reading dataset");
H5Pclose(plist_id);
H5Sclose(space_id);
H5Sclose(memspace_id);
H5Dclose(dataset_id);
H5Fclose(file_id);
}
}
#endif // HAVE_MPI
} // namespace cvflann::mpi
//! @endcond
#endif /* OPENCV_FLANN_HDF5_H_ */
@@ -0,0 +1,244 @@
/***********************************************************************
* Software License Agreement (BSD License)
*
* Copyright 2008-2009 Marius Muja (mariusm@cs.ubc.ca). All rights reserved.
* Copyright 2008-2009 David G. Lowe (lowe@cs.ubc.ca). All rights reserved.
*
* THE BSD LICENSE
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*************************************************************************/
#ifndef OPENCV_FLANN_HEAP_H_
#define OPENCV_FLANN_HEAP_H_
//! @cond IGNORED
#include <algorithm>
#include <vector>
#include <unordered_map>
namespace cvflann
{
// TODO: Define x > y operator and use std::greater<T> instead
template <typename T>
struct greater
{
bool operator()(const T& x, const T& y) const
{
return y < x;
}
};
/**
* Priority Queue Implementation
*
* The priority queue is implemented with a heap. A heap is a complete
* (full) binary tree in which each parent is less than both of its
* children, but the order of the children is unspecified.
*/
template <typename T>
class Heap
{
/**
* Storage array for the heap.
* Type T must be comparable.
*/
std::vector<T> heap;
public:
/**
* \brief Constructs a heap with a pre-allocated capacity
*
* \param capacity heap maximum capacity
*/
Heap(const int capacity)
{
reserve(capacity);
}
/**
* \brief Move-constructs a heap from an external vector
*
* \param vec external vector
*/
Heap(std::vector<T>&& vec)
: heap(std::move(vec))
{
std::make_heap(heap.begin(), heap.end(), greater<T>());
}
/**
*
* \returns heap size
*/
int size() const
{
return (int)heap.size();
}
/**
*
* \returns heap capacity
*/
int capacity() const
{
return (int)heap.capacity();
}
/**
* \brief Tests if the heap is empty
*
* \returns true is heap empty, false otherwise
*/
bool empty()
{
return heap.empty();
}
/**
* \brief Clears the heap.
*/
void clear()
{
heap.clear();
}
/**
* \brief Sets the heap maximum capacity.
*
* \param capacity heap maximum capacity
*/
void reserve(const int capacity)
{
heap.reserve(capacity);
}
/**
* \brief Inserts a new element in the heap.
*
* We select the next empty leaf node, and then keep moving any larger
* parents down until the right location is found to store this element.
*
* \param value the new element to be inserted in the heap
*/
void insert(T value)
{
/* If heap is full, then return without adding this element. */
if (size() == capacity()) {
return;
}
heap.push_back(value);
std::push_heap(heap.begin(), heap.end(), greater<T>());
}
/**
* \brief Returns the node of minimum value from the heap (top of the heap).
*
* \param[out] value parameter used to return the min element
* \returns false if heap empty
*/
bool popMin(T& value)
{
if (empty()) {
return false;
}
value = heap[0];
std::pop_heap(heap.begin(), heap.end(), greater<T>());
heap.pop_back();
return true; /* Return old last node. */
}
/**
* \brief Returns a shared heap for the given memory pool ID.
*
* It constructs the heap if it does not already exists.
*
* \param poolId a user-chosen hashable ID for identifying the heap.
* For thread-safe operations, using current thread ID is a good choice.
* \param capacity heap maximum capacity
* \param iterThreshold remove heaps that were not reused for more than specified iterations count
* if iterThreshold value is less 2, it will be internally adjusted to twice the number of CPU threads
* \returns pointer to the heap
*/
template <typename HashableT>
static cv::Ptr<Heap<T>> getPooledInstance(
const HashableT& poolId, const int capacity, int iterThreshold = 0)
{
static cv::Mutex mutex;
const cv::AutoLock lock(mutex);
struct HeapMapValueType {
cv::Ptr<Heap<T>> heapPtr;
int iterCounter;
};
typedef std::unordered_map<HashableT, HeapMapValueType> HeapMapType;
static HeapMapType heapsPool;
typename HeapMapType::iterator heapIt = heapsPool.find(poolId);
if (heapIt == heapsPool.end())
{
// Construct the heap as it does not already exists
HeapMapValueType heapAndTimePair = {cv::makePtr<Heap<T>>(capacity), 0};
const std::pair<typename HeapMapType::iterator, bool>& emplaceResult = heapsPool.emplace(poolId, std::move(heapAndTimePair));
CV_CheckEQ(static_cast<int>(emplaceResult.second), 1, "Failed to insert the heap into its memory pool");
heapIt = emplaceResult.first;
}
else
{
CV_CheckEQ(heapIt->second.heapPtr.use_count(), 1, "Cannot modify a heap that is currently accessed by another caller");
heapIt->second.heapPtr->clear();
heapIt->second.heapPtr->reserve(capacity);
heapIt->second.iterCounter = 0;
}
if (iterThreshold <= 1) {
iterThreshold = 2 * cv::getNumThreads();
}
// Remove heaps that were not reused for more than given iterThreshold
typename HeapMapType::iterator cleanupIt = heapsPool.begin();
while (cleanupIt != heapsPool.end())
{
if (cleanupIt->second.iterCounter++ > iterThreshold)
{
CV_Assert(cleanupIt != heapIt);
cleanupIt = heapsPool.erase(cleanupIt);
continue;
}
++cleanupIt;
}
return heapIt->second.heapPtr;
}
};
}
//! @endcond
#endif //OPENCV_FLANN_HEAP_H_
@@ -0,0 +1,846 @@
/***********************************************************************
* Software License Agreement (BSD License)
*
* Copyright 2008-2011 Marius Muja (mariusm@cs.ubc.ca). All rights reserved.
* Copyright 2008-2011 David G. Lowe (lowe@cs.ubc.ca). All rights reserved.
*
* THE BSD LICENSE
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*************************************************************************/
#ifndef OPENCV_FLANN_HIERARCHICAL_CLUSTERING_INDEX_H_
#define OPENCV_FLANN_HIERARCHICAL_CLUSTERING_INDEX_H_
//! @cond IGNORED
#include <algorithm>
#include <map>
#include <limits>
#include <cmath>
#include "general.h"
#include "nn_index.h"
#include "dist.h"
#include "matrix.h"
#include "result_set.h"
#include "heap.h"
#include "allocator.h"
#include "random.h"
#include "saving.h"
namespace cvflann
{
struct HierarchicalClusteringIndexParams : public IndexParams
{
HierarchicalClusteringIndexParams(int branching = 32,
flann_centers_init_t centers_init = FLANN_CENTERS_RANDOM,
int trees = 4, int leaf_size = 100)
{
(*this)["algorithm"] = FLANN_INDEX_HIERARCHICAL;
// The branching factor used in the hierarchical clustering
(*this)["branching"] = branching;
// Algorithm used for picking the initial cluster centers
(*this)["centers_init"] = centers_init;
// number of parallel trees to build
(*this)["trees"] = trees;
// maximum leaf size
(*this)["leaf_size"] = leaf_size;
}
};
/**
* Hierarchical index
*
* Contains a tree constructed through a hierarchical clustering
* and other information for indexing a set of points for nearest-neighbour matching.
*/
template <typename Distance>
class HierarchicalClusteringIndex : public NNIndex<Distance>
{
public:
typedef typename Distance::ElementType ElementType;
typedef typename Distance::ResultType DistanceType;
private:
typedef void (HierarchicalClusteringIndex::* centersAlgFunction)(int, int*, int, int*, int&);
/**
* The function used for choosing the cluster centers.
*/
centersAlgFunction chooseCenters;
/**
* Chooses the initial centers in the k-means clustering in a random manner.
*
* Params:
* k = number of centers
* vecs = the dataset of points
* indices = indices in the dataset
* indices_length = length of indices vector
*
*/
void chooseCentersRandom(int k, int* dsindices, int indices_length, int* centers, int& centers_length)
{
UniqueRandom r(indices_length);
int index;
for (index=0; index<k; ++index) {
bool duplicate = true;
int rnd;
while (duplicate) {
duplicate = false;
rnd = r.next();
if (rnd<0) {
centers_length = index;
return;
}
centers[index] = dsindices[rnd];
for (int j=0; j<index; ++j) {
DistanceType sq = distance(dataset[centers[index]], dataset[centers[j]], dataset.cols);
if (sq<1e-16) {
duplicate = true;
}
}
}
}
centers_length = index;
}
/**
* Chooses the initial centers in the k-means using Gonzales' algorithm
* so that the centers are spaced apart from each other.
*
* Params:
* k = number of centers
* vecs = the dataset of points
* indices = indices in the dataset
* Returns:
*/
void chooseCentersGonzales(int k, int* dsindices, int indices_length, int* centers, int& centers_length)
{
int n = indices_length;
int rnd = rand_int(n);
CV_DbgAssert(rnd >=0 && rnd < n);
centers[0] = dsindices[rnd];
int index;
for (index=1; index<k; ++index) {
int best_index = -1;
DistanceType best_val = 0;
for (int j=0; j<n; ++j) {
DistanceType dist = distance(dataset[centers[0]],dataset[dsindices[j]],dataset.cols);
for (int i=1; i<index; ++i) {
DistanceType tmp_dist = distance(dataset[centers[i]],dataset[dsindices[j]],dataset.cols);
if (tmp_dist<dist) {
dist = tmp_dist;
}
}
if (dist>best_val) {
best_val = dist;
best_index = j;
}
}
if (best_index!=-1) {
centers[index] = dsindices[best_index];
}
else {
break;
}
}
centers_length = index;
}
/**
* Chooses the initial centers in the k-means using the algorithm
* proposed in the KMeans++ paper:
* Arthur, David; Vassilvitskii, Sergei - k-means++: The Advantages of Careful Seeding
*
* Implementation of this function was converted from the one provided in Arthur's code.
*
* Params:
* k = number of centers
* vecs = the dataset of points
* indices = indices in the dataset
* Returns:
*/
void chooseCentersKMeanspp(int k, int* dsindices, int indices_length, int* centers, int& centers_length)
{
int n = indices_length;
double currentPot = 0;
DistanceType* closestDistSq = new DistanceType[n];
// Choose one random center and set the closestDistSq values
int index = rand_int(n);
CV_DbgAssert(index >=0 && index < n);
centers[0] = dsindices[index];
// Computing distance^2 will have the advantage of even higher probability further to pick new centers
// far from previous centers (and this complies to "k-means++: the advantages of careful seeding" article)
for (int i = 0; i < n; i++) {
closestDistSq[i] = distance(dataset[dsindices[i]], dataset[dsindices[index]], dataset.cols);
closestDistSq[i] = ensureSquareDistance<Distance>( closestDistSq[i] );
currentPot += closestDistSq[i];
}
const int numLocalTries = 1;
// Choose each center
int centerCount;
for (centerCount = 1; centerCount < k; centerCount++) {
// Repeat several trials
double bestNewPot = -1;
int bestNewIndex = 0;
for (int localTrial = 0; localTrial < numLocalTries; localTrial++) {
// Choose our center - have to be slightly careful to return a valid answer even accounting
// for possible rounding errors
double randVal = rand_double(currentPot);
for (index = 0; index < n-1; index++) {
if (randVal <= closestDistSq[index]) break;
else randVal -= closestDistSq[index];
}
// Compute the new potential
double newPot = 0;
for (int i = 0; i < n; i++) {
DistanceType dist = distance(dataset[dsindices[i]], dataset[dsindices[index]], dataset.cols);
newPot += std::min( ensureSquareDistance<Distance>(dist), closestDistSq[i] );
}
// Store the best result
if ((bestNewPot < 0)||(newPot < bestNewPot)) {
bestNewPot = newPot;
bestNewIndex = index;
}
}
// Add the appropriate center
centers[centerCount] = dsindices[bestNewIndex];
currentPot = bestNewPot;
for (int i = 0; i < n; i++) {
DistanceType dist = distance(dataset[dsindices[i]], dataset[dsindices[bestNewIndex]], dataset.cols);
closestDistSq[i] = std::min( ensureSquareDistance<Distance>(dist), closestDistSq[i] );
}
}
centers_length = centerCount;
delete[] closestDistSq;
}
/**
* Chooses the initial centers in a way inspired by Gonzales (by Pierre-Emmanuel Viel):
* select the first point of the list as a candidate, then parse the points list. If another
* point is further than current candidate from the other centers, test if it is a good center
* of a local aggregation. If it is, replace current candidate by this point. And so on...
*
* Used with KMeansIndex that computes centers coordinates by averaging positions of clusters points,
* this doesn't make a real difference with previous methods. But used with HierarchicalClusteringIndex
* class that pick centers among existing points instead of computing the barycenters, there is a real
* improvement.
*
* Params:
* k = number of centers
* vecs = the dataset of points
* indices = indices in the dataset
* Returns:
*/
void GroupWiseCenterChooser(int k, int* dsindices, int indices_length, int* centers, int& centers_length)
{
const float kSpeedUpFactor = 1.3f;
int n = indices_length;
DistanceType* closestDistSq = new DistanceType[n];
// Choose one random center and set the closestDistSq values
int index = rand_int(n);
CV_DbgAssert(index >=0 && index < n);
centers[0] = dsindices[index];
for (int i = 0; i < n; i++) {
closestDistSq[i] = distance(dataset[dsindices[i]], dataset[dsindices[index]], dataset.cols);
}
// Choose each center
int centerCount;
for (centerCount = 1; centerCount < k; centerCount++) {
// Repeat several trials
double bestNewPot = -1;
int bestNewIndex = 0;
DistanceType furthest = 0;
for (index = 0; index < n; index++) {
// We will test only the potential of the points further than current candidate
if( closestDistSq[index] > kSpeedUpFactor * (float)furthest ) {
// Compute the new potential
double newPot = 0;
for (int i = 0; i < n; i++) {
newPot += std::min( distance(dataset[dsindices[i]], dataset[dsindices[index]], dataset.cols)
, closestDistSq[i] );
}
// Store the best result
if ((bestNewPot < 0)||(newPot <= bestNewPot)) {
bestNewPot = newPot;
bestNewIndex = index;
furthest = closestDistSq[index];
}
}
}
// Add the appropriate center
centers[centerCount] = dsindices[bestNewIndex];
for (int i = 0; i < n; i++) {
closestDistSq[i] = std::min( distance(dataset[dsindices[i]], dataset[dsindices[bestNewIndex]], dataset.cols)
, closestDistSq[i] );
}
}
centers_length = centerCount;
delete[] closestDistSq;
}
public:
/**
* Index constructor
*
* Params:
* inputData = dataset with the input features
* params = parameters passed to the hierarchical k-means algorithm
*/
HierarchicalClusteringIndex(const Matrix<ElementType>& inputData, const IndexParams& index_params = HierarchicalClusteringIndexParams(),
Distance d = Distance())
: dataset(inputData), params(index_params), root(NULL), indices(NULL), distance(d)
{
memoryCounter = 0;
size_ = dataset.rows;
veclen_ = dataset.cols;
branching_ = get_param(params,"branching",32);
centers_init_ = get_param(params,"centers_init", FLANN_CENTERS_RANDOM);
trees_ = get_param(params,"trees",4);
leaf_size_ = get_param(params,"leaf_size",100);
if (centers_init_==FLANN_CENTERS_RANDOM) {
chooseCenters = &HierarchicalClusteringIndex::chooseCentersRandom;
}
else if (centers_init_==FLANN_CENTERS_GONZALES) {
chooseCenters = &HierarchicalClusteringIndex::chooseCentersGonzales;
}
else if (centers_init_==FLANN_CENTERS_KMEANSPP) {
chooseCenters = &HierarchicalClusteringIndex::chooseCentersKMeanspp;
}
else if (centers_init_==FLANN_CENTERS_GROUPWISE) {
chooseCenters = &HierarchicalClusteringIndex::GroupWiseCenterChooser;
}
else {
FLANN_THROW(cv::Error::StsError, "Unknown algorithm for choosing initial centers.");
}
root = new NodePtr[trees_];
indices = new int*[trees_];
for (int i=0; i<trees_; ++i) {
root[i] = NULL;
indices[i] = NULL;
}
}
HierarchicalClusteringIndex(const HierarchicalClusteringIndex&);
HierarchicalClusteringIndex& operator=(const HierarchicalClusteringIndex&);
/**
* Index destructor.
*
* Release the memory used by the index.
*/
virtual ~HierarchicalClusteringIndex()
{
if (root!=NULL) {
delete[] root;
}
if (indices!=NULL) {
free_indices();
delete[] indices;
}
}
/**
* Returns size of index.
*/
size_t size() const CV_OVERRIDE
{
return size_;
}
/**
* Returns the length of an index feature.
*/
size_t veclen() const CV_OVERRIDE
{
return veclen_;
}
/**
* Computes the inde memory usage
* Returns: memory used by the index
*/
int usedMemory() const CV_OVERRIDE
{
return pool.usedMemory+pool.wastedMemory+memoryCounter;
}
/**
* Builds the index
*/
void buildIndex() CV_OVERRIDE
{
if (branching_<2) {
FLANN_THROW(cv::Error::StsError, "Branching factor must be at least 2");
}
free_indices();
for (int i=0; i<trees_; ++i) {
indices[i] = new int[size_];
for (size_t j=0; j<size_; ++j) {
indices[i][j] = (int)j;
}
root[i] = pool.allocate<Node>();
computeClustering(root[i], indices[i], (int)size_, branching_,0);
}
}
flann_algorithm_t getType() const CV_OVERRIDE
{
return FLANN_INDEX_HIERARCHICAL;
}
void saveIndex(FILE* stream) CV_OVERRIDE
{
save_value(stream, branching_);
save_value(stream, trees_);
save_value(stream, centers_init_);
save_value(stream, leaf_size_);
save_value(stream, memoryCounter);
for (int i=0; i<trees_; ++i) {
save_value(stream, *indices[i], size_);
save_tree(stream, root[i], i);
}
}
void loadIndex(FILE* stream) CV_OVERRIDE
{
if (root!=NULL) {
delete[] root;
}
if (indices!=NULL) {
free_indices();
delete[] indices;
}
load_value(stream, branching_);
load_value(stream, trees_);
load_value(stream, centers_init_);
load_value(stream, leaf_size_);
load_value(stream, memoryCounter);
indices = new int*[trees_];
root = new NodePtr[trees_];
for (int i=0; i<trees_; ++i) {
indices[i] = new int[size_];
load_value(stream, *indices[i], size_);
load_tree(stream, root[i], i);
}
params["algorithm"] = getType();
params["branching"] = branching_;
params["trees"] = trees_;
params["centers_init"] = centers_init_;
params["leaf_size"] = leaf_size_;
}
/**
* Find set of nearest neighbors to vec. Their indices are stored inside
* the result object.
*
* Params:
* result = the result object in which the indices of the nearest-neighbors are stored
* vec = the vector for which to search the nearest neighbors
* searchParams = parameters that influence the search algorithm (checks)
*/
void findNeighbors(ResultSet<DistanceType>& result, const ElementType* vec, const SearchParams& searchParams) CV_OVERRIDE
{
const int maxChecks = get_param(searchParams,"checks",32);
const bool explore_all_trees = get_param(searchParams,"explore_all_trees",false);
// Priority queue storing intermediate branches in the best-bin-first search
const cv::Ptr<Heap<BranchSt>>& heap = Heap<BranchSt>::getPooledInstance(cv::utils::getThreadID(), (int)size_);
std::vector<bool> checked(size_,false);
int checks = 0;
for (int i=0; i<trees_; ++i) {
findNN(root[i], result, vec, checks, maxChecks, heap, checked, explore_all_trees);
if (!explore_all_trees && (checks >= maxChecks) && result.full())
break;
}
BranchSt branch;
while (heap->popMin(branch) && (checks<maxChecks || !result.full())) {
NodePtr node = branch.node;
findNN(node, result, vec, checks, maxChecks, heap, checked, false);
}
CV_Assert(result.full());
}
IndexParams getParameters() const CV_OVERRIDE
{
return params;
}
private:
/**
* Structure representing a node in the hierarchical k-means tree.
*/
struct Node
{
/**
* The cluster center index
*/
int pivot;
/**
* The cluster size (number of points in the cluster)
*/
int size;
/**
* Child nodes (only for non-terminal nodes)
*/
Node** childs;
/**
* Node points (only for terminal nodes)
*/
int* indices;
/**
* Level
*/
int level;
};
typedef Node* NodePtr;
/**
* Alias definition for a nicer syntax.
*/
typedef BranchStruct<NodePtr, DistanceType> BranchSt;
void save_tree(FILE* stream, NodePtr node, int num)
{
save_value(stream, *node);
if (node->childs==NULL) {
int indices_offset = (int)(node->indices - indices[num]);
save_value(stream, indices_offset);
}
else {
for(int i=0; i<branching_; ++i) {
save_tree(stream, node->childs[i], num);
}
}
}
void load_tree(FILE* stream, NodePtr& node, int num)
{
node = pool.allocate<Node>();
load_value(stream, *node);
if (node->childs==NULL) {
int indices_offset;
load_value(stream, indices_offset);
node->indices = indices[num] + indices_offset;
}
else {
node->childs = pool.allocate<NodePtr>(branching_);
for(int i=0; i<branching_; ++i) {
load_tree(stream, node->childs[i], num);
}
}
}
/**
* Release the inner elements of indices[]
*/
void free_indices()
{
if (indices!=NULL) {
for(int i=0; i<trees_; ++i) {
if (indices[i]!=NULL) {
delete[] indices[i];
indices[i] = NULL;
}
}
}
}
void computeLabels(int* dsindices, int indices_length, int* centers, int centers_length, int* labels, DistanceType& cost)
{
cost = 0;
for (int i=0; i<indices_length; ++i) {
ElementType* point = dataset[dsindices[i]];
DistanceType dist = distance(point, dataset[centers[0]], veclen_);
labels[i] = 0;
for (int j=1; j<centers_length; ++j) {
DistanceType new_dist = distance(point, dataset[centers[j]], veclen_);
if (dist>new_dist) {
labels[i] = j;
dist = new_dist;
}
}
cost += dist;
}
}
/**
* The method responsible with actually doing the recursive hierarchical
* clustering
*
* Params:
* node = the node to cluster
* indices = indices of the points belonging to the current node
* branching = the branching factor to use in the clustering
*
* TODO: for 1-sized clusters don't store a cluster center (it's the same as the single cluster point)
*/
void computeClustering(NodePtr node, int* dsindices, int indices_length, int branching, int level)
{
node->size = indices_length;
node->level = level;
if (indices_length < leaf_size_) { // leaf node
node->indices = dsindices;
std::sort(node->indices,node->indices+indices_length);
node->childs = NULL;
return;
}
std::vector<int> centers(branching);
std::vector<int> labels(indices_length);
int centers_length;
(this->*chooseCenters)(branching, dsindices, indices_length, &centers[0], centers_length);
if (centers_length<branching) {
node->indices = dsindices;
std::sort(node->indices,node->indices+indices_length);
node->childs = NULL;
return;
}
// assign points to clusters
DistanceType cost;
computeLabels(dsindices, indices_length, &centers[0], centers_length, &labels[0], cost);
node->childs = pool.allocate<NodePtr>(branching);
int start = 0;
int end = start;
for (int i=0; i<branching; ++i) {
for (int j=0; j<indices_length; ++j) {
if (labels[j]==i) {
std::swap(dsindices[j],dsindices[end]);
std::swap(labels[j],labels[end]);
end++;
}
}
node->childs[i] = pool.allocate<Node>();
node->childs[i]->pivot = centers[i];
node->childs[i]->indices = NULL;
computeClustering(node->childs[i],dsindices+start, end-start, branching, level+1);
start=end;
}
}
/**
* Performs one descent in the hierarchical k-means tree. The branches not
* visited are stored in a priority queue.
*
* Params:
* node = node to explore
* result = container for the k-nearest neighbors found
* vec = query points
* checks = how many points in the dataset have been checked so far
* maxChecks = maximum dataset points to checks
*/
void findNN(NodePtr node, ResultSet<DistanceType>& result, const ElementType* vec, int& checks, int maxChecks,
const cv::Ptr<Heap<BranchSt>>& heap, std::vector<bool>& checked, bool explore_all_trees = false)
{
if (node->childs==NULL) {
if (!explore_all_trees && (checks>=maxChecks) && result.full()) {
return;
}
for (int i=0; i<node->size; ++i) {
int index = node->indices[i];
if (!checked[index]) {
DistanceType dist = distance(dataset[index], vec, veclen_);
result.addPoint(dist, index);
checked[index] = true;
++checks;
}
}
}
else {
DistanceType* domain_distances = new DistanceType[branching_];
int best_index = 0;
domain_distances[best_index] = distance(vec, dataset[node->childs[best_index]->pivot], veclen_);
for (int i=1; i<branching_; ++i) {
domain_distances[i] = distance(vec, dataset[node->childs[i]->pivot], veclen_);
if (domain_distances[i]<domain_distances[best_index]) {
best_index = i;
}
}
for (int i=0; i<branching_; ++i) {
if (i!=best_index) {
heap->insert(BranchSt(node->childs[i],domain_distances[i]));
}
}
delete[] domain_distances;
findNN(node->childs[best_index],result,vec, checks, maxChecks, heap, checked, explore_all_trees);
}
}
private:
/**
* The dataset used by this index
*/
const Matrix<ElementType> dataset;
/**
* Parameters used by this index
*/
IndexParams params;
/**
* Number of features in the dataset.
*/
size_t size_;
/**
* Length of each feature.
*/
size_t veclen_;
/**
* The root node in the tree.
*/
NodePtr* root;
/**
* Array of indices to vectors in the dataset.
*/
int** indices;
/**
* The distance
*/
Distance distance;
/**
* Pooled memory allocator.
*
* Using a pooled memory allocator is more efficient
* than allocating memory directly when there is a large
* number small of memory allocations.
*/
PooledAllocator pool;
/**
* Memory occupied by the index.
*/
int memoryCounter;
/** index parameters */
int branching_;
int trees_;
flann_centers_init_t centers_init_;
int leaf_size_;
};
}
//! @endcond
#endif /* OPENCV_FLANN_HIERARCHICAL_CLUSTERING_INDEX_H_ */
@@ -0,0 +1,319 @@
/***********************************************************************
* Software License Agreement (BSD License)
*
* Copyright 2008-2009 Marius Muja (mariusm@cs.ubc.ca). All rights reserved.
* Copyright 2008-2009 David G. Lowe (lowe@cs.ubc.ca). All rights reserved.
*
* THE BSD LICENSE
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*************************************************************************/
#ifndef OPENCV_FLANN_INDEX_TESTING_H_
#define OPENCV_FLANN_INDEX_TESTING_H_
//! @cond IGNORED
#include <cstring>
#include <cmath>
#include "matrix.h"
#include "nn_index.h"
#include "result_set.h"
#include "logger.h"
#include "timer.h"
namespace cvflann
{
inline int countCorrectMatches(int* neighbors, int* groundTruth, int n)
{
int count = 0;
for (int i=0; i<n; ++i) {
for (int k=0; k<n; ++k) {
if (neighbors[i]==groundTruth[k]) {
count++;
break;
}
}
}
return count;
}
template <typename Distance>
typename Distance::ResultType computeDistanceRaport(const Matrix<typename Distance::ElementType>& inputData, typename Distance::ElementType* target,
int* neighbors, int* groundTruth, int veclen, int n, const Distance& distance)
{
typedef typename Distance::ResultType DistanceType;
DistanceType ret = 0;
for (int i=0; i<n; ++i) {
DistanceType den = distance(inputData[groundTruth[i]], target, veclen);
DistanceType num = distance(inputData[neighbors[i]], target, veclen);
if ((den==0)&&(num==0)) {
ret += 1;
}
else {
ret += num/den;
}
}
return ret;
}
template <typename Distance>
float search_with_ground_truth(NNIndex<Distance>& index, const Matrix<typename Distance::ElementType>& inputData,
const Matrix<typename Distance::ElementType>& testData, const Matrix<int>& matches, int nn, int checks,
float& time, typename Distance::ResultType& dist, const Distance& distance, int skipMatches)
{
typedef typename Distance::ResultType DistanceType;
if (matches.cols<size_t(nn)) {
Logger::info("matches.cols=%d, nn=%d\n",matches.cols,nn);
FLANN_THROW(cv::Error::StsError, "Ground truth is not computed for as many neighbors as requested");
}
KNNResultSet<DistanceType> resultSet(nn+skipMatches);
SearchParams searchParams(checks);
std::vector<int> indices(nn+skipMatches);
std::vector<DistanceType> dists(nn+skipMatches);
int* neighbors = &indices[skipMatches];
int correct = 0;
DistanceType distR = 0;
StartStopTimer t;
int repeats = 0;
while (t.value<0.2) {
repeats++;
t.start();
correct = 0;
distR = 0;
for (size_t i = 0; i < testData.rows; i++) {
resultSet.init(&indices[0], &dists[0]);
index.findNeighbors(resultSet, testData[i], searchParams);
correct += countCorrectMatches(neighbors,matches[i], nn);
distR += computeDistanceRaport<Distance>(inputData, testData[i], neighbors, matches[i], (int)testData.cols, nn, distance);
}
t.stop();
}
time = float(t.value/repeats);
float precicion = (float)correct/(nn*testData.rows);
dist = distR/(testData.rows*nn);
Logger::info("%8d %10.4g %10.5g %10.5g %10.5g\n",
checks, precicion, time, 1000.0 * time / testData.rows, dist);
return precicion;
}
template <typename Distance>
float test_index_checks(NNIndex<Distance>& index, const Matrix<typename Distance::ElementType>& inputData,
const Matrix<typename Distance::ElementType>& testData, const Matrix<int>& matches,
int checks, float& precision, const Distance& distance, int nn = 1, int skipMatches = 0)
{
typedef typename Distance::ResultType DistanceType;
Logger::info(" Nodes Precision(%) Time(s) Time/vec(ms) Mean dist\n");
Logger::info("---------------------------------------------------------\n");
float time = 0;
DistanceType dist = 0;
precision = search_with_ground_truth(index, inputData, testData, matches, nn, checks, time, dist, distance, skipMatches);
return time;
}
template <typename Distance>
float test_index_precision(NNIndex<Distance>& index, const Matrix<typename Distance::ElementType>& inputData,
const Matrix<typename Distance::ElementType>& testData, const Matrix<int>& matches,
float precision, int& checks, const Distance& distance, int nn = 1, int skipMatches = 0)
{
typedef typename Distance::ResultType DistanceType;
const float SEARCH_EPS = 0.001f;
Logger::info(" Nodes Precision(%) Time(s) Time/vec(ms) Mean dist\n");
Logger::info("---------------------------------------------------------\n");
int c2 = 1;
float p2;
int c1 = 1;
//float p1;
float time;
DistanceType dist;
p2 = search_with_ground_truth(index, inputData, testData, matches, nn, c2, time, dist, distance, skipMatches);
if (p2>precision) {
Logger::info("Got as close as I can\n");
checks = c2;
return time;
}
while (p2<precision) {
c1 = c2;
//p1 = p2;
c2 *=2;
p2 = search_with_ground_truth(index, inputData, testData, matches, nn, c2, time, dist, distance, skipMatches);
}
int cx;
float realPrecision;
if (fabs(p2-precision)>SEARCH_EPS) {
Logger::info("Start linear estimation\n");
// after we got to values in the vecinity of the desired precision
// use linear approximation get a better estimation
cx = (c1+c2)/2;
realPrecision = search_with_ground_truth(index, inputData, testData, matches, nn, cx, time, dist, distance, skipMatches);
while (fabs(realPrecision-precision)>SEARCH_EPS) {
if (realPrecision<precision) {
c1 = cx;
}
else {
c2 = cx;
}
cx = (c1+c2)/2;
if (cx==c1) {
Logger::info("Got as close as I can\n");
break;
}
realPrecision = search_with_ground_truth(index, inputData, testData, matches, nn, cx, time, dist, distance, skipMatches);
}
c2 = cx;
p2 = realPrecision;
}
else {
Logger::info("No need for linear estimation\n");
cx = c2;
realPrecision = p2;
}
checks = cx;
return time;
}
template <typename Distance>
void test_index_precisions(NNIndex<Distance>& index, const Matrix<typename Distance::ElementType>& inputData,
const Matrix<typename Distance::ElementType>& testData, const Matrix<int>& matches,
float* precisions, int precisions_length, const Distance& distance, int nn = 1, int skipMatches = 0, float maxTime = 0)
{
typedef typename Distance::ResultType DistanceType;
const float SEARCH_EPS = 0.001;
// make sure precisions array is sorted
std::sort(precisions, precisions+precisions_length);
int pindex = 0;
float precision = precisions[pindex];
Logger::info(" Nodes Precision(%) Time(s) Time/vec(ms) Mean dist\n");
Logger::info("---------------------------------------------------------\n");
int c2 = 1;
float p2;
int c1 = 1;
float time;
DistanceType dist;
p2 = search_with_ground_truth(index, inputData, testData, matches, nn, c2, time, dist, distance, skipMatches);
// if precision for 1 run down the tree is already
// better then some of the requested precisions, then
// skip those
while (precisions[pindex]<p2 && pindex<precisions_length) {
pindex++;
}
if (pindex==precisions_length) {
Logger::info("Got as close as I can\n");
return;
}
for (int i=pindex; i<precisions_length; ++i) {
precision = precisions[i];
while (p2<precision) {
c1 = c2;
c2 *=2;
p2 = search_with_ground_truth(index, inputData, testData, matches, nn, c2, time, dist, distance, skipMatches);
if ((maxTime> 0)&&(time > maxTime)&&(p2<precision)) return;
}
int cx;
float realPrecision;
if (fabs(p2-precision)>SEARCH_EPS) {
Logger::info("Start linear estimation\n");
// after we got to values in the vecinity of the desired precision
// use linear approximation get a better estimation
cx = (c1+c2)/2;
realPrecision = search_with_ground_truth(index, inputData, testData, matches, nn, cx, time, dist, distance, skipMatches);
while (fabs(realPrecision-precision)>SEARCH_EPS) {
if (realPrecision<precision) {
c1 = cx;
}
else {
c2 = cx;
}
cx = (c1+c2)/2;
if (cx==c1) {
Logger::info("Got as close as I can\n");
break;
}
realPrecision = search_with_ground_truth(index, inputData, testData, matches, nn, cx, time, dist, distance, skipMatches);
}
c2 = cx;
p2 = realPrecision;
}
else {
Logger::info("No need for linear estimation\n");
cx = c2;
realPrecision = p2;
}
}
}
}
//! @endcond
#endif //OPENCV_FLANN_INDEX_TESTING_H_
@@ -0,0 +1,636 @@
/***********************************************************************
* Software License Agreement (BSD License)
*
* Copyright 2008-2009 Marius Muja (mariusm@cs.ubc.ca). All rights reserved.
* Copyright 2008-2009 David G. Lowe (lowe@cs.ubc.ca). All rights reserved.
*
* THE BSD LICENSE
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*************************************************************************/
#ifndef OPENCV_FLANN_KDTREE_INDEX_H_
#define OPENCV_FLANN_KDTREE_INDEX_H_
//! @cond IGNORED
#include <algorithm>
#include <map>
#include <cstring>
#include "nn_index.h"
#include "dynamic_bitset.h"
#include "matrix.h"
#include "result_set.h"
#include "heap.h"
#include "allocator.h"
#include "random.h"
#include "saving.h"
namespace cvflann
{
struct KDTreeIndexParams : public IndexParams
{
KDTreeIndexParams(int trees = 4)
{
(*this)["algorithm"] = FLANN_INDEX_KDTREE;
(*this)["trees"] = trees;
}
};
/**
* Randomized kd-tree index
*
* Contains the k-d trees and other information for indexing a set of points
* for nearest-neighbor matching.
*/
template <typename Distance>
class KDTreeIndex : public NNIndex<Distance>
{
public:
typedef typename Distance::ElementType ElementType;
typedef typename Distance::ResultType DistanceType;
/**
* KDTree constructor
*
* Params:
* inputData = dataset with the input features
* params = parameters passed to the kdtree algorithm
*/
KDTreeIndex(const Matrix<ElementType>& inputData, const IndexParams& params = KDTreeIndexParams(),
Distance d = Distance() ) :
dataset_(inputData), index_params_(params), distance_(d)
{
size_ = dataset_.rows;
veclen_ = dataset_.cols;
trees_ = get_param(index_params_,"trees",4);
tree_roots_ = new NodePtr[trees_];
// Create a permutable array of indices to the input vectors.
vind_.resize(size_);
for (size_t i = 0; i < size_; ++i) {
vind_[i] = int(i);
}
mean_ = new DistanceType[veclen_];
var_ = new DistanceType[veclen_];
}
KDTreeIndex(const KDTreeIndex&);
KDTreeIndex& operator=(const KDTreeIndex&);
/**
* Standard destructor
*/
~KDTreeIndex()
{
if (tree_roots_!=NULL) {
delete[] tree_roots_;
}
delete[] mean_;
delete[] var_;
}
/**
* Builds the index
*/
void buildIndex() CV_OVERRIDE
{
/* Construct the randomized trees. */
for (int i = 0; i < trees_; i++) {
/* Randomize the order of vectors to allow for unbiased sampling. */
#ifndef OPENCV_FLANN_USE_STD_RAND
cv::randShuffle(vind_);
#else
std::random_shuffle(vind_.begin(), vind_.end());
#endif
tree_roots_[i] = divideTree(&vind_[0], int(size_) );
}
}
flann_algorithm_t getType() const CV_OVERRIDE
{
return FLANN_INDEX_KDTREE;
}
void saveIndex(FILE* stream) CV_OVERRIDE
{
save_value(stream, trees_);
for (int i=0; i<trees_; ++i) {
save_tree(stream, tree_roots_[i]);
}
}
void loadIndex(FILE* stream) CV_OVERRIDE
{
load_value(stream, trees_);
if (tree_roots_!=NULL) {
delete[] tree_roots_;
}
tree_roots_ = new NodePtr[trees_];
for (int i=0; i<trees_; ++i) {
load_tree(stream,tree_roots_[i]);
}
index_params_["algorithm"] = getType();
index_params_["trees"] = tree_roots_;
}
/**
* Returns size of index.
*/
size_t size() const CV_OVERRIDE
{
return size_;
}
/**
* Returns the length of an index feature.
*/
size_t veclen() const CV_OVERRIDE
{
return veclen_;
}
/**
* Computes the inde memory usage
* Returns: memory used by the index
*/
int usedMemory() const CV_OVERRIDE
{
return int(pool_.usedMemory+pool_.wastedMemory+dataset_.rows*sizeof(int)); // pool memory and vind array memory
}
/**
* Find set of nearest neighbors to vec. Their indices are stored inside
* the result object.
*
* Params:
* result = the result object in which the indices of the nearest-neighbors are stored
* vec = the vector for which to search the nearest neighbors
* maxCheck = the maximum number of restarts (in a best-bin-first manner)
*/
void findNeighbors(ResultSet<DistanceType>& result, const ElementType* vec, const SearchParams& searchParams) CV_OVERRIDE
{
const int maxChecks = get_param(searchParams,"checks", 32);
const float epsError = 1+get_param(searchParams,"eps",0.0f);
const bool explore_all_trees = get_param(searchParams,"explore_all_trees",false);
if (maxChecks==FLANN_CHECKS_UNLIMITED) {
getExactNeighbors(result, vec, epsError);
}
else {
getNeighbors(result, vec, maxChecks, epsError, explore_all_trees);
}
}
IndexParams getParameters() const CV_OVERRIDE
{
return index_params_;
}
private:
/*--------------------- Internal Data Structures --------------------------*/
struct Node
{
/**
* Dimension used for subdivision.
*/
int divfeat;
/**
* The values used for subdivision.
*/
DistanceType divval;
/**
* The child nodes.
*/
Node* child1, * child2;
};
typedef Node* NodePtr;
typedef BranchStruct<NodePtr, DistanceType> BranchSt;
typedef BranchSt* Branch;
void save_tree(FILE* stream, NodePtr tree)
{
save_value(stream, *tree);
if (tree->child1!=NULL) {
save_tree(stream, tree->child1);
}
if (tree->child2!=NULL) {
save_tree(stream, tree->child2);
}
}
void load_tree(FILE* stream, NodePtr& tree)
{
tree = pool_.allocate<Node>();
load_value(stream, *tree);
if (tree->child1!=NULL) {
load_tree(stream, tree->child1);
}
if (tree->child2!=NULL) {
load_tree(stream, tree->child2);
}
}
/**
* Create a tree node that subdivides the list of vecs from vind[first]
* to vind[last]. The routine is called recursively on each sublist.
* Place a pointer to this new tree node in the location pTree.
*
* Params: pTree = the new node to create
* first = index of the first vector
* last = index of the last vector
*/
NodePtr divideTree(int* ind, int count)
{
NodePtr node = pool_.allocate<Node>(); // allocate memory
/* If too few exemplars remain, then make this a leaf node. */
if ( count == 1) {
node->child1 = node->child2 = NULL; /* Mark as leaf node. */
node->divfeat = *ind; /* Store index of this vec. */
}
else {
int idx;
int cutfeat;
DistanceType cutval;
meanSplit(ind, count, idx, cutfeat, cutval);
node->divfeat = cutfeat;
node->divval = cutval;
node->child1 = divideTree(ind, idx);
node->child2 = divideTree(ind+idx, count-idx);
}
return node;
}
/**
* Choose which feature to use in order to subdivide this set of vectors.
* Make a random choice among those with the highest variance, and use
* its variance as the threshold value.
*/
void meanSplit(int* ind, int count, int& index, int& cutfeat, DistanceType& cutval)
{
memset(mean_,0,veclen_*sizeof(DistanceType));
memset(var_,0,veclen_*sizeof(DistanceType));
/* Compute mean values. Only the first SAMPLE_MEAN values need to be
sampled to get a good estimate.
*/
int cnt = std::min((int)SAMPLE_MEAN+1, count);
for (int j = 0; j < cnt; ++j) {
ElementType* v = dataset_[ind[j]];
for (size_t k=0; k<veclen_; ++k) {
mean_[k] += v[k];
}
}
for (size_t k=0; k<veclen_; ++k) {
mean_[k] /= cnt;
}
/* Compute variances (no need to divide by count). */
for (int j = 0; j < cnt; ++j) {
ElementType* v = dataset_[ind[j]];
for (size_t k=0; k<veclen_; ++k) {
DistanceType dist = v[k] - mean_[k];
var_[k] += dist * dist;
}
}
/* Select one of the highest variance indices at random. */
cutfeat = selectDivision(var_);
cutval = mean_[cutfeat];
int lim1, lim2;
planeSplit(ind, count, cutfeat, cutval, lim1, lim2);
if (lim1>count/2) index = lim1;
else if (lim2<count/2) index = lim2;
else index = count/2;
/* If either list is empty, it means that all remaining features
* are identical. Split in the middle to maintain a balanced tree.
*/
if ((lim1==count)||(lim2==0)) index = count/2;
}
/**
* Select the top RAND_DIM largest values from v and return the index of
* one of these selected at random.
*/
int selectDivision(DistanceType* v)
{
int num = 0;
size_t topind[RAND_DIM];
/* Create a list of the indices of the top RAND_DIM values. */
for (size_t i = 0; i < veclen_; ++i) {
if ((num < RAND_DIM)||(v[i] > v[topind[num-1]])) {
/* Put this element at end of topind. */
if (num < RAND_DIM) {
topind[num++] = i; /* Add to list. */
}
else {
topind[num-1] = i; /* Replace last element. */
}
/* Bubble end value down to right location by repeated swapping. */
int j = num - 1;
while (j > 0 && v[topind[j]] > v[topind[j-1]]) {
std::swap(topind[j], topind[j-1]);
--j;
}
}
}
/* Select a random integer in range [0,num-1], and return that index. */
int rnd = rand_int(num);
return (int)topind[rnd];
}
/**
* Subdivide the list of points by a plane perpendicular on axe corresponding
* to the 'cutfeat' dimension at 'cutval' position.
*
* On return:
* dataset[ind[0..lim1-1]][cutfeat]<cutval
* dataset[ind[lim1..lim2-1]][cutfeat]==cutval
* dataset[ind[lim2..count]][cutfeat]>cutval
*/
void planeSplit(int* ind, int count, int cutfeat, DistanceType cutval, int& lim1, int& lim2)
{
/* Move vector indices for left subtree to front of list. */
int left = 0;
int right = count-1;
for (;; ) {
while (left<=right && dataset_[ind[left]][cutfeat]<cutval) ++left;
while (left<=right && dataset_[ind[right]][cutfeat]>=cutval) --right;
if (left>right) break;
std::swap(ind[left], ind[right]); ++left; --right;
}
lim1 = left;
right = count-1;
for (;; ) {
while (left<=right && dataset_[ind[left]][cutfeat]<=cutval) ++left;
while (left<=right && dataset_[ind[right]][cutfeat]>cutval) --right;
if (left>right) break;
std::swap(ind[left], ind[right]); ++left; --right;
}
lim2 = left;
}
/**
* Performs an exact nearest neighbor search. The exact search performs a full
* traversal of the tree.
*/
void getExactNeighbors(ResultSet<DistanceType>& result, const ElementType* vec, float epsError)
{
// checkID -= 1; /* Set a different unique ID for each search. */
if (trees_ > 1) {
fprintf(stderr,"It doesn't make any sense to use more than one tree for exact search");
}
if (trees_>0) {
searchLevelExact(result, vec, tree_roots_[0], 0.0, epsError);
}
CV_Assert(result.full());
}
/**
* Performs the approximate nearest-neighbor search. The search is approximate
* because the tree traversal is abandoned after a given number of descends in
* the tree.
*/
void getNeighbors(ResultSet<DistanceType>& result, const ElementType* vec,
int maxCheck, float epsError, bool explore_all_trees = false)
{
int i;
BranchSt branch;
int checkCount = 0;
DynamicBitset checked(size_);
// Priority queue storing intermediate branches in the best-bin-first search
const cv::Ptr<Heap<BranchSt>>& heap = Heap<BranchSt>::getPooledInstance(cv::utils::getThreadID(), (int)size_);
/* Search once through each tree down to root. */
for (i = 0; i < trees_; ++i) {
searchLevel(result, vec, tree_roots_[i], 0, checkCount, maxCheck,
epsError, heap, checked, explore_all_trees);
if (!explore_all_trees && (checkCount >= maxCheck) && result.full())
break;
}
/* Keep searching other branches from heap until finished. */
while ( heap->popMin(branch) && (checkCount < maxCheck || !result.full() )) {
searchLevel(result, vec, branch.node, branch.mindist, checkCount, maxCheck,
epsError, heap, checked, false);
}
CV_Assert(result.full());
}
/**
* Search starting from a given node of the tree. Based on any mismatches at
* higher levels, all exemplars below this level must have a distance of
* at least "mindistsq".
*/
void searchLevel(ResultSet<DistanceType>& result_set, const ElementType* vec, NodePtr node, DistanceType mindist, int& checkCount, int maxCheck,
float epsError, const cv::Ptr<Heap<BranchSt>>& heap, DynamicBitset& checked, bool explore_all_trees = false)
{
if (result_set.worstDist()<mindist) {
// printf("Ignoring branch, too far\n");
return;
}
/* If this is a leaf node, then do check and return. */
if ((node->child1 == NULL)&&(node->child2 == NULL)) {
/* Do not check same node more than once when searching multiple trees.
Once a vector is checked, we set its location in vind to the
current checkID.
*/
int index = node->divfeat;
if ( checked.test(index) ||
(!explore_all_trees && (checkCount>=maxCheck) && result_set.full()) ) {
return;
}
checked.set(index);
checkCount++;
DistanceType dist = distance_(dataset_[index], vec, veclen_);
result_set.addPoint(dist,index);
return;
}
/* Which child branch should be taken first? */
ElementType val = vec[node->divfeat];
DistanceType diff = val - node->divval;
NodePtr bestChild = (diff < 0) ? node->child1 : node->child2;
NodePtr otherChild = (diff < 0) ? node->child2 : node->child1;
/* Create a branch record for the branch not taken. Add distance
of this feature boundary (we don't attempt to correct for any
use of this feature in a parent node, which is unlikely to
happen and would have only a small effect). Don't bother
adding more branches to heap after halfway point, as cost of
adding exceeds their value.
*/
DistanceType new_distsq = mindist + distance_.accum_dist(val, node->divval, node->divfeat);
// if (2 * checkCount < maxCheck || !result.full()) {
if ((new_distsq*epsError < result_set.worstDist())|| !result_set.full()) {
heap->insert( BranchSt(otherChild, new_distsq) );
}
/* Call recursively to search next level down. */
searchLevel(result_set, vec, bestChild, mindist, checkCount, maxCheck, epsError, heap, checked);
}
/**
* Performs an exact search in the tree starting from a node.
*/
void searchLevelExact(ResultSet<DistanceType>& result_set, const ElementType* vec, const NodePtr node, DistanceType mindist, const float epsError)
{
/* If this is a leaf node, then do check and return. */
if ((node->child1 == NULL)&&(node->child2 == NULL)) {
int index = node->divfeat;
DistanceType dist = distance_(dataset_[index], vec, veclen_);
result_set.addPoint(dist,index);
return;
}
/* Which child branch should be taken first? */
ElementType val = vec[node->divfeat];
DistanceType diff = val - node->divval;
NodePtr bestChild = (diff < 0) ? node->child1 : node->child2;
NodePtr otherChild = (diff < 0) ? node->child2 : node->child1;
/* Create a branch record for the branch not taken. Add distance
of this feature boundary (we don't attempt to correct for any
use of this feature in a parent node, which is unlikely to
happen and would have only a small effect). Don't bother
adding more branches to heap after halfway point, as cost of
adding exceeds their value.
*/
DistanceType new_distsq = mindist + distance_.accum_dist(val, node->divval, node->divfeat);
/* Call recursively to search next level down. */
searchLevelExact(result_set, vec, bestChild, mindist, epsError);
if (new_distsq*epsError<=result_set.worstDist()) {
searchLevelExact(result_set, vec, otherChild, new_distsq, epsError);
}
}
private:
enum
{
/**
* To improve efficiency, only SAMPLE_MEAN random values are used to
* compute the mean and variance at each level when building a tree.
* A value of 100 seems to perform as well as using all values.
*/
SAMPLE_MEAN = 100,
/**
* Top random dimensions to consider
*
* When creating random trees, the dimension on which to subdivide is
* selected at random from among the top RAND_DIM dimensions with the
* highest variance. A value of 5 works well.
*/
RAND_DIM=5
};
/**
* Number of randomized trees that are used
*/
int trees_;
/**
* Array of indices to vectors in the dataset.
*/
std::vector<int> vind_;
/**
* The dataset used by this index
*/
const Matrix<ElementType> dataset_;
IndexParams index_params_;
size_t size_;
size_t veclen_;
DistanceType* mean_;
DistanceType* var_;
/**
* Array of k-d trees used to find neighbours.
*/
NodePtr* tree_roots_;
/**
* Pooled memory allocator.
*
* Using a pooled memory allocator is more efficient
* than allocating memory directly when there is a large
* number small of memory allocations.
*/
PooledAllocator pool_;
Distance distance_;
}; // class KDTreeForest
}
//! @endcond
#endif //OPENCV_FLANN_KDTREE_INDEX_H_
@@ -0,0 +1,645 @@
/***********************************************************************
* Software License Agreement (BSD License)
*
* Copyright 2008-2009 Marius Muja (mariusm@cs.ubc.ca). All rights reserved.
* Copyright 2008-2009 David G. Lowe (lowe@cs.ubc.ca). All rights reserved.
*
* THE BSD LICENSE
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*************************************************************************/
#ifndef OPENCV_FLANN_KDTREE_SINGLE_INDEX_H_
#define OPENCV_FLANN_KDTREE_SINGLE_INDEX_H_
//! @cond IGNORED
#include <algorithm>
#include <map>
#include <cstring>
#include "nn_index.h"
#include "matrix.h"
#include "result_set.h"
#include "heap.h"
#include "allocator.h"
#include "random.h"
#include "saving.h"
namespace cvflann
{
struct KDTreeSingleIndexParams : public IndexParams
{
KDTreeSingleIndexParams(int leaf_max_size = 10, bool reorder = true, int dim = -1)
{
(*this)["algorithm"] = FLANN_INDEX_KDTREE_SINGLE;
(*this)["leaf_max_size"] = leaf_max_size;
(*this)["reorder"] = reorder;
(*this)["dim"] = dim;
}
};
/**
* Randomized kd-tree index
*
* Contains the k-d trees and other information for indexing a set of points
* for nearest-neighbor matching.
*/
template <typename Distance>
class KDTreeSingleIndex : public NNIndex<Distance>
{
public:
typedef typename Distance::ElementType ElementType;
typedef typename Distance::ResultType DistanceType;
/**
* KDTree constructor
*
* Params:
* inputData = dataset with the input features
* params = parameters passed to the kdtree algorithm
*/
KDTreeSingleIndex(const Matrix<ElementType>& inputData, const IndexParams& params = KDTreeSingleIndexParams(),
Distance d = Distance() ) :
dataset_(inputData), index_params_(params), distance_(d)
{
size_ = dataset_.rows;
dim_ = dataset_.cols;
root_node_ = 0;
int dim_param = get_param(params,"dim",-1);
if (dim_param>0) dim_ = dim_param;
leaf_max_size_ = get_param(params,"leaf_max_size",10);
reorder_ = get_param(params,"reorder",true);
// Create a permutable array of indices to the input vectors.
vind_.resize(size_);
for (size_t i = 0; i < size_; i++) {
vind_[i] = (int)i;
}
}
KDTreeSingleIndex(const KDTreeSingleIndex&);
KDTreeSingleIndex& operator=(const KDTreeSingleIndex&);
/**
* Standard destructor
*/
~KDTreeSingleIndex()
{
if (reorder_) delete[] data_.data;
}
/**
* Builds the index
*/
void buildIndex() CV_OVERRIDE
{
computeBoundingBox(root_bbox_);
root_node_ = divideTree(0, (int)size_, root_bbox_ ); // construct the tree
if (reorder_) {
delete[] data_.data;
data_ = cvflann::Matrix<ElementType>(new ElementType[size_*dim_], size_, dim_);
for (size_t i=0; i<size_; ++i) {
for (size_t j=0; j<dim_; ++j) {
data_[i][j] = dataset_[vind_[i]][j];
}
}
}
else {
data_ = dataset_;
}
}
flann_algorithm_t getType() const CV_OVERRIDE
{
return FLANN_INDEX_KDTREE_SINGLE;
}
void saveIndex(FILE* stream) CV_OVERRIDE
{
save_value(stream, size_);
save_value(stream, dim_);
save_value(stream, root_bbox_);
save_value(stream, reorder_);
save_value(stream, leaf_max_size_);
save_value(stream, vind_);
if (reorder_) {
save_value(stream, data_);
}
save_tree(stream, root_node_);
}
void loadIndex(FILE* stream) CV_OVERRIDE
{
load_value(stream, size_);
load_value(stream, dim_);
load_value(stream, root_bbox_);
load_value(stream, reorder_);
load_value(stream, leaf_max_size_);
load_value(stream, vind_);
if (reorder_) {
load_value(stream, data_);
}
else {
data_ = dataset_;
}
load_tree(stream, root_node_);
index_params_["algorithm"] = getType();
index_params_["leaf_max_size"] = leaf_max_size_;
index_params_["reorder"] = reorder_;
}
/**
* Returns size of index.
*/
size_t size() const CV_OVERRIDE
{
return size_;
}
/**
* Returns the length of an index feature.
*/
size_t veclen() const CV_OVERRIDE
{
return dim_;
}
/**
* Computes the inde memory usage
* Returns: memory used by the index
*/
int usedMemory() const CV_OVERRIDE
{
return (int)(pool_.usedMemory+pool_.wastedMemory+dataset_.rows*sizeof(int)); // pool memory and vind array memory
}
/**
* \brief Perform k-nearest neighbor search
* \param[in] queries The query points for which to find the nearest neighbors
* \param[out] indices The indices of the nearest neighbors found
* \param[out] dists Distances to the nearest neighbors found
* \param[in] knn Number of nearest neighbors to return
* \param[in] params Search parameters
*/
void knnSearch(const Matrix<ElementType>& queries, Matrix<int>& indices, Matrix<DistanceType>& dists, int knn, const SearchParams& params) CV_OVERRIDE
{
CV_Assert(queries.cols == veclen());
CV_Assert(indices.rows >= queries.rows);
CV_Assert(dists.rows >= queries.rows);
CV_Assert(int(indices.cols) >= knn);
CV_Assert(int(dists.cols) >= knn);
KNNSimpleResultSet<DistanceType> resultSet(knn);
for (size_t i = 0; i < queries.rows; i++) {
resultSet.init(indices[i], dists[i]);
findNeighbors(resultSet, queries[i], params);
}
}
IndexParams getParameters() const CV_OVERRIDE
{
return index_params_;
}
/**
* Find set of nearest neighbors to vec. Their indices are stored inside
* the result object.
*
* Params:
* result = the result object in which the indices of the nearest-neighbors are stored
* vec = the vector for which to search the nearest neighbors
* maxCheck = the maximum number of restarts (in a best-bin-first manner)
*/
void findNeighbors(ResultSet<DistanceType>& result, const ElementType* vec, const SearchParams& searchParams) CV_OVERRIDE
{
float epsError = 1+get_param(searchParams,"eps",0.0f);
std::vector<DistanceType> dists(dim_,0);
DistanceType distsq = computeInitialDistances(vec, dists);
searchLevel(result, vec, root_node_, distsq, dists, epsError);
}
private:
/*--------------------- Internal Data Structures --------------------------*/
struct Node
{
/**
* Indices of points in leaf node
*/
int left, right;
/**
* Dimension used for subdivision.
*/
int divfeat;
/**
* The values used for subdivision.
*/
DistanceType divlow, divhigh;
/**
* The child nodes.
*/
Node* child1, * child2;
};
typedef Node* NodePtr;
struct Interval
{
DistanceType low, high;
};
typedef std::vector<Interval> BoundingBox;
typedef BranchStruct<NodePtr, DistanceType> BranchSt;
typedef BranchSt* Branch;
void save_tree(FILE* stream, NodePtr tree)
{
save_value(stream, *tree);
if (tree->child1!=NULL) {
save_tree(stream, tree->child1);
}
if (tree->child2!=NULL) {
save_tree(stream, tree->child2);
}
}
void load_tree(FILE* stream, NodePtr& tree)
{
tree = pool_.allocate<Node>();
load_value(stream, *tree);
if (tree->child1!=NULL) {
load_tree(stream, tree->child1);
}
if (tree->child2!=NULL) {
load_tree(stream, tree->child2);
}
}
void computeBoundingBox(BoundingBox& bbox)
{
bbox.resize(dim_);
for (size_t i=0; i<dim_; ++i) {
bbox[i].low = (DistanceType)dataset_[0][i];
bbox[i].high = (DistanceType)dataset_[0][i];
}
for (size_t k=1; k<dataset_.rows; ++k) {
for (size_t i=0; i<dim_; ++i) {
if (dataset_[k][i]<bbox[i].low) bbox[i].low = (DistanceType)dataset_[k][i];
if (dataset_[k][i]>bbox[i].high) bbox[i].high = (DistanceType)dataset_[k][i];
}
}
}
/**
* Create a tree node that subdivides the list of vecs from vind[first]
* to vind[last]. The routine is called recursively on each sublist.
* Place a pointer to this new tree node in the location pTree.
*
* Params: pTree = the new node to create
* first = index of the first vector
* last = index of the last vector
*/
NodePtr divideTree(int left, int right, BoundingBox& bbox)
{
NodePtr node = pool_.allocate<Node>(); // allocate memory
/* If too few exemplars remain, then make this a leaf node. */
if ( (right-left) <= leaf_max_size_) {
node->child1 = node->child2 = NULL; /* Mark as leaf node. */
node->left = left;
node->right = right;
// compute bounding-box of leaf points
for (size_t i=0; i<dim_; ++i) {
bbox[i].low = (DistanceType)dataset_[vind_[left]][i];
bbox[i].high = (DistanceType)dataset_[vind_[left]][i];
}
for (int k=left+1; k<right; ++k) {
for (size_t i=0; i<dim_; ++i) {
if (bbox[i].low>dataset_[vind_[k]][i]) bbox[i].low=(DistanceType)dataset_[vind_[k]][i];
if (bbox[i].high<dataset_[vind_[k]][i]) bbox[i].high=(DistanceType)dataset_[vind_[k]][i];
}
}
}
else {
int idx;
int cutfeat;
DistanceType cutval;
middleSplit_(&vind_[0]+left, right-left, idx, cutfeat, cutval, bbox);
node->divfeat = cutfeat;
BoundingBox left_bbox(bbox);
left_bbox[cutfeat].high = cutval;
node->child1 = divideTree(left, left+idx, left_bbox);
BoundingBox right_bbox(bbox);
right_bbox[cutfeat].low = cutval;
node->child2 = divideTree(left+idx, right, right_bbox);
node->divlow = left_bbox[cutfeat].high;
node->divhigh = right_bbox[cutfeat].low;
for (size_t i=0; i<dim_; ++i) {
bbox[i].low = std::min(left_bbox[i].low, right_bbox[i].low);
bbox[i].high = std::max(left_bbox[i].high, right_bbox[i].high);
}
}
return node;
}
void computeMinMax(int* ind, int count, int dim, ElementType& min_elem, ElementType& max_elem)
{
min_elem = dataset_[ind[0]][dim];
max_elem = dataset_[ind[0]][dim];
for (int i=1; i<count; ++i) {
ElementType val = dataset_[ind[i]][dim];
if (val<min_elem) min_elem = val;
if (val>max_elem) max_elem = val;
}
}
void middleSplit(int* ind, int count, int& index, int& cutfeat, DistanceType& cutval, const BoundingBox& bbox)
{
// find the largest span from the approximate bounding box
ElementType max_span = bbox[0].high-bbox[0].low;
cutfeat = 0;
cutval = (bbox[0].high+bbox[0].low)/2;
for (size_t i=1; i<dim_; ++i) {
ElementType span = bbox[i].high-bbox[i].low;
if (span>max_span) {
max_span = span;
cutfeat = i;
cutval = (bbox[i].high+bbox[i].low)/2;
}
}
// compute exact span on the found dimension
ElementType min_elem, max_elem;
computeMinMax(ind, count, cutfeat, min_elem, max_elem);
cutval = (min_elem+max_elem)/2;
max_span = max_elem - min_elem;
// check if a dimension of a largest span exists
size_t k = cutfeat;
for (size_t i=0; i<dim_; ++i) {
if (i==k) continue;
ElementType span = bbox[i].high-bbox[i].low;
if (span>max_span) {
computeMinMax(ind, count, i, min_elem, max_elem);
span = max_elem - min_elem;
if (span>max_span) {
max_span = span;
cutfeat = i;
cutval = (min_elem+max_elem)/2;
}
}
}
int lim1, lim2;
planeSplit(ind, count, cutfeat, cutval, lim1, lim2);
if (lim1>count/2) index = lim1;
else if (lim2<count/2) index = lim2;
else index = count/2;
}
void middleSplit_(int* ind, int count, int& index, int& cutfeat, DistanceType& cutval, const BoundingBox& bbox)
{
const float EPS=0.00001f;
DistanceType max_span = bbox[0].high-bbox[0].low;
for (size_t i=1; i<dim_; ++i) {
DistanceType span = bbox[i].high-bbox[i].low;
if (span>max_span) {
max_span = span;
}
}
DistanceType max_spread = -1;
cutfeat = 0;
for (size_t i=0; i<dim_; ++i) {
DistanceType span = bbox[i].high-bbox[i].low;
if (span>(DistanceType)((1-EPS)*max_span)) {
ElementType min_elem, max_elem;
computeMinMax(ind, count, (int)i, min_elem, max_elem);
DistanceType spread = (DistanceType)(max_elem-min_elem);
if (spread>max_spread) {
cutfeat = (int)i;
max_spread = spread;
}
}
}
// split in the middle
DistanceType split_val = (bbox[cutfeat].low+bbox[cutfeat].high)/2;
ElementType min_elem, max_elem;
computeMinMax(ind, count, cutfeat, min_elem, max_elem);
if (split_val<min_elem) cutval = (DistanceType)min_elem;
else if (split_val>max_elem) cutval = (DistanceType)max_elem;
else cutval = split_val;
int lim1, lim2;
planeSplit(ind, count, cutfeat, cutval, lim1, lim2);
if (lim1>count/2) index = lim1;
else if (lim2<count/2) index = lim2;
else index = count/2;
}
/**
* Subdivide the list of points by a plane perpendicular on axe corresponding
* to the 'cutfeat' dimension at 'cutval' position.
*
* On return:
* dataset[ind[0..lim1-1]][cutfeat]<cutval
* dataset[ind[lim1..lim2-1]][cutfeat]==cutval
* dataset[ind[lim2..count]][cutfeat]>cutval
*/
void planeSplit(int* ind, int count, int cutfeat, DistanceType cutval, int& lim1, int& lim2)
{
/* Move vector indices for left subtree to front of list. */
int left = 0;
int right = count-1;
for (;; ) {
while (left<=right && dataset_[ind[left]][cutfeat]<cutval) ++left;
while (left<=right && dataset_[ind[right]][cutfeat]>=cutval) --right;
if (left>right) break;
std::swap(ind[left], ind[right]); ++left; --right;
}
/* If either list is empty, it means that all remaining features
* are identical. Split in the middle to maintain a balanced tree.
*/
lim1 = left;
right = count-1;
for (;; ) {
while (left<=right && dataset_[ind[left]][cutfeat]<=cutval) ++left;
while (left<=right && dataset_[ind[right]][cutfeat]>cutval) --right;
if (left>right) break;
std::swap(ind[left], ind[right]); ++left; --right;
}
lim2 = left;
}
DistanceType computeInitialDistances(const ElementType* vec, std::vector<DistanceType>& dists)
{
DistanceType distsq = 0.0;
for (size_t i = 0; i < dim_; ++i) {
if (vec[i] < root_bbox_[i].low) {
dists[i] = distance_.accum_dist(vec[i], root_bbox_[i].low, (int)i);
distsq += dists[i];
}
if (vec[i] > root_bbox_[i].high) {
dists[i] = distance_.accum_dist(vec[i], root_bbox_[i].high, (int)i);
distsq += dists[i];
}
}
return distsq;
}
/**
* Performs an exact search in the tree starting from a node.
*/
void searchLevel(ResultSet<DistanceType>& result_set, const ElementType* vec, const NodePtr node, DistanceType mindistsq,
std::vector<DistanceType>& dists, const float epsError)
{
/* If this is a leaf node, then do check and return. */
if ((node->child1 == NULL)&&(node->child2 == NULL)) {
DistanceType worst_dist = result_set.worstDist();
if (reorder_) {
for (int i=node->left; i<node->right; ++i) {
DistanceType dist = distance_(vec, data_[i], dim_, worst_dist);
if (dist<worst_dist) {
result_set.addPoint(dist,vind_[i]);
}
}
} else {
for (int i=node->left; i<node->right; ++i) {
DistanceType dist = distance_(vec, data_[vind_[i]], dim_, worst_dist);
if (dist<worst_dist) {
result_set.addPoint(dist,vind_[i]);
}
}
}
return;
}
/* Which child branch should be taken first? */
int idx = node->divfeat;
ElementType val = vec[idx];
DistanceType diff1 = val - node->divlow;
DistanceType diff2 = val - node->divhigh;
NodePtr bestChild;
NodePtr otherChild;
DistanceType cut_dist;
if ((diff1+diff2)<0) {
bestChild = node->child1;
otherChild = node->child2;
cut_dist = distance_.accum_dist(val, node->divhigh, idx);
}
else {
bestChild = node->child2;
otherChild = node->child1;
cut_dist = distance_.accum_dist( val, node->divlow, idx);
}
/* Call recursively to search next level down. */
searchLevel(result_set, vec, bestChild, mindistsq, dists, epsError);
DistanceType dst = dists[idx];
mindistsq = mindistsq + cut_dist - dst;
dists[idx] = cut_dist;
if (mindistsq*epsError<=result_set.worstDist()) {
searchLevel(result_set, vec, otherChild, mindistsq, dists, epsError);
}
dists[idx] = dst;
}
private:
/**
* The dataset used by this index
*/
const Matrix<ElementType> dataset_;
IndexParams index_params_;
int leaf_max_size_;
bool reorder_;
/**
* Array of indices to vectors in the dataset.
*/
std::vector<int> vind_;
Matrix<ElementType> data_;
size_t size_;
size_t dim_;
/**
* Array of k-d trees used to find neighbours.
*/
NodePtr root_node_;
BoundingBox root_bbox_;
/**
* Pooled memory allocator.
*
* Using a pooled memory allocator is more efficient
* than allocating memory directly when there is a large
* number small of memory allocations.
*/
PooledAllocator pool_;
Distance distance_;
}; // class KDTree
}
//! @endcond
#endif //OPENCV_FLANN_KDTREE_SINGLE_INDEX_H_
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,135 @@
/***********************************************************************
* Software License Agreement (BSD License)
*
* Copyright 2008-2009 Marius Muja (mariusm@cs.ubc.ca). All rights reserved.
* Copyright 2008-2009 David G. Lowe (lowe@cs.ubc.ca). All rights reserved.
*
* THE BSD LICENSE
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*************************************************************************/
#ifndef OPENCV_FLANN_LINEAR_INDEX_H_
#define OPENCV_FLANN_LINEAR_INDEX_H_
//! @cond IGNORED
#include "nn_index.h"
namespace cvflann
{
struct LinearIndexParams : public IndexParams
{
LinearIndexParams()
{
(* this)["algorithm"] = FLANN_INDEX_LINEAR;
}
};
template <typename Distance>
class LinearIndex : public NNIndex<Distance>
{
public:
typedef typename Distance::ElementType ElementType;
typedef typename Distance::ResultType DistanceType;
LinearIndex(const Matrix<ElementType>& inputData, const IndexParams& params = LinearIndexParams(),
Distance d = Distance()) :
dataset_(inputData), index_params_(params), distance_(d)
{
}
LinearIndex(const LinearIndex&);
LinearIndex& operator=(const LinearIndex&);
flann_algorithm_t getType() const CV_OVERRIDE
{
return FLANN_INDEX_LINEAR;
}
size_t size() const CV_OVERRIDE
{
return dataset_.rows;
}
size_t veclen() const CV_OVERRIDE
{
return dataset_.cols;
}
int usedMemory() const CV_OVERRIDE
{
return 0;
}
void buildIndex() CV_OVERRIDE
{
/* nothing to do here for linear search */
}
void saveIndex(FILE*) CV_OVERRIDE
{
/* nothing to do here for linear search */
}
void loadIndex(FILE*) CV_OVERRIDE
{
/* nothing to do here for linear search */
index_params_["algorithm"] = getType();
}
void findNeighbors(ResultSet<DistanceType>& resultSet, const ElementType* vec, const SearchParams& /*searchParams*/) CV_OVERRIDE
{
ElementType* data = dataset_.data;
for (size_t i = 0; i < dataset_.rows; ++i, data += dataset_.cols) {
DistanceType dist = distance_(data, vec, dataset_.cols);
resultSet.addPoint(dist, (int)i);
}
}
IndexParams getParameters() const CV_OVERRIDE
{
return index_params_;
}
private:
/** The dataset */
const Matrix<ElementType> dataset_;
/** Index parameters */
IndexParams index_params_;
/** Index distance */
Distance distance_;
};
}
//! @endcond
#endif // OPENCV_FLANN_LINEAR_INDEX_H_
@@ -0,0 +1,139 @@
/***********************************************************************
* Software License Agreement (BSD License)
*
* Copyright 2008-2009 Marius Muja (mariusm@cs.ubc.ca). All rights reserved.
* Copyright 2008-2009 David G. Lowe (lowe@cs.ubc.ca). All rights reserved.
*
* THE BSD LICENSE
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*************************************************************************/
#ifndef OPENCV_FLANN_LOGGER_H
#define OPENCV_FLANN_LOGGER_H
//! @cond IGNORED
#include <stdio.h>
#include <stdarg.h>
#include "defines.h"
namespace cvflann
{
class Logger
{
Logger() : stream(stdout), logLevel(FLANN_LOG_WARN) {}
~Logger()
{
if ((stream!=NULL)&&(stream!=stdout)) {
fclose(stream);
}
}
static Logger& instance()
{
static Logger logger;
return logger;
}
void _setDestination(const char* name)
{
if (name==NULL) {
stream = stdout;
}
else {
#ifdef _MSC_VER
if (fopen_s(&stream, name, "w") != 0)
stream = NULL;
#else
stream = fopen(name,"w");
#endif
if (stream == NULL) {
stream = stdout;
}
}
}
int _log(int level, const char* fmt, va_list arglist)
{
if (level > logLevel ) return -1;
int ret = vfprintf(stream, fmt, arglist);
return ret;
}
public:
/**
* Sets the logging level. All messages with lower priority will be ignored.
* @param level Logging level
*/
static void setLevel(int level) { instance().logLevel = level; }
/**
* Sets the logging destination
* @param name Filename or NULL for console
*/
static void setDestination(const char* name) { instance()._setDestination(name); }
/**
* Print log message
* @param level Log level
* @param fmt Message format
* @return
*/
static int log(int level, const char* fmt, ...)
{
va_list arglist;
va_start(arglist, fmt);
int ret = instance()._log(level,fmt,arglist);
va_end(arglist);
return ret;
}
#define LOG_METHOD(NAME,LEVEL) \
static int NAME(const char* fmt, ...) \
{ \
va_list ap; \
va_start(ap, fmt); \
int ret = instance()._log(LEVEL, fmt, ap); \
va_end(ap); \
return ret; \
}
LOG_METHOD(fatal, FLANN_LOG_FATAL)
LOG_METHOD(error, FLANN_LOG_ERROR)
LOG_METHOD(warn, FLANN_LOG_WARN)
LOG_METHOD(info, FLANN_LOG_INFO)
private:
FILE* stream;
int logLevel;
};
}
//! @endcond
#endif //OPENCV_FLANN_LOGGER_H
@@ -0,0 +1,403 @@
/***********************************************************************
* Software License Agreement (BSD License)
*
* Copyright 2008-2009 Marius Muja (mariusm@cs.ubc.ca). All rights reserved.
* Copyright 2008-2009 David G. Lowe (lowe@cs.ubc.ca). All rights reserved.
*
* THE BSD LICENSE
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*************************************************************************/
/***********************************************************************
* Author: Vincent Rabaud
*************************************************************************/
#ifndef OPENCV_FLANN_LSH_INDEX_H_
#define OPENCV_FLANN_LSH_INDEX_H_
//! @cond IGNORED
#include <algorithm>
#include <cstring>
#include <map>
#include <vector>
#include "nn_index.h"
#include "matrix.h"
#include "result_set.h"
#include "heap.h"
#include "lsh_table.h"
#include "allocator.h"
#include "random.h"
#include "saving.h"
#ifdef _MSC_VER
#pragma warning(push)
#pragma warning(disable: 4702) //disable unreachable code
#endif
namespace cvflann
{
struct LshIndexParams : public IndexParams
{
LshIndexParams(int table_number = 12, int key_size = 20, int multi_probe_level = 2)
{
(*this)["algorithm"] = FLANN_INDEX_LSH;
// The number of hash tables to use
(*this)["table_number"] = table_number;
// The length of the key in the hash tables
(*this)["key_size"] = key_size;
// Number of levels to use in multi-probe (0 for standard LSH)
(*this)["multi_probe_level"] = multi_probe_level;
}
};
/**
* Locality-sensitive hashing index
*
* Contains the tables and other information for indexing a set of points
* for nearest-neighbor matching.
*/
template<typename Distance>
class LshIndex : public NNIndex<Distance>
{
public:
typedef typename Distance::ElementType ElementType;
typedef typename Distance::ResultType DistanceType;
/** Constructor
* @param input_data dataset with the input features
* @param params parameters passed to the LSH algorithm
* @param d the distance used
*/
LshIndex(const Matrix<ElementType>& input_data, const IndexParams& params = LshIndexParams(),
Distance d = Distance()) :
dataset_(input_data), index_params_(params), distance_(d)
{
// cv::flann::IndexParams sets integer params as 'int', so it is used with get_param
// in place of 'unsigned int'
table_number_ = get_param(index_params_,"table_number",12);
key_size_ = get_param(index_params_,"key_size",20);
multi_probe_level_ = get_param(index_params_,"multi_probe_level",2);
feature_size_ = (unsigned)dataset_.cols;
fill_xor_mask(0, key_size_, multi_probe_level_, xor_masks_);
}
LshIndex(const LshIndex&);
LshIndex& operator=(const LshIndex&);
/**
* Builds the index
*/
void buildIndex() CV_OVERRIDE
{
tables_.resize(table_number_);
for (int i = 0; i < table_number_; ++i) {
lsh::LshTable<ElementType>& table = tables_[i];
table = lsh::LshTable<ElementType>(feature_size_, key_size_);
// Add the features to the table
table.add(dataset_);
}
}
flann_algorithm_t getType() const CV_OVERRIDE
{
return FLANN_INDEX_LSH;
}
void saveIndex(FILE* stream) CV_OVERRIDE
{
save_value(stream,table_number_);
save_value(stream,key_size_);
save_value(stream,multi_probe_level_);
save_value(stream, dataset_);
}
void loadIndex(FILE* stream) CV_OVERRIDE
{
load_value(stream, table_number_);
load_value(stream, key_size_);
load_value(stream, multi_probe_level_);
load_value(stream, dataset_);
// Building the index is so fast we can afford not storing it
buildIndex();
index_params_["algorithm"] = getType();
index_params_["table_number"] = table_number_;
index_params_["key_size"] = key_size_;
index_params_["multi_probe_level"] = multi_probe_level_;
}
/**
* Returns size of index.
*/
size_t size() const CV_OVERRIDE
{
return dataset_.rows;
}
/**
* Returns the length of an index feature.
*/
size_t veclen() const CV_OVERRIDE
{
return feature_size_;
}
/**
* Computes the index memory usage
* Returns: memory used by the index
*/
int usedMemory() const CV_OVERRIDE
{
return (int)(dataset_.rows * sizeof(int));
}
IndexParams getParameters() const CV_OVERRIDE
{
return index_params_;
}
/**
* \brief Perform k-nearest neighbor search
* \param[in] queries The query points for which to find the nearest neighbors
* \param[out] indices The indices of the nearest neighbors found
* \param[out] dists Distances to the nearest neighbors found
* \param[in] knn Number of nearest neighbors to return
* \param[in] params Search parameters
*/
virtual void knnSearch(const Matrix<ElementType>& queries, Matrix<int>& indices, Matrix<DistanceType>& dists, int knn, const SearchParams& params) CV_OVERRIDE
{
CV_Assert(queries.cols == veclen());
CV_Assert(indices.rows >= queries.rows);
CV_Assert(dists.rows >= queries.rows);
CV_Assert(int(indices.cols) >= knn);
CV_Assert(int(dists.cols) >= knn);
KNNUniqueResultSet<DistanceType> resultSet(knn);
for (size_t i = 0; i < queries.rows; i++) {
resultSet.clear();
std::fill_n(indices[i], knn, -1);
std::fill_n(dists[i], knn, std::numeric_limits<DistanceType>::max());
findNeighbors(resultSet, queries[i], params);
if (get_param(params,"sorted",true)) resultSet.sortAndCopy(indices[i], dists[i], knn);
else resultSet.copy(indices[i], dists[i], knn);
}
}
/**
* Find set of nearest neighbors to vec. Their indices are stored inside
* the result object.
*
* Params:
* result = the result object in which the indices of the nearest-neighbors are stored
* vec = the vector for which to search the nearest neighbors
* maxCheck = the maximum number of restarts (in a best-bin-first manner)
*/
void findNeighbors(ResultSet<DistanceType>& result, const ElementType* vec, const SearchParams& /*searchParams*/) CV_OVERRIDE
{
getNeighbors(vec, result);
}
private:
/** Defines the comparator on score and index
*/
typedef std::pair<float, unsigned int> ScoreIndexPair;
struct SortScoreIndexPairOnSecond
{
bool operator()(const ScoreIndexPair& left, const ScoreIndexPair& right) const
{
return left.second < right.second;
}
};
/** Fills the different xor masks to use when getting the neighbors in multi-probe LSH
* @param key the key we build neighbors from
* @param lowest_index the lowest index of the bit set
* @param level the multi-probe level we are at
* @param xor_masks all the xor mask
*/
void fill_xor_mask(lsh::BucketKey key, int lowest_index, unsigned int level,
std::vector<lsh::BucketKey>& xor_masks)
{
xor_masks.push_back(key);
if (level == 0) return;
for (int index = lowest_index - 1; index >= 0; --index) {
// Create a new key
lsh::BucketKey new_key = key | (1 << index);
fill_xor_mask(new_key, index, level - 1, xor_masks);
}
}
/** Performs the approximate nearest-neighbor search.
* @param vec the feature to analyze
* @param do_radius flag indicating if we check the radius too
* @param radius the radius if it is a radius search
* @param do_k flag indicating if we limit the number of nn
* @param k_nn the number of nearest neighbors
* @param checked_average used for debugging
*/
void getNeighbors(const ElementType* vec, bool /*do_radius*/, float radius, bool do_k, unsigned int k_nn,
float& /*checked_average*/)
{
static std::vector<ScoreIndexPair> score_index_heap;
if (do_k) {
unsigned int worst_score = std::numeric_limits<unsigned int>::max();
typename std::vector<lsh::LshTable<ElementType> >::const_iterator table = tables_.begin();
typename std::vector<lsh::LshTable<ElementType> >::const_iterator table_end = tables_.end();
for (; table != table_end; ++table) {
size_t key = table->getKey(vec);
std::vector<lsh::BucketKey>::const_iterator xor_mask = xor_masks_.begin();
std::vector<lsh::BucketKey>::const_iterator xor_mask_end = xor_masks_.end();
for (; xor_mask != xor_mask_end; ++xor_mask) {
size_t sub_key = key ^ (*xor_mask);
const lsh::Bucket* bucket = table->getBucketFromKey(sub_key);
if (bucket == 0) continue;
// Go over each descriptor index
std::vector<lsh::FeatureIndex>::const_iterator training_index = bucket->begin();
std::vector<lsh::FeatureIndex>::const_iterator last_training_index = bucket->end();
DistanceType hamming_distance;
// Process the rest of the candidates
for (; training_index < last_training_index; ++training_index) {
hamming_distance = distance_(vec, dataset_[*training_index], dataset_.cols);
if (hamming_distance < worst_score) {
// Insert the new element
score_index_heap.push_back(ScoreIndexPair(hamming_distance, training_index));
std::push_heap(score_index_heap.begin(), score_index_heap.end());
if (score_index_heap.size() > (unsigned int)k_nn) {
// Remove the highest distance value as we have too many elements
std::pop_heap(score_index_heap.begin(), score_index_heap.end());
score_index_heap.pop_back();
// Keep track of the worst score
worst_score = score_index_heap.front().first;
}
}
}
}
}
}
else {
typename std::vector<lsh::LshTable<ElementType> >::const_iterator table = tables_.begin();
typename std::vector<lsh::LshTable<ElementType> >::const_iterator table_end = tables_.end();
for (; table != table_end; ++table) {
size_t key = table->getKey(vec);
std::vector<lsh::BucketKey>::const_iterator xor_mask = xor_masks_.begin();
std::vector<lsh::BucketKey>::const_iterator xor_mask_end = xor_masks_.end();
for (; xor_mask != xor_mask_end; ++xor_mask) {
size_t sub_key = key ^ (*xor_mask);
const lsh::Bucket* bucket = table->getBucketFromKey(sub_key);
if (bucket == 0) continue;
// Go over each descriptor index
std::vector<lsh::FeatureIndex>::const_iterator training_index = bucket->begin();
std::vector<lsh::FeatureIndex>::const_iterator last_training_index = bucket->end();
DistanceType hamming_distance;
// Process the rest of the candidates
for (; training_index < last_training_index; ++training_index) {
// Compute the Hamming distance
hamming_distance = distance_(vec, dataset_[*training_index], dataset_.cols);
if (hamming_distance < radius) score_index_heap.push_back(ScoreIndexPair(hamming_distance, training_index));
}
}
}
}
}
/** Performs the approximate nearest-neighbor search.
* This is a slower version than the above as it uses the ResultSet
* @param vec the feature to analyze
*/
void getNeighbors(const ElementType* vec, ResultSet<DistanceType>& result)
{
typename std::vector<lsh::LshTable<ElementType> >::const_iterator table = tables_.begin();
typename std::vector<lsh::LshTable<ElementType> >::const_iterator table_end = tables_.end();
for (; table != table_end; ++table) {
size_t key = table->getKey(vec);
std::vector<lsh::BucketKey>::const_iterator xor_mask = xor_masks_.begin();
std::vector<lsh::BucketKey>::const_iterator xor_mask_end = xor_masks_.end();
for (; xor_mask != xor_mask_end; ++xor_mask) {
size_t sub_key = key ^ (*xor_mask);
const lsh::Bucket* bucket = table->getBucketFromKey((lsh::BucketKey)sub_key);
if (bucket == 0) continue;
// Go over each descriptor index
std::vector<lsh::FeatureIndex>::const_iterator training_index = bucket->begin();
std::vector<lsh::FeatureIndex>::const_iterator last_training_index = bucket->end();
DistanceType hamming_distance;
// Process the rest of the candidates
for (; training_index < last_training_index; ++training_index) {
// Compute the Hamming distance
hamming_distance = distance_(vec, dataset_[*training_index], (int)dataset_.cols);
result.addPoint(hamming_distance, *training_index);
}
}
}
}
/** The different hash tables */
std::vector<lsh::LshTable<ElementType> > tables_;
/** The data the LSH tables where built from */
Matrix<ElementType> dataset_;
/** The size of the features (as ElementType[]) */
unsigned int feature_size_;
IndexParams index_params_;
/** table number */
int table_number_;
/** key size */
int key_size_;
/** How far should we look for neighbors in multi-probe LSH */
int multi_probe_level_;
/** The XOR masks to apply to a key to get the neighboring buckets */
std::vector<lsh::BucketKey> xor_masks_;
Distance distance_;
};
}
#ifdef _MSC_VER
#pragma warning(pop)
#endif
//! @endcond
#endif //OPENCV_FLANN_LSH_INDEX_H_
@@ -0,0 +1,525 @@
/***********************************************************************
* Software License Agreement (BSD License)
*
* Copyright 2008-2009 Marius Muja (mariusm@cs.ubc.ca). All rights reserved.
* Copyright 2008-2009 David G. Lowe (lowe@cs.ubc.ca). All rights reserved.
*
* THE BSD LICENSE
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*************************************************************************/
/***********************************************************************
* Author: Vincent Rabaud
*************************************************************************/
#ifndef OPENCV_FLANN_LSH_TABLE_H_
#define OPENCV_FLANN_LSH_TABLE_H_
//! @cond IGNORED
#include <algorithm>
#include <iostream>
#include <iomanip>
#include <limits.h>
// TODO as soon as we use C++0x, use the code in USE_UNORDERED_MAP
#ifdef __GXX_EXPERIMENTAL_CXX0X__
# define USE_UNORDERED_MAP 1
#else
# define USE_UNORDERED_MAP 0
#endif
#if USE_UNORDERED_MAP
#include <unordered_map>
#else
#include <map>
#endif
#include <math.h>
#include <stddef.h>
#include "dynamic_bitset.h"
#include "matrix.h"
#ifdef _MSC_VER
#pragma warning(push)
#pragma warning(disable: 4702) //disable unreachable code
#endif
namespace cvflann
{
namespace lsh
{
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/** What is stored in an LSH bucket
*/
typedef uint32_t FeatureIndex;
/** The id from which we can get a bucket back in an LSH table
*/
typedef unsigned int BucketKey;
/** A bucket in an LSH table
*/
typedef std::vector<FeatureIndex> Bucket;
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/** POD for stats about an LSH table
*/
struct LshStats
{
std::vector<unsigned int> bucket_sizes_;
size_t n_buckets_;
size_t bucket_size_mean_;
size_t bucket_size_median_;
size_t bucket_size_min_;
size_t bucket_size_max_;
size_t bucket_size_std_dev;
/** Each contained vector contains three value: beginning/end for interval, number of elements in the bin
*/
std::vector<std::vector<unsigned int> > size_histogram_;
};
/** Overload the << operator for LshStats
* @param out the streams
* @param stats the stats to display
* @return the streams
*/
inline std::ostream& operator <<(std::ostream& out, const LshStats& stats)
{
int w = 20;
out << "Lsh Table Stats:\n" << std::setw(w) << std::setiosflags(std::ios::right) << "N buckets : "
<< stats.n_buckets_ << "\n" << std::setw(w) << std::setiosflags(std::ios::right) << "mean size : "
<< std::setiosflags(std::ios::left) << stats.bucket_size_mean_ << "\n" << std::setw(w)
<< std::setiosflags(std::ios::right) << "median size : " << stats.bucket_size_median_ << "\n" << std::setw(w)
<< std::setiosflags(std::ios::right) << "min size : " << std::setiosflags(std::ios::left)
<< stats.bucket_size_min_ << "\n" << std::setw(w) << std::setiosflags(std::ios::right) << "max size : "
<< std::setiosflags(std::ios::left) << stats.bucket_size_max_;
// Display the histogram
out << std::endl << std::setw(w) << std::setiosflags(std::ios::right) << "histogram : "
<< std::setiosflags(std::ios::left);
for (std::vector<std::vector<unsigned int> >::const_iterator iterator = stats.size_histogram_.begin(), end =
stats.size_histogram_.end(); iterator != end; ++iterator) out << (*iterator)[0] << "-" << (*iterator)[1] << ": " << (*iterator)[2] << ", ";
return out;
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/** Lsh hash table. As its key is a sub-feature, and as usually
* the size of it is pretty small, we keep it as a continuous memory array.
* The value is an index in the corpus of features (we keep it as an unsigned
* int for pure memory reasons, it could be a size_t)
*/
template<typename ElementType>
class LshTable
{
public:
/** A container of all the feature indices. Optimized for space
*/
#if USE_UNORDERED_MAP
typedef std::unordered_map<BucketKey, Bucket> BucketsSpace;
#else
typedef std::map<BucketKey, Bucket> BucketsSpace;
#endif
/** A container of all the feature indices. Optimized for speed
*/
typedef std::vector<Bucket> BucketsSpeed;
/** Default constructor
*/
LshTable()
{
key_size_ = 0;
feature_size_ = 0;
speed_level_ = kArray;
}
/** Default constructor
* Create the mask and allocate the memory
* @param feature_size is the size of the feature (considered as a ElementType[])
* @param key_size is the number of bits that are turned on in the feature
*/
LshTable(unsigned int feature_size, unsigned int key_size)
{
feature_size_ = feature_size;
CV_UNUSED(key_size);
CV_Error(cv::Error::StsUnsupportedFormat, "LSH is not implemented for that type" );
}
/** Add a feature to the table
* @param value the value to store for that feature
* @param feature the feature itself
*/
void add(unsigned int value, const ElementType* feature)
{
// Add the value to the corresponding bucket
BucketKey key = (lsh::BucketKey)getKey(feature);
switch (speed_level_) {
case kArray:
// That means we get the buckets from an array
buckets_speed_[key].push_back(value);
break;
case kBitsetHash:
// That means we can check the bitset for the presence of a key
key_bitset_.set(key);
buckets_space_[key].push_back(value);
break;
case kHash:
{
// That means we have to check for the hash table for the presence of a key
buckets_space_[key].push_back(value);
break;
}
}
}
/** Add a set of features to the table
* @param dataset the values to store
*/
void add(Matrix<ElementType> dataset)
{
#if USE_UNORDERED_MAP
buckets_space_.rehash((buckets_space_.size() + dataset.rows) * 1.2);
#endif
// Add the features to the table
for (unsigned int i = 0; i < dataset.rows; ++i) add(i, dataset[i]);
// Now that the table is full, optimize it for speed/space
optimize();
}
/** Get a bucket given the key
* @param key
* @return
*/
inline const Bucket* getBucketFromKey(BucketKey key) const
{
// Generate other buckets
switch (speed_level_) {
case kArray:
// That means we get the buckets from an array
return &buckets_speed_[key];
break;
case kBitsetHash:
// That means we can check the bitset for the presence of a key
if (key_bitset_.test(key)) return &buckets_space_.find(key)->second;
else return 0;
break;
case kHash:
{
// That means we have to check for the hash table for the presence of a key
BucketsSpace::const_iterator bucket_it, bucket_end = buckets_space_.end();
bucket_it = buckets_space_.find(key);
// Stop here if that bucket does not exist
if (bucket_it == bucket_end) return 0;
else return &bucket_it->second;
break;
}
}
return 0;
}
/** Compute the sub-signature of a feature
*/
size_t getKey(const ElementType* /*feature*/) const
{
CV_Error(cv::Error::StsUnsupportedFormat, "LSH is not implemented for that type" );
return 0;
}
/** Get statistics about the table
* @return
*/
LshStats getStats() const;
private:
/** defines the speed fo the implementation
* kArray uses a vector for storing data
* kBitsetHash uses a hash map but checks for the validity of a key with a bitset
* kHash uses a hash map only
*/
enum SpeedLevel
{
kArray, kBitsetHash, kHash
};
/** Initialize some variables
*/
void initialize(size_t key_size)
{
const size_t key_size_lower_bound = 1;
//a value (size_t(1) << key_size) must fit the size_t type so key_size has to be strictly less than size of size_t
const size_t key_size_upper_bound = (std::min)(sizeof(BucketKey) * CHAR_BIT + 1, sizeof(size_t) * CHAR_BIT);
if (key_size < key_size_lower_bound || key_size >= key_size_upper_bound)
{
CV_Error(cv::Error::StsBadArg, cv::format("Invalid key_size (=%d). Valid values for your system are %d <= key_size < %d.", (int)key_size, (int)key_size_lower_bound, (int)key_size_upper_bound));
}
speed_level_ = kHash;
key_size_ = (unsigned)key_size;
}
/** Optimize the table for speed/space
*/
void optimize()
{
// If we are already using the fast storage, no need to do anything
if (speed_level_ == kArray) return;
// Use an array if it will be more than half full
if (buckets_space_.size() > ((size_t(1) << key_size_) / 2)) {
speed_level_ = kArray;
// Fill the array version of it
buckets_speed_.resize(size_t(1) << key_size_);
for (BucketsSpace::const_iterator key_bucket = buckets_space_.begin(); key_bucket != buckets_space_.end(); ++key_bucket) buckets_speed_[key_bucket->first] = key_bucket->second;
// Empty the hash table
buckets_space_.clear();
return;
}
// If the bitset is going to use less than 10% of the RAM of the hash map (at least 1 size_t for the key and two
// for the vector) or less than 512MB (key_size_ <= 30)
if (((std::max(buckets_space_.size(), buckets_speed_.size()) * CHAR_BIT * 3 * sizeof(BucketKey)) / 10
>= (size_t(1) << key_size_)) || (key_size_ <= 32)) {
speed_level_ = kBitsetHash;
key_bitset_.resize(size_t(1) << key_size_);
key_bitset_.reset();
// Try with the BucketsSpace
for (BucketsSpace::const_iterator key_bucket = buckets_space_.begin(); key_bucket != buckets_space_.end(); ++key_bucket) key_bitset_.set(key_bucket->first);
}
else {
speed_level_ = kHash;
key_bitset_.clear();
}
}
/** The vector of all the buckets if they are held for speed
*/
BucketsSpeed buckets_speed_;
/** The hash table of all the buckets in case we cannot use the speed version
*/
BucketsSpace buckets_space_;
/** What is used to store the data */
SpeedLevel speed_level_;
/** If the subkey is small enough, it will keep track of which subkeys are set through that bitset
* That is just a speedup so that we don't look in the hash table (which can be mush slower that checking a bitset)
*/
DynamicBitset key_bitset_;
/** The size of the sub-signature in bits
*/
unsigned int key_size_;
unsigned int feature_size_;
// Members only used for the unsigned char specialization
/** The mask to apply to a feature to get the hash key
* Only used in the unsigned char case
*/
std::vector<size_t> mask_;
};
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Specialization for unsigned char
template<>
inline LshTable<unsigned char>::LshTable(unsigned int feature_size, unsigned int subsignature_size)
{
feature_size_ = feature_size;
initialize(subsignature_size);
// Allocate the mask
mask_ = std::vector<size_t>((feature_size * sizeof(char) + sizeof(size_t) - 1) / sizeof(size_t), 0);
// A bit brutal but fast to code
std::vector<int> indices(feature_size * CHAR_BIT);
for (size_t i = 0; i < feature_size * CHAR_BIT; ++i) indices[i] = (int)i;
#ifndef OPENCV_FLANN_USE_STD_RAND
cv::randShuffle(indices);
#else
std::random_shuffle(indices.begin(), indices.end());
#endif
// Generate a random set of order of subsignature_size_ bits
for (unsigned int i = 0; i < key_size_; ++i) {
size_t index = indices[i];
// Set that bit in the mask
size_t divisor = CHAR_BIT * sizeof(size_t);
size_t idx = index / divisor; //pick the right size_t index
mask_[idx] |= size_t(1) << (index % divisor); //use modulo to find the bit offset
}
// Set to 1 if you want to display the mask for debug
#if 0
{
size_t bcount = 0;
BOOST_FOREACH(size_t mask_block, mask_){
out << std::setw(sizeof(size_t) * CHAR_BIT / 4) << std::setfill('0') << std::hex << mask_block
<< std::endl;
bcount += __builtin_popcountll(mask_block);
}
out << "bit count : " << std::dec << bcount << std::endl;
out << "mask size : " << mask_.size() << std::endl;
return out;
}
#endif
}
/** Return the Subsignature of a feature
* @param feature the feature to analyze
*/
template<>
inline size_t LshTable<unsigned char>::getKey(const unsigned char* feature) const
{
// no need to check if T is dividable by sizeof(size_t) like in the Hamming
// distance computation as we have a mask
// FIXIT: This is bad assumption, because we reading tail bytes after of the allocated features buffer
const size_t* feature_block_ptr = reinterpret_cast<const size_t*> ((const void*)feature);
// Figure out the subsignature of the feature
// Given the feature ABCDEF, and the mask 001011, the output will be
// 000CEF
size_t subsignature = 0;
size_t bit_index = 1;
for (unsigned i = 0; i < feature_size_; i += sizeof(size_t)) {
// get the mask and signature blocks
size_t feature_block;
if (i <= feature_size_ - sizeof(size_t))
{
feature_block = *feature_block_ptr;
}
else
{
size_t tmp = 0;
memcpy(&tmp, feature_block_ptr, feature_size_ - i); // preserve bytes order
feature_block = tmp;
}
size_t mask_block = mask_[i / sizeof(size_t)];
while (mask_block) {
// Get the lowest set bit in the mask block
size_t lowest_bit = mask_block & (-(ptrdiff_t)mask_block);
// Add it to the current subsignature if necessary
subsignature += (feature_block & lowest_bit) ? bit_index : 0;
// Reset the bit in the mask block
mask_block ^= lowest_bit;
// increment the bit index for the subsignature
bit_index <<= 1;
}
// Check the next feature block
++feature_block_ptr;
}
return subsignature;
}
template<>
inline LshStats LshTable<unsigned char>::getStats() const
{
LshStats stats;
stats.bucket_size_mean_ = 0;
if ((buckets_speed_.empty()) && (buckets_space_.empty())) {
stats.n_buckets_ = 0;
stats.bucket_size_median_ = 0;
stats.bucket_size_min_ = 0;
stats.bucket_size_max_ = 0;
return stats;
}
if (!buckets_speed_.empty()) {
for (BucketsSpeed::const_iterator pbucket = buckets_speed_.begin(); pbucket != buckets_speed_.end(); ++pbucket) {
stats.bucket_sizes_.push_back((lsh::FeatureIndex)pbucket->size());
stats.bucket_size_mean_ += pbucket->size();
}
stats.bucket_size_mean_ /= buckets_speed_.size();
stats.n_buckets_ = buckets_speed_.size();
}
else {
for (BucketsSpace::const_iterator x = buckets_space_.begin(); x != buckets_space_.end(); ++x) {
stats.bucket_sizes_.push_back((lsh::FeatureIndex)x->second.size());
stats.bucket_size_mean_ += x->second.size();
}
stats.bucket_size_mean_ /= buckets_space_.size();
stats.n_buckets_ = buckets_space_.size();
}
std::sort(stats.bucket_sizes_.begin(), stats.bucket_sizes_.end());
// BOOST_FOREACH(int size, stats.bucket_sizes_)
// std::cout << size << " ";
// std::cout << std::endl;
stats.bucket_size_median_ = stats.bucket_sizes_[stats.bucket_sizes_.size() / 2];
stats.bucket_size_min_ = stats.bucket_sizes_.front();
stats.bucket_size_max_ = stats.bucket_sizes_.back();
// TODO compute mean and std
/*float mean, stddev;
stats.bucket_size_mean_ = mean;
stats.bucket_size_std_dev = stddev;*/
// Include a histogram of the buckets
unsigned int bin_start = 0;
unsigned int bin_end = 20;
bool is_new_bin = true;
for (std::vector<unsigned int>::iterator iterator = stats.bucket_sizes_.begin(), end = stats.bucket_sizes_.end(); iterator
!= end; )
if (*iterator < bin_end) {
if (is_new_bin) {
stats.size_histogram_.push_back(std::vector<unsigned int>(3, 0));
stats.size_histogram_.back()[0] = bin_start;
stats.size_histogram_.back()[1] = bin_end - 1;
is_new_bin = false;
}
++stats.size_histogram_.back()[2];
++iterator;
}
else {
bin_start += 20;
bin_end += 20;
is_new_bin = true;
}
return stats;
}
// End the two namespaces
}
}
#ifdef _MSC_VER
#pragma warning(pop)
#endif
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//! @endcond
#endif /* OPENCV_FLANN_LSH_TABLE_H_ */
@@ -0,0 +1,118 @@
/***********************************************************************
* Software License Agreement (BSD License)
*
* Copyright 2008-2009 Marius Muja (mariusm@cs.ubc.ca). All rights reserved.
* Copyright 2008-2009 David G. Lowe (lowe@cs.ubc.ca). All rights reserved.
*
* THE BSD LICENSE
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*************************************************************************/
#ifndef OPENCV_FLANN_DATASET_H_
#define OPENCV_FLANN_DATASET_H_
//! @cond IGNORED
#include <stdio.h>
namespace cvflann
{
/**
* Class that implements a simple rectangular matrix stored in a memory buffer and
* provides convenient matrix-like access using the [] operators.
*/
template <typename T>
class Matrix
{
public:
typedef T type;
size_t rows;
size_t cols;
size_t stride;
T* data;
Matrix() : rows(0), cols(0), stride(0), data(NULL)
{
}
Matrix(T* data_, size_t rows_, size_t cols_, size_t stride_ = 0) :
rows(rows_), cols(cols_), stride(stride_), data(data_)
{
if (stride==0) stride = cols;
}
/**
* Convenience function for deallocating the storage data.
*/
CV_DEPRECATED void free()
{
fprintf(stderr, "The cvflann::Matrix<T>::free() method is deprecated "
"and it does not do any memory deallocation any more. You are"
"responsible for deallocating the matrix memory (by doing"
"'delete[] matrix.data' for example)");
}
/**
* Operator that return a (pointer to a) row of the data.
*/
T* operator[](size_t index) const
{
return data+index*stride;
}
};
class UntypedMatrix
{
public:
size_t rows;
size_t cols;
void* data;
flann_datatype_t type;
UntypedMatrix(void* data_, long rows_, long cols_) :
rows(rows_), cols(cols_), data(data_)
{
}
~UntypedMatrix()
{
}
template<typename T>
Matrix<T> as()
{
return Matrix<T>((T*)data, rows, cols);
}
};
}
//! @endcond
#endif //OPENCV_FLANN_DATASET_H_
@@ -0,0 +1,185 @@
/*M///////////////////////////////////////////////////////////////////////////////////////
//
// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
//
// By downloading, copying, installing or using the software you agree to this license.
// If you do not agree to this license, do not download, install,
// copy or use the software.
//
//
// License Agreement
// For Open Source Computer Vision Library
//
// Copyright (C) 2000-2008, Intel Corporation, all rights reserved.
// Copyright (C) 2009, Willow Garage Inc., all rights reserved.
// Third party copyrights are property of their respective owners.
//
// Redistribution and use in source and binary forms, with or without modification,
// are permitted provided that the following conditions are met:
//
// * Redistribution's of source code must retain the above copyright notice,
// this list of conditions and the following disclaimer.
//
// * Redistribution's in binary form must reproduce the above copyright notice,
// this list of conditions and the following disclaimer in the documentation
// and/or other materials provided with the distribution.
//
// * The name of the copyright holders may not be used to endorse or promote products
// derived from this software without specific prior written permission.
//
// This software is provided by the copyright holders and contributors "as is" and
// any express or implied warranties, including, but not limited to, the implied
// warranties of merchantability and fitness for a particular purpose are disclaimed.
// In no event shall the Intel Corporation or contributors be liable for any direct,
// indirect, incidental, special, exemplary, or consequential damages
// (including, but not limited to, procurement of substitute goods or services;
// loss of use, data, or profits; or business interruption) however caused
// and on any theory of liability, whether in contract, strict liability,
// or tort (including negligence or otherwise) arising in any way out of
// the use of this software, even if advised of the possibility of such damage.
//
//M*/
#ifndef OPENCV_MINIFLANN_HPP
#define OPENCV_MINIFLANN_HPP
//! @cond IGNORED
#include "opencv2/core.hpp"
#include "opencv2/flann/defines.h"
namespace cv
{
namespace flann
{
enum FlannIndexType {
FLANN_INDEX_TYPE_8U = CV_8U,
FLANN_INDEX_TYPE_8S = CV_8S,
FLANN_INDEX_TYPE_16U = CV_16U,
FLANN_INDEX_TYPE_16S = CV_16S,
FLANN_INDEX_TYPE_32S = CV_32S,
FLANN_INDEX_TYPE_32F = CV_32F,
FLANN_INDEX_TYPE_64F = CV_64F,
FLANN_INDEX_TYPE_STRING,
FLANN_INDEX_TYPE_BOOL,
FLANN_INDEX_TYPE_ALGORITHM,
LAST_VALUE_FLANN_INDEX_TYPE = FLANN_INDEX_TYPE_ALGORITHM
};
struct CV_EXPORTS IndexParams
{
IndexParams();
~IndexParams();
String getString(const String& key, const String& defaultVal=String()) const;
int getInt(const String& key, int defaultVal=-1) const;
double getDouble(const String& key, double defaultVal=-1) const;
void setString(const String& key, const String& value);
void setInt(const String& key, int value);
void setDouble(const String& key, double value);
void setFloat(const String& key, float value);
void setBool(const String& key, bool value);
void setAlgorithm(int value);
// FIXIT: replace by void write(FileStorage& fs) const + read()
void getAll(std::vector<String>& names,
std::vector<FlannIndexType>& types,
std::vector<String>& strValues,
std::vector<double>& numValues) const;
void* params;
private:
IndexParams(const IndexParams &); // copy disabled
IndexParams& operator=(const IndexParams &); // assign disabled
};
struct CV_EXPORTS KDTreeIndexParams : public IndexParams
{
KDTreeIndexParams(int trees=4);
};
struct CV_EXPORTS LinearIndexParams : public IndexParams
{
LinearIndexParams();
};
struct CV_EXPORTS CompositeIndexParams : public IndexParams
{
CompositeIndexParams(int trees = 4, int branching = 32, int iterations = 11,
cvflann::flann_centers_init_t centers_init = cvflann::FLANN_CENTERS_RANDOM, float cb_index = 0.2f );
};
struct CV_EXPORTS AutotunedIndexParams : public IndexParams
{
AutotunedIndexParams(float target_precision = 0.8f, float build_weight = 0.01f,
float memory_weight = 0, float sample_fraction = 0.1f);
};
struct CV_EXPORTS HierarchicalClusteringIndexParams : public IndexParams
{
HierarchicalClusteringIndexParams(int branching = 32,
cvflann::flann_centers_init_t centers_init = cvflann::FLANN_CENTERS_RANDOM, int trees = 4, int leaf_size = 100 );
};
struct CV_EXPORTS KMeansIndexParams : public IndexParams
{
KMeansIndexParams(int branching = 32, int iterations = 11,
cvflann::flann_centers_init_t centers_init = cvflann::FLANN_CENTERS_RANDOM, float cb_index = 0.2f );
};
struct CV_EXPORTS LshIndexParams : public IndexParams
{
LshIndexParams(int table_number, int key_size, int multi_probe_level);
};
struct CV_EXPORTS SavedIndexParams : public IndexParams
{
SavedIndexParams(const String& filename);
};
struct CV_EXPORTS SearchParams : public IndexParams
{
SearchParams( int checks, float eps, bool sorted, bool explore_all_trees );
SearchParams( int checks = 32, float eps = 0, bool sorted = true );
};
class CV_EXPORTS_W Index
{
public:
CV_WRAP Index();
CV_WRAP Index(InputArray features, const IndexParams& params, cvflann::flann_distance_t distType=cvflann::FLANN_DIST_L2);
virtual ~Index();
CV_WRAP virtual void build(InputArray features, const IndexParams& params, cvflann::flann_distance_t distType=cvflann::FLANN_DIST_L2);
CV_WRAP virtual void knnSearch(InputArray query, OutputArray indices,
OutputArray dists, int knn, const SearchParams& params=SearchParams());
CV_WRAP virtual int radiusSearch(InputArray query, OutputArray indices,
OutputArray dists, double radius, int maxResults,
const SearchParams& params=SearchParams());
CV_WRAP virtual void save(const String& filename) const;
CV_WRAP virtual bool load(InputArray features, const String& filename);
CV_WRAP virtual void release();
CV_WRAP cvflann::flann_distance_t getDistance() const;
CV_WRAP cvflann::flann_algorithm_t getAlgorithm() const;
protected:
bool load_(const String& filename);
cvflann::flann_distance_t distType;
cvflann::flann_algorithm_t algo;
int featureType;
void* index;
Mat features_clone; // index may store features pointer internally for searching, so avoid dangling pointers: https://github.com/opencv/opencv/issues/17553
};
} } // namespace cv::flann
//! @endcond
#endif
@@ -0,0 +1,180 @@
/***********************************************************************
* Software License Agreement (BSD License)
*
* Copyright 2008-2009 Marius Muja (mariusm@cs.ubc.ca). All rights reserved.
* Copyright 2008-2009 David G. Lowe (lowe@cs.ubc.ca). All rights reserved.
*
* THE BSD LICENSE
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*************************************************************************/
#ifndef OPENCV_FLANN_NNINDEX_H
#define OPENCV_FLANN_NNINDEX_H
#include "matrix.h"
#include "result_set.h"
#include "params.h"
//! @cond IGNORED
namespace cvflann
{
/**
* Nearest-neighbour index base class
*/
template <typename Distance>
class NNIndex
{
typedef typename Distance::ElementType ElementType;
typedef typename Distance::ResultType DistanceType;
public:
virtual ~NNIndex() {}
/**
* \brief Builds the index
*/
virtual void buildIndex() = 0;
/**
* \brief Perform k-nearest neighbor search
* \param[in] queries The query points for which to find the nearest neighbors
* \param[out] indices The indices of the nearest neighbors found
* \param[out] dists Distances to the nearest neighbors found
* \param[in] knn Number of nearest neighbors to return
* \param[in] params Search parameters
*/
virtual void knnSearch(const Matrix<ElementType>& queries, Matrix<int>& indices, Matrix<DistanceType>& dists, int knn, const SearchParams& params)
{
CV_Assert(queries.cols == veclen());
CV_Assert(indices.rows >= queries.rows);
CV_Assert(dists.rows >= queries.rows);
CV_Assert(int(indices.cols) >= knn);
CV_Assert(int(dists.cols) >= knn);
#if 0
KNNResultSet<DistanceType> resultSet(knn);
for (size_t i = 0; i < queries.rows; i++) {
resultSet.init(indices[i], dists[i]);
findNeighbors(resultSet, queries[i], params);
}
#else
KNNUniqueResultSet<DistanceType> resultSet(knn);
for (size_t i = 0; i < queries.rows; i++) {
resultSet.clear();
findNeighbors(resultSet, queries[i], params);
if (get_param(params,"sorted",true)) resultSet.sortAndCopy(indices[i], dists[i], knn);
else resultSet.copy(indices[i], dists[i], knn);
}
#endif
}
/**
* \brief Perform radius search
* \param[in] query The query point
* \param[out] indices The indinces of the neighbors found within the given radius
* \param[out] dists The distances to the nearest neighbors found
* \param[in] radius The radius used for search
* \param[in] params Search parameters
* \returns Number of neighbors found
*/
virtual int radiusSearch(const Matrix<ElementType>& query, Matrix<int>& indices, Matrix<DistanceType>& dists, float radius, const SearchParams& params)
{
if (query.rows != 1) {
fprintf(stderr, "I can only search one feature at a time for range search\n");
return -1;
}
CV_Assert(query.cols == veclen());
CV_Assert(indices.cols == dists.cols);
int n = 0;
int* indices_ptr = NULL;
DistanceType* dists_ptr = NULL;
if (indices.cols > 0) {
n = (int)indices.cols;
indices_ptr = indices[0];
dists_ptr = dists[0];
}
RadiusUniqueResultSet<DistanceType> resultSet((DistanceType)radius);
resultSet.clear();
findNeighbors(resultSet, query[0], params);
if (n>0) {
if (get_param(params,"sorted",true)) resultSet.sortAndCopy(indices_ptr, dists_ptr, n);
else resultSet.copy(indices_ptr, dists_ptr, n);
}
return (int)resultSet.size();
}
/**
* \brief Saves the index to a stream
* \param stream The stream to save the index to
*/
virtual void saveIndex(FILE* stream) = 0;
/**
* \brief Loads the index from a stream
* \param stream The stream from which the index is loaded
*/
virtual void loadIndex(FILE* stream) = 0;
/**
* \returns number of features in this index.
*/
virtual size_t size() const = 0;
/**
* \returns The dimensionality of the features in this index.
*/
virtual size_t veclen() const = 0;
/**
* \returns The amount of memory (in bytes) used by the index.
*/
virtual int usedMemory() const = 0;
/**
* \returns The index type (kdtree, kmeans,...)
*/
virtual flann_algorithm_t getType() const = 0;
/**
* \returns The index parameters
*/
virtual IndexParams getParameters() const = 0;
/**
* \brief Method that searches for nearest-neighbours
*/
virtual void findNeighbors(ResultSet<DistanceType>& result, const ElementType* vec, const SearchParams& searchParams) = 0;
};
}
//! @endcond
#endif //OPENCV_FLANN_NNINDEX_H
@@ -0,0 +1,95 @@
/***********************************************************************
* Software License Agreement (BSD License)
*
* Copyright 2008-2009 Marius Muja (mariusm@cs.ubc.ca). All rights reserved.
* Copyright 2008-2009 David G. Lowe (lowe@cs.ubc.ca). All rights reserved.
*
* THE BSD LICENSE
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*************************************************************************/
#ifndef OPENCV_FLANN_OBJECT_FACTORY_H_
#define OPENCV_FLANN_OBJECT_FACTORY_H_
//! @cond IGNORED
#include <map>
namespace cvflann
{
class CreatorNotFound
{
};
template<typename BaseClass,
typename UniqueIdType,
typename ObjectCreator = BaseClass* (*)()>
class ObjectFactory
{
typedef ObjectFactory<BaseClass,UniqueIdType,ObjectCreator> ThisClass;
typedef std::map<UniqueIdType, ObjectCreator> ObjectRegistry;
// singleton class, private constructor
ObjectFactory() {}
public:
bool subscribe(UniqueIdType id, ObjectCreator creator)
{
if (object_registry.find(id) != object_registry.end()) return false;
object_registry[id] = creator;
return true;
}
bool unregister(UniqueIdType id)
{
return object_registry.erase(id) == 1;
}
ObjectCreator create(UniqueIdType id)
{
typename ObjectRegistry::const_iterator iter = object_registry.find(id);
if (iter == object_registry.end()) {
throw CreatorNotFound();
}
return iter->second;
}
static ThisClass& instance()
{
static ThisClass the_factory;
return the_factory;
}
private:
ObjectRegistry object_registry;
};
}
//! @endcond
#endif /* OPENCV_FLANN_OBJECT_FACTORY_H_ */
@@ -0,0 +1,116 @@
/***********************************************************************
* Software License Agreement (BSD License)
*
* Copyright 2008-2011 Marius Muja (mariusm@cs.ubc.ca). All rights reserved.
* Copyright 2008-2011 David G. Lowe (lowe@cs.ubc.ca). All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*************************************************************************/
#ifndef OPENCV_FLANN_PARAMS_H_
#define OPENCV_FLANN_PARAMS_H_
//! @cond IGNORED
#include "any.h"
#include "general.h"
#include <iostream>
#include <map>
namespace cvflann
{
typedef std::map<cv::String, any> IndexParams;
struct SearchParams : public IndexParams
{
SearchParams(int checks = 32, float eps = 0, bool sorted = true )
{
init(checks, eps, sorted, false);
}
SearchParams(int checks, float eps, bool sorted, bool explore_all_trees )
{
init(checks, eps, sorted, explore_all_trees);
}
void init(int checks = 32, float eps = 0, bool sorted = true, bool explore_all_trees = false )
{
// how many leafs to visit when searching for neighbours (-1 for unlimited)
(*this)["checks"] = checks;
// search for eps-approximate neighbours (default: 0)
(*this)["eps"] = eps;
// only for radius search, require neighbours sorted by distance (default: true)
(*this)["sorted"] = sorted;
// if false, search stops at the tree reaching the number of max checks (original behavior).
// When true, we do a descent in each tree and. Like before the alternative paths
// stored in the heap are not be processed further when max checks is reached.
(*this)["explore_all_trees"] = explore_all_trees;
}
};
template<typename T>
T get_param(const IndexParams& params, cv::String name, const T& default_value)
{
IndexParams::const_iterator it = params.find(name);
if (it != params.end()) {
return it->second.cast<T>();
}
else {
return default_value;
}
}
template<typename T>
T get_param(const IndexParams& params, cv::String name)
{
IndexParams::const_iterator it = params.find(name);
if (it != params.end()) {
return it->second.cast<T>();
}
else {
FLANN_THROW(cv::Error::StsBadArg, cv::String("Missing parameter '")+name+cv::String("' in the parameters given"));
}
}
inline void print_params(const IndexParams& params, std::ostream& stream)
{
IndexParams::const_iterator it;
for(it=params.begin(); it!=params.end(); ++it) {
stream << it->first << " : " << it->second << std::endl;
}
}
inline void print_params(const IndexParams& params)
{
print_params(params, std::cout);
}
}
//! @endcond
#endif /* OPENCV_FLANN_PARAMS_H_ */
@@ -0,0 +1,157 @@
/***********************************************************************
* Software License Agreement (BSD License)
*
* Copyright 2008-2009 Marius Muja (mariusm@cs.ubc.ca). All rights reserved.
* Copyright 2008-2009 David G. Lowe (lowe@cs.ubc.ca). All rights reserved.
*
* THE BSD LICENSE
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*************************************************************************/
#ifndef OPENCV_FLANN_RANDOM_H
#define OPENCV_FLANN_RANDOM_H
//! @cond IGNORED
#include <algorithm>
#include <cstdlib>
#include <vector>
namespace cvflann
{
inline int rand()
{
#ifndef OPENCV_FLANN_USE_STD_RAND
# if INT_MAX == RAND_MAX
int v = cv::theRNG().next() & INT_MAX;
# else
int v = cv::theRNG().uniform(0, RAND_MAX + 1);
# endif
#else
int v = std::rand();
#endif // OPENCV_FLANN_USE_STD_RAND
return v;
}
/**
* Seeds the random number generator
* @param seed Random seed
*/
inline void seed_random(unsigned int seed)
{
#ifndef OPENCV_FLANN_USE_STD_RAND
cv::theRNG() = cv::RNG(seed);
#else
std::srand(seed);
#endif
}
/*
* Generates a random double value.
*/
/**
* Generates a random double value.
* @param high Upper limit
* @param low Lower limit
* @return Random double value
*/
inline double rand_double(double high = 1.0, double low = 0)
{
return low + ((high-low) * (rand() / (RAND_MAX + 1.0)));
}
/**
* Generates a random integer value.
* @param high Upper limit
* @param low Lower limit
* @return Random integer value
*/
inline int rand_int(int high = RAND_MAX, int low = 0)
{
return low + (int) ( double(high-low) * (rand() / (RAND_MAX + 1.0)));
}
/**
* Random number generator that returns a distinct number from
* the [0,n) interval each time.
*/
class UniqueRandom
{
std::vector<int> vals_;
int size_;
int counter_;
public:
/**
* Constructor.
* @param n Size of the interval from which to generate
* @return
*/
UniqueRandom(int n)
{
init(n);
}
/**
* Initializes the number generator.
* @param n the size of the interval from which to generate random numbers.
*/
void init(int n)
{
// create and initialize an array of size n
vals_.resize(n);
size_ = n;
for (int i = 0; i < size_; ++i) vals_[i] = i;
// shuffle the elements in the array
#ifndef OPENCV_FLANN_USE_STD_RAND
cv::randShuffle(vals_);
#else
std::random_shuffle(vals_.begin(), vals_.end());
#endif
counter_ = 0;
}
/**
* Return a distinct random integer in greater or equal to 0 and less
* than 'n' on each call. It should be called maximum 'n' times.
* Returns: a random integer
*/
int next()
{
if (counter_ == size_) {
return -1;
}
else {
return vals_[counter_++];
}
}
};
}
//! @endcond
#endif //OPENCV_FLANN_RANDOM_H
@@ -0,0 +1,548 @@
/***********************************************************************
* Software License Agreement (BSD License)
*
* Copyright 2008-2009 Marius Muja (mariusm@cs.ubc.ca). All rights reserved.
* Copyright 2008-2009 David G. Lowe (lowe@cs.ubc.ca). All rights reserved.
*
* THE BSD LICENSE
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*************************************************************************/
#ifndef OPENCV_FLANN_RESULTSET_H
#define OPENCV_FLANN_RESULTSET_H
//! @cond IGNORED
#include <algorithm>
#include <cstring>
#include <iostream>
#include <limits>
#include <set>
#include <vector>
namespace cvflann
{
/* This record represents a branch point when finding neighbors in
the tree. It contains a record of the minimum distance to the query
point, as well as the node at which the search resumes.
*/
template <typename T, typename DistanceType>
struct BranchStruct
{
T node; /* Tree node at which search resumes */
DistanceType mindist; /* Minimum distance to query for all nodes below. */
BranchStruct() {}
BranchStruct(const T& aNode, DistanceType dist) : node(aNode), mindist(dist) {}
bool operator<(const BranchStruct<T, DistanceType>& rhs) const
{
return mindist<rhs.mindist;
}
};
template <typename DistanceType>
class ResultSet
{
public:
virtual ~ResultSet() {}
virtual bool full() const = 0;
virtual void addPoint(DistanceType dist, int index) = 0;
virtual DistanceType worstDist() const = 0;
};
/**
* KNNSimpleResultSet does not ensure that the element it holds are unique.
* Is used in those cases where the nearest neighbour algorithm used does not
* attempt to insert the same element multiple times.
*/
template <typename DistanceType>
class KNNSimpleResultSet : public ResultSet<DistanceType>
{
int* indices;
DistanceType* dists;
int capacity;
int count;
DistanceType worst_distance_;
public:
KNNSimpleResultSet(int capacity_) : capacity(capacity_), count(0)
{
}
void init(int* indices_, DistanceType* dists_)
{
indices = indices_;
dists = dists_;
count = 0;
worst_distance_ = (std::numeric_limits<DistanceType>::max)();
dists[capacity-1] = worst_distance_;
}
size_t size() const
{
return count;
}
bool full() const CV_OVERRIDE
{
return count == capacity;
}
void addPoint(DistanceType dist, int index) CV_OVERRIDE
{
if (dist >= worst_distance_) return;
int i;
for (i=count; i>0; --i) {
#ifdef FLANN_FIRST_MATCH
if ( (dists[i-1]>dist) || ((dist==dists[i-1])&&(indices[i-1]>index)) )
#else
if (dists[i-1]>dist)
#endif
{
if (i<capacity) {
dists[i] = dists[i-1];
indices[i] = indices[i-1];
}
}
else break;
}
if (count < capacity) ++count;
dists[i] = dist;
indices[i] = index;
worst_distance_ = dists[capacity-1];
}
DistanceType worstDist() const CV_OVERRIDE
{
return worst_distance_;
}
};
/**
* K-Nearest neighbour result set. Ensures that the elements inserted are unique
*/
template <typename DistanceType>
class KNNResultSet : public ResultSet<DistanceType>
{
int* indices;
DistanceType* dists;
int capacity;
int count;
DistanceType worst_distance_;
public:
KNNResultSet(int capacity_)
: indices(NULL), dists(NULL), capacity(capacity_), count(0), worst_distance_(0)
{
}
void init(int* indices_, DistanceType* dists_)
{
indices = indices_;
dists = dists_;
count = 0;
worst_distance_ = (std::numeric_limits<DistanceType>::max)();
dists[capacity-1] = worst_distance_;
}
size_t size() const
{
return count;
}
bool full() const CV_OVERRIDE
{
return count == capacity;
}
void addPoint(DistanceType dist, int index) CV_OVERRIDE
{
CV_DbgAssert(indices);
CV_DbgAssert(dists);
if (dist >= worst_distance_) return;
int i;
for (i = count; i > 0; --i) {
#ifdef FLANN_FIRST_MATCH
if ( (dists[i-1]<=dist) && ((dist!=dists[i-1])||(indices[i-1]<=index)) )
#else
if (dists[i-1]<=dist)
#endif
{
// Check for duplicate indices
for (int j = i; dists[j] == dist && j--;) {
if (indices[j] == index) {
return;
}
}
break;
}
}
if (count < capacity) ++count;
for (int j = count-1; j > i; --j) {
dists[j] = dists[j-1];
indices[j] = indices[j-1];
}
dists[i] = dist;
indices[i] = index;
worst_distance_ = dists[capacity-1];
}
DistanceType worstDist() const CV_OVERRIDE
{
return worst_distance_;
}
};
/**
* A result-set class used when performing a radius based search.
*/
template <typename DistanceType>
class RadiusResultSet : public ResultSet<DistanceType>
{
DistanceType radius;
int* indices;
DistanceType* dists;
size_t capacity;
size_t count;
public:
RadiusResultSet(DistanceType radius_, int* indices_, DistanceType* dists_, int capacity_) :
radius(radius_), indices(indices_), dists(dists_), capacity(capacity_)
{
init();
}
~RadiusResultSet()
{
}
void init()
{
count = 0;
}
size_t size() const
{
return count;
}
bool full() const
{
return true;
}
void addPoint(DistanceType dist, int index)
{
if (dist<radius) {
if ((capacity>0)&&(count < capacity)) {
dists[count] = dist;
indices[count] = index;
}
count++;
}
}
DistanceType worstDist() const
{
return radius;
}
};
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/** Class that holds the k NN neighbors
* Faster than KNNResultSet as it uses a binary heap and does not maintain two arrays
*/
template<typename DistanceType>
class UniqueResultSet : public ResultSet<DistanceType>
{
public:
struct DistIndex
{
DistIndex(DistanceType dist, unsigned int index) :
dist_(dist), index_(index)
{
}
bool operator<(const DistIndex dist_index) const
{
return (dist_ < dist_index.dist_) || ((dist_ == dist_index.dist_) && index_ < dist_index.index_);
}
DistanceType dist_;
unsigned int index_;
};
/** Default constructor */
UniqueResultSet() :
is_full_(false), worst_distance_(std::numeric_limits<DistanceType>::max())
{
}
/** Check the status of the set
* @return true if we have k NN
*/
inline bool full() const CV_OVERRIDE
{
return is_full_;
}
/** Remove all elements in the set
*/
virtual void clear() = 0;
/** Copy the set to two C arrays
* @param indices pointer to a C array of indices
* @param dist pointer to a C array of distances
* @param n_neighbors the number of neighbors to copy
*/
virtual void copy(int* indices, DistanceType* dist, int n_neighbors = -1) const
{
if (n_neighbors < 0) {
for (typename std::set<DistIndex>::const_iterator dist_index = dist_indices_.begin(), dist_index_end =
dist_indices_.end(); dist_index != dist_index_end; ++dist_index, ++indices, ++dist) {
*indices = dist_index->index_;
*dist = dist_index->dist_;
}
}
else {
int i = 0;
for (typename std::set<DistIndex>::const_iterator dist_index = dist_indices_.begin(), dist_index_end =
dist_indices_.end(); (dist_index != dist_index_end) && (i < n_neighbors); ++dist_index, ++indices, ++dist, ++i) {
*indices = dist_index->index_;
*dist = dist_index->dist_;
}
}
}
/** Copy the set to two C arrays but sort it according to the distance first
* @param indices pointer to a C array of indices
* @param dist pointer to a C array of distances
* @param n_neighbors the number of neighbors to copy
*/
virtual void sortAndCopy(int* indices, DistanceType* dist, int n_neighbors = -1) const
{
copy(indices, dist, n_neighbors);
}
/** The number of neighbors in the set
* @return
*/
size_t size() const
{
return dist_indices_.size();
}
/** The distance of the furthest neighbor
* If we don't have enough neighbors, it returns the max possible value
* @return
*/
inline DistanceType worstDist() const CV_OVERRIDE
{
return worst_distance_;
}
protected:
/** Flag to say if the set is full */
bool is_full_;
/** The worst distance found so far */
DistanceType worst_distance_;
/** The best candidates so far */
std::set<DistIndex> dist_indices_;
};
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/** Class that holds the k NN neighbors
* Faster than KNNResultSet as it uses a binary heap and does not maintain two arrays
*/
template<typename DistanceType>
class KNNUniqueResultSet : public UniqueResultSet<DistanceType>
{
public:
/** Constructor
* @param capacity the number of neighbors to store at max
*/
KNNUniqueResultSet(unsigned int capacity) : capacity_(capacity)
{
this->is_full_ = false;
this->clear();
}
/** Add a possible candidate to the best neighbors
* @param dist distance for that neighbor
* @param index index of that neighbor
*/
inline void addPoint(DistanceType dist, int index) CV_OVERRIDE
{
// Don't do anything if we are worse than the worst
if (dist >= worst_distance_) return;
dist_indices_.insert(DistIndex(dist, index));
if (is_full_) {
if (dist_indices_.size() > capacity_) {
dist_indices_.erase(*dist_indices_.rbegin());
worst_distance_ = dist_indices_.rbegin()->dist_;
}
}
else if (dist_indices_.size() == capacity_) {
is_full_ = true;
worst_distance_ = dist_indices_.rbegin()->dist_;
}
}
/** Remove all elements in the set
*/
void clear() CV_OVERRIDE
{
dist_indices_.clear();
worst_distance_ = std::numeric_limits<DistanceType>::max();
is_full_ = false;
}
protected:
typedef typename UniqueResultSet<DistanceType>::DistIndex DistIndex;
using UniqueResultSet<DistanceType>::is_full_;
using UniqueResultSet<DistanceType>::worst_distance_;
using UniqueResultSet<DistanceType>::dist_indices_;
/** The number of neighbors to keep */
unsigned int capacity_;
};
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/** Class that holds the radius nearest neighbors
* It is more accurate than RadiusResult as it is not limited in the number of neighbors
*/
template<typename DistanceType>
class RadiusUniqueResultSet : public UniqueResultSet<DistanceType>
{
public:
/** Constructor
* @param radius the maximum distance of a neighbor
*/
RadiusUniqueResultSet(DistanceType radius) :
radius_(radius)
{
is_full_ = true;
}
/** Add a possible candidate to the best neighbors
* @param dist distance for that neighbor
* @param index index of that neighbor
*/
void addPoint(DistanceType dist, int index) CV_OVERRIDE
{
if (dist <= radius_) dist_indices_.insert(DistIndex(dist, index));
}
/** Remove all elements in the set
*/
inline void clear() CV_OVERRIDE
{
dist_indices_.clear();
}
/** Check the status of the set
* @return alwys false
*/
inline bool full() const CV_OVERRIDE
{
return true;
}
/** The distance of the furthest neighbor
* If we don't have enough neighbors, it returns the max possible value
* @return
*/
inline DistanceType worstDist() const CV_OVERRIDE
{
return radius_;
}
private:
typedef typename UniqueResultSet<DistanceType>::DistIndex DistIndex;
using UniqueResultSet<DistanceType>::dist_indices_;
using UniqueResultSet<DistanceType>::is_full_;
/** The furthest distance a neighbor can be */
DistanceType radius_;
};
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/** Class that holds the k NN neighbors within a radius distance
*/
template<typename DistanceType>
class KNNRadiusUniqueResultSet : public KNNUniqueResultSet<DistanceType>
{
public:
/** Constructor
* @param capacity the number of neighbors to store at max
* @param radius the maximum distance of a neighbor
*/
KNNRadiusUniqueResultSet(unsigned int capacity, DistanceType radius)
{
this->capacity_ = capacity;
this->radius_ = radius;
this->dist_indices_.reserve(capacity_);
this->clear();
}
/** Remove all elements in the set
*/
void clear()
{
dist_indices_.clear();
worst_distance_ = radius_;
is_full_ = false;
}
private:
using KNNUniqueResultSet<DistanceType>::dist_indices_;
using KNNUniqueResultSet<DistanceType>::is_full_;
using KNNUniqueResultSet<DistanceType>::worst_distance_;
/** The maximum number of neighbors to consider */
unsigned int capacity_;
/** The maximum distance of a neighbor */
DistanceType radius_;
};
}
//! @endcond
#endif //OPENCV_FLANN_RESULTSET_H
@@ -0,0 +1,84 @@
/***********************************************************************
* Software License Agreement (BSD License)
*
* Copyright 2008-2009 Marius Muja (mariusm@cs.ubc.ca). All rights reserved.
* Copyright 2008-2009 David G. Lowe (lowe@cs.ubc.ca). All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*************************************************************************/
#ifndef OPENCV_FLANN_SAMPLING_H_
#define OPENCV_FLANN_SAMPLING_H_
//! @cond IGNORED
#include "matrix.h"
#include "random.h"
namespace cvflann
{
template<typename T>
Matrix<T> random_sample(Matrix<T>& srcMatrix, long size, bool remove = false)
{
Matrix<T> newSet(new T[size * srcMatrix.cols], size,srcMatrix.cols);
T* src,* dest;
for (long i=0; i<size; ++i) {
long r = rand_int((int)(srcMatrix.rows-i));
dest = newSet[i];
src = srcMatrix[r];
std::copy(src, src+srcMatrix.cols, dest);
if (remove) {
src = srcMatrix[srcMatrix.rows-i-1];
dest = srcMatrix[r];
std::copy(src, src+srcMatrix.cols, dest);
}
}
if (remove) {
srcMatrix.rows -= size;
}
return newSet;
}
template<typename T>
Matrix<T> random_sample(const Matrix<T>& srcMatrix, size_t size)
{
UniqueRandom rand((int)srcMatrix.rows);
Matrix<T> newSet(new T[size * srcMatrix.cols], size,srcMatrix.cols);
T* src,* dest;
for (size_t i=0; i<size; ++i) {
long r = rand.next();
dest = newSet[i];
src = srcMatrix[r];
std::copy(src, src+srcMatrix.cols, dest);
}
return newSet;
}
} // namespace
//! @endcond
#endif /* OPENCV_FLANN_SAMPLING_H_ */
@@ -0,0 +1,191 @@
/***********************************************************************
* Software License Agreement (BSD License)
*
* Copyright 2008-2009 Marius Muja (mariusm@cs.ubc.ca). All rights reserved.
* Copyright 2008-2009 David G. Lowe (lowe@cs.ubc.ca). All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE NNIndexGOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*************************************************************************/
#ifndef OPENCV_FLANN_SAVING_H_
#define OPENCV_FLANN_SAVING_H_
//! @cond IGNORED
#include <cstring>
#include <vector>
#include "general.h"
#include "nn_index.h"
#ifdef FLANN_SIGNATURE_
#undef FLANN_SIGNATURE_
#endif
#define FLANN_SIGNATURE_ "FLANN_INDEX"
namespace cvflann
{
template <typename T>
struct Datatype {};
template<>
struct Datatype<char> { static flann_datatype_t type() { return FLANN_INT8; } };
template<>
struct Datatype<short> { static flann_datatype_t type() { return FLANN_INT16; } };
template<>
struct Datatype<int> { static flann_datatype_t type() { return FLANN_INT32; } };
template<>
struct Datatype<unsigned char> { static flann_datatype_t type() { return FLANN_UINT8; } };
template<>
struct Datatype<unsigned short> { static flann_datatype_t type() { return FLANN_UINT16; } };
template<>
struct Datatype<unsigned int> { static flann_datatype_t type() { return FLANN_UINT32; } };
template<>
struct Datatype<float> { static flann_datatype_t type() { return FLANN_FLOAT32; } };
template<>
struct Datatype<double> { static flann_datatype_t type() { return FLANN_FLOAT64; } };
/**
* Structure representing the index header.
*/
struct IndexHeader
{
char signature[16];
char version[16];
flann_datatype_t data_type;
flann_algorithm_t index_type;
size_t rows;
size_t cols;
};
/**
* Saves index header to stream
*
* @param stream - Stream to save to
* @param index - The index to save
*/
template<typename Distance>
void save_header(FILE* stream, const NNIndex<Distance>& index)
{
IndexHeader header;
memset(header.signature, 0, sizeof(header.signature));
strcpy(header.signature, FLANN_SIGNATURE_);
memset(header.version, 0, sizeof(header.version));
strcpy(header.version, FLANN_VERSION_);
header.data_type = Datatype<typename Distance::ElementType>::type();
header.index_type = index.getType();
header.rows = index.size();
header.cols = index.veclen();
std::fwrite(&header, sizeof(header),1,stream);
}
/**
*
* @param stream - Stream to load from
* @return Index header
*/
inline IndexHeader load_header(FILE* stream)
{
IndexHeader header;
size_t read_size = fread(&header,sizeof(header),1,stream);
if (read_size!=(size_t)1) {
FLANN_THROW(cv::Error::StsError, "Invalid index file, cannot read");
}
if (strcmp(header.signature,FLANN_SIGNATURE_)!=0) {
FLANN_THROW(cv::Error::StsError, "Invalid index file, wrong signature");
}
return header;
}
template<typename T>
void save_value(FILE* stream, const T& value, size_t count = 1)
{
fwrite(&value, sizeof(value),count, stream);
}
template<typename T>
void save_value(FILE* stream, const cvflann::Matrix<T>& value)
{
fwrite(&value, sizeof(value),1, stream);
fwrite(value.data, sizeof(T),value.rows*value.cols, stream);
}
template<typename T>
void save_value(FILE* stream, const std::vector<T>& value)
{
size_t size = value.size();
fwrite(&size, sizeof(size_t), 1, stream);
fwrite(&value[0], sizeof(T), size, stream);
}
template<typename T>
void load_value(FILE* stream, T& value, size_t count = 1)
{
size_t read_cnt = fread(&value, sizeof(value), count, stream);
if (read_cnt != count) {
FLANN_THROW(cv::Error::StsParseError, "Cannot read from file");
}
}
template<typename T>
void load_value(FILE* stream, cvflann::Matrix<T>& value)
{
size_t read_cnt = fread(&value, sizeof(value), 1, stream);
if (read_cnt != 1) {
FLANN_THROW(cv::Error::StsParseError, "Cannot read from file");
}
value.data = new T[value.rows*value.cols];
read_cnt = fread(value.data, sizeof(T), value.rows*value.cols, stream);
if (read_cnt != (size_t)(value.rows*value.cols)) {
FLANN_THROW(cv::Error::StsParseError, "Cannot read from file");
}
}
template<typename T>
void load_value(FILE* stream, std::vector<T>& value)
{
size_t size;
size_t read_cnt = fread(&size, sizeof(size_t), 1, stream);
if (read_cnt!=1) {
FLANN_THROW(cv::Error::StsError, "Cannot read from file");
}
value.resize(size);
read_cnt = fread(&value[0], sizeof(T), size, stream);
if (read_cnt != size) {
FLANN_THROW(cv::Error::StsError, "Cannot read from file");
}
}
}
//! @endcond
#endif /* OPENCV_FLANN_SAVING_H_ */
@@ -0,0 +1,190 @@
/***********************************************************************
* Software License Agreement (BSD License)
*
* Copyright 2008-2009 Marius Muja (mariusm@cs.ubc.ca). All rights reserved.
* Copyright 2008-2009 David G. Lowe (lowe@cs.ubc.ca). All rights reserved.
*
* THE BSD LICENSE
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*************************************************************************/
#ifndef OPENCV_FLANN_SIMPLEX_DOWNHILL_H_
#define OPENCV_FLANN_SIMPLEX_DOWNHILL_H_
//! @cond IGNORED
namespace cvflann
{
/**
Adds val to array vals (and point to array points) and keeping the arrays sorted by vals.
*/
template <typename T>
void addValue(int pos, float val, float* vals, T* point, T* points, int n)
{
vals[pos] = val;
for (int i=0; i<n; ++i) {
points[pos*n+i] = point[i];
}
// bubble down
int j=pos;
while (j>0 && vals[j]<vals[j-1]) {
swap(vals[j],vals[j-1]);
for (int i=0; i<n; ++i) {
swap(points[j*n+i],points[(j-1)*n+i]);
}
--j;
}
}
/**
Simplex downhill optimization function.
Preconditions: points is a 2D mattrix of size (n+1) x n
func is the cost function taking n an array of n params and returning float
vals is the cost function in the n+1 simplex points, if NULL it will be computed
Postcondition: returns optimum value and points[0..n] are the optimum parameters
*/
template <typename T, typename F>
float optimizeSimplexDownhill(T* points, int n, F func, float* vals = NULL )
{
const int MAX_ITERATIONS = 10;
CV_DbgAssert(n>0);
T* p_o = new T[n];
T* p_r = new T[n];
T* p_e = new T[n];
int alpha = 1;
int iterations = 0;
bool ownVals = false;
if (vals == NULL) {
ownVals = true;
vals = new float[n+1];
for (int i=0; i<n+1; ++i) {
float val = func(points+i*n);
addValue(i, val, vals, points+i*n, points, n);
}
}
int nn = n*n;
while (true) {
if (iterations++ > MAX_ITERATIONS) break;
// compute average of simplex points (except the highest point)
for (int j=0; j<n; ++j) {
p_o[j] = 0;
for (int i=0; i<n; ++i) {
p_o[i] += points[j*n+i];
}
}
for (int i=0; i<n; ++i) {
p_o[i] /= n;
}
bool converged = true;
for (int i=0; i<n; ++i) {
if (p_o[i] != points[nn+i]) {
converged = false;
}
}
if (converged) break;
// trying a reflection
for (int i=0; i<n; ++i) {
p_r[i] = p_o[i] + alpha*(p_o[i]-points[nn+i]);
}
float val_r = func(p_r);
if ((val_r>=vals[0])&&(val_r<vals[n])) {
// reflection between second highest and lowest
// add it to the simplex
Logger::info("Choosing reflection\n");
addValue(n, val_r,vals, p_r, points, n);
continue;
}
if (val_r<vals[0]) {
// value is smaller than smallest in simplex
// expand some more to see if it drops further
for (int i=0; i<n; ++i) {
p_e[i] = 2*p_r[i]-p_o[i];
}
float val_e = func(p_e);
if (val_e<val_r) {
Logger::info("Choosing reflection and expansion\n");
addValue(n, val_e,vals,p_e,points,n);
}
else {
Logger::info("Choosing reflection\n");
addValue(n, val_r,vals,p_r,points,n);
}
continue;
}
if (val_r>=vals[n]) {
for (int i=0; i<n; ++i) {
p_e[i] = (p_o[i]+points[nn+i])/2;
}
float val_e = func(p_e);
if (val_e<vals[n]) {
Logger::info("Choosing contraction\n");
addValue(n,val_e,vals,p_e,points,n);
continue;
}
}
{
Logger::info("Full contraction\n");
for (int j=1; j<=n; ++j) {
for (int i=0; i<n; ++i) {
points[j*n+i] = (points[j*n+i]+points[i])/2;
}
float val = func(points+j*n);
addValue(j,val,vals,points+j*n,points,n);
}
}
}
float bestVal = vals[0];
delete[] p_r;
delete[] p_o;
delete[] p_e;
if (ownVals) delete[] vals;
return bestVal;
}
}
//! @endcond
#endif //OPENCV_FLANN_SIMPLEX_DOWNHILL_H_
@@ -0,0 +1,99 @@
/***********************************************************************
* Software License Agreement (BSD License)
*
* Copyright 2008-2009 Marius Muja (mariusm@cs.ubc.ca). All rights reserved.
* Copyright 2008-2009 David G. Lowe (lowe@cs.ubc.ca). All rights reserved.
*
* THE BSD LICENSE
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*************************************************************************/
#ifndef OPENCV_FLANN_TIMER_H
#define OPENCV_FLANN_TIMER_H
//! @cond IGNORED
#include <time.h>
#include "opencv2/core.hpp"
#include "opencv2/core/utility.hpp"
namespace cvflann
{
/**
* A start-stop timer class.
*
* Can be used to time portions of code.
*/
class StartStopTimer
{
int64 startTime;
public:
/**
* Value of the timer.
*/
double value;
/**
* Constructor.
*/
StartStopTimer()
: startTime(0)
{
reset();
}
/**
* Starts the timer.
*/
void start()
{
startTime = cv::getTickCount();
}
/**
* Stops the timer and updates timer value.
*/
void stop()
{
int64 stopTime = cv::getTickCount();
value += ( (double)stopTime - startTime) / cv::getTickFrequency();
}
/**
* Resets the timer value to 0.
*/
void reset()
{
value = 0;
}
};
}
//! @endcond
#endif // FLANN_TIMER_H
@@ -0,0 +1,42 @@
// This file is part of OpenCV project.
// It is subject to the license terms in the LICENSE file found in the top-level directory
// of this distribution and at http://opencv.org/license.html.
//
// Copyright (C) 2018-2021 Intel Corporation
#ifndef OPENCV_GAPI_HPP
#define OPENCV_GAPI_HPP
#include <memory>
/** \defgroup gapi G-API framework
@{
@defgroup gapi_main_classes G-API Main Classes
@defgroup gapi_data_objects G-API Data Types
@{
@defgroup gapi_meta_args G-API Metadata Descriptors
@}
@defgroup gapi_std_backends G-API Standard Backends
@defgroup gapi_compile_args G-API Graph Compilation Arguments
@defgroup gapi_serialization G-API Serialization functionality
@}
*/
#include <opencv2/gapi/gmat.hpp>
#include <opencv2/gapi/garray.hpp>
#include <opencv2/gapi/gscalar.hpp>
#include <opencv2/gapi/gopaque.hpp>
#include <opencv2/gapi/gframe.hpp>
#include <opencv2/gapi/gcomputation.hpp>
#include <opencv2/gapi/gcompiled.hpp>
#include <opencv2/gapi/gtyped.hpp>
#include <opencv2/gapi/gkernel.hpp>
#include <opencv2/gapi/operators.hpp>
// Include these files here to avoid cyclic dependency between
// Desync & GKernel & GComputation & GStreamingCompiled.
#include <opencv2/gapi/streaming/desync.hpp>
#include <opencv2/gapi/streaming/format.hpp>
#endif // OPENCV_GAPI_HPP
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,27 @@
// This file is part of OpenCV project.
// It is subject to the license terms in the LICENSE file found in the top-level directory
// of this distribution and at http://opencv.org/license.html.
//
// Copyright (C) 2018 Intel Corporation
#ifndef OPENCV_GAPI_CPU_CORE_API_HPP
#define OPENCV_GAPI_CPU_CORE_API_HPP
#include <opencv2/gapi/gkernel.hpp> // GKernelPackage
#include <opencv2/gapi/own/exports.hpp> // GAPI_EXPORTS
namespace cv {
namespace gapi {
namespace core {
namespace cpu {
GAPI_EXPORTS_W cv::GKernelPackage kernels();
} // namespace cpu
} // namespace core
} // namespace gapi
} // namespace cv
#endif // OPENCV_GAPI_CPU_CORE_API_HPP
@@ -0,0 +1,542 @@
// This file is part of OpenCV project.
// It is subject to the license terms in the LICENSE file found in the top-level directory
// of this distribution and at http://opencv.org/license.html.
//
// Copyright (C) 2018-2022 Intel Corporation
#ifndef OPENCV_GAPI_GCPUKERNEL_HPP
#define OPENCV_GAPI_GCPUKERNEL_HPP
#if defined _MSC_VER
#pragma warning(push)
#pragma warning(disable: 4702) // "Unreachable code" on postprocess(...) call inside OCVCallHelper
#endif
#include <functional>
#include <unordered_map>
#include <utility>
#include <vector>
#include <opencv2/core/mat.hpp>
#include <opencv2/gapi/gcommon.hpp>
#include <opencv2/gapi/gkernel.hpp>
#include <opencv2/gapi/garg.hpp>
#include <opencv2/gapi/gmetaarg.hpp>
#include <opencv2/gapi/util/compiler_hints.hpp> //suppress_unused_warning
#include <opencv2/gapi/util/util.hpp>
// FIXME: namespace scheme for backends?
namespace cv {
namespace gimpl
{
// Forward-declare an internal class
class GCPUExecutable;
} // namespace gimpl
namespace gapi
{
/**
* @brief This namespace contains G-API CPU backend functions,
* structures, and symbols.
*/
namespace cpu
{
/**
* \addtogroup gapi_std_backends
* @{
*
* @brief G-API backends available in this OpenCV version
*
* G-API backends play a corner stone role in G-API execution
* stack. Every backend is hardware-oriented and thus can run its
* kernels efficiently on the target platform.
*
* Backends are usually "black boxes" for G-API users -- on the API
* side, all backends are represented as different objects of the
* same class cv::gapi::GBackend.
* User can manipulate with backends by specifying which kernels to use.
*
* @sa @ref gapi_hld
*/
/**
* @brief Get a reference to CPU (OpenCV) backend.
*
* This is the default backend in G-API at the moment, providing
* broader functional coverage but losing some graph model
* advantages. Provided mostly for reference and prototyping
* purposes.
*
* @sa gapi_std_backends
*/
GAPI_EXPORTS cv::gapi::GBackend backend();
/** @} */
class GOCVFunctor;
//! @cond IGNORED
template<typename K, typename Callable>
GOCVFunctor ocv_kernel(const Callable& c);
template<typename K, typename Callable>
GOCVFunctor ocv_kernel(Callable& c);
//! @endcond
} // namespace cpu
} // namespace gapi
// Represents arguments which are passed to a wrapped CPU function
// FIXME: put into detail?
class GAPI_EXPORTS GCPUContext
{
public:
// Generic accessor API
template<typename T>
const T& inArg(int input) { return m_args.at(input).get<T>(); }
// Syntax sugar
const cv::Mat& inMat(int input);
cv::Mat& outMatR(int output); // FIXME: Avoid cv::Mat m = ctx.outMatR()
const cv::Scalar& inVal(int input);
cv::Scalar& outValR(int output); // FIXME: Avoid cv::Scalar s = ctx.outValR()
cv::MediaFrame& outFrame(int output);
template<typename T> std::vector<T>& outVecR(int output) // FIXME: the same issue
{
return outVecRef(output).wref<T>();
}
template<typename T> T& outOpaqueR(int output) // FIXME: the same issue
{
return outOpaqueRef(output).wref<T>();
}
GArg state()
{
return m_state;
}
protected:
detail::VectorRef& outVecRef(int output);
detail::OpaqueRef& outOpaqueRef(int output);
std::vector<GArg> m_args;
GArg m_state;
//FIXME: avoid conversion of arguments from internal representation to OpenCV one on each call
//to OCV kernel. (This can be achieved by a two single time conversions in GCPUExecutable::run,
//once on enter for input and output arguments, and once before return for output arguments only
std::unordered_map<std::size_t, GRunArgP> m_results;
friend class gimpl::GCPUExecutable;
};
class GAPI_EXPORTS GCPUKernel
{
public:
// This function is a kernel's execution entry point (does the processing work)
using RunF = std::function<void(GCPUContext &)>;
// This function is a stateful kernel's setup routine (configures state)
using SetupF = std::function<void(const GMetaArgs &, const GArgs &,
GArg &, const GCompileArgs &)>;
GCPUKernel();
GCPUKernel(const RunF& runF, const SetupF& setupF = nullptr);
RunF m_runF = nullptr;
SetupF m_setupF = nullptr;
bool m_isStateful = false;
};
// FIXME: This is an ugly ad-hoc implementation. TODO: refactor
namespace detail
{
template<class T> struct get_in;
template<> struct get_in<cv::GMat>
{
static cv::Mat get(GCPUContext &ctx, int idx) { return ctx.inMat(idx); }
};
template<> struct get_in<cv::GMatP>
{
static cv::Mat get(GCPUContext &ctx, int idx) { return get_in<cv::GMat>::get(ctx, idx); }
};
template<> struct get_in<cv::GFrame>
{
static cv::MediaFrame get(GCPUContext &ctx, int idx) { return ctx.inArg<cv::MediaFrame>(idx); }
};
template<> struct get_in<cv::GScalar>
{
static cv::Scalar get(GCPUContext &ctx, int idx) { return ctx.inVal(idx); }
};
template<typename U> struct get_in<cv::GArray<U> >
{
static const std::vector<U>& get(GCPUContext &ctx, int idx) { return ctx.inArg<VectorRef>(idx).rref<U>(); }
};
template<typename U> struct get_in<cv::GOpaque<U> >
{
static const U& get(GCPUContext &ctx, int idx) { return ctx.inArg<OpaqueRef>(idx).rref<U>(); }
};
//FIXME(dm): GArray<Mat>/GArray<GMat> conversion should be done more gracefully in the system
template<> struct get_in<cv::GArray<cv::GMat> >: public get_in<cv::GArray<cv::Mat> >
{
};
//FIXME(dm): GArray<Scalar>/GArray<GScalar> conversion should be done more gracefully in the system
template<> struct get_in<cv::GArray<cv::GScalar> >: public get_in<cv::GArray<cv::Scalar> >
{
};
// FIXME(dm): GArray<vector<U>>/GArray<GArray<U>> conversion should be done more gracefully in the system
template<typename U> struct get_in<cv::GArray<cv::GArray<U>> >: public get_in<cv::GArray<std::vector<U>> >
{
};
//FIXME(dm): GOpaque<Mat>/GOpaque<GMat> conversion should be done more gracefully in the system
template<> struct get_in<cv::GOpaque<cv::GMat> >: public get_in<cv::GOpaque<cv::Mat> >
{
};
//FIXME(dm): GOpaque<Scalar>/GOpaque<GScalar> conversion should be done more gracefully in the system
template<> struct get_in<cv::GOpaque<cv::GScalar> >: public get_in<cv::GOpaque<cv::Mat> >
{
};
template<class T> struct get_in
{
static T get(GCPUContext &ctx, int idx) { return ctx.inArg<T>(idx); }
};
struct tracked_cv_mat{
tracked_cv_mat(cv::Mat& m) : r{m}, original_data{m.data} {}
cv::Mat r;
uchar* original_data;
operator cv::Mat& (){ return r;}
void validate() const{
if (r.data != original_data)
{
util::throw_error
(std::logic_error
("OpenCV kernel output parameter was reallocated. \n"
"Incorrect meta data was provided ?"));
}
}
};
template<typename... Outputs>
void postprocess(Outputs&... outs)
{
struct
{
void operator()(tracked_cv_mat* bm) { bm->validate(); }
void operator()(...) { }
} validate;
//dummy array to unfold parameter pack
int dummy[] = { 0, (validate(&outs), 0)... };
cv::util::suppress_unused_warning(dummy);
}
template<class T> struct get_out;
template<> struct get_out<cv::GMat>
{
static tracked_cv_mat get(GCPUContext &ctx, int idx)
{
auto& r = ctx.outMatR(idx);
return {r};
}
};
template<> struct get_out<cv::GMatP>
{
static tracked_cv_mat get(GCPUContext &ctx, int idx)
{
return get_out<cv::GMat>::get(ctx, idx);
}
};
template<> struct get_out<cv::GScalar>
{
static cv::Scalar& get(GCPUContext &ctx, int idx)
{
return ctx.outValR(idx);
}
};
template<> struct get_out<cv::GFrame>
{
static cv::MediaFrame& get(GCPUContext &ctx, int idx)
{
return ctx.outFrame(idx);
}
};
template<typename U> struct get_out<cv::GArray<U>>
{
static std::vector<U>& get(GCPUContext &ctx, int idx)
{
return ctx.outVecR<U>(idx);
}
};
//FIXME(dm): GArray<Mat>/GArray<GMat> conversion should be done more gracefully in the system
template<> struct get_out<cv::GArray<cv::GMat> >: public get_out<cv::GArray<cv::Mat> >
{
};
// FIXME(dm): GArray<vector<U>>/GArray<GArray<U>> conversion should be done more gracefully in the system
template<typename U> struct get_out<cv::GArray<cv::GArray<U>> >: public get_out<cv::GArray<std::vector<U>> >
{
};
template<typename U> struct get_out<cv::GOpaque<U>>
{
static U& get(GCPUContext &ctx, int idx)
{
return ctx.outOpaqueR<U>(idx);
}
};
template<typename, typename>
struct OCVSetupHelper;
template<typename Impl, typename... Ins>
struct OCVSetupHelper<Impl, std::tuple<Ins...>>
{
// Using 'auto' return type and 'decltype' specifier in both 'setup_impl' versions
// to check existence of required 'Impl::setup' functions.
// While 'decltype' specifier accepts expression we pass expression with 'comma-operator'
// where first operand of comma-operator is call attempt to desired 'Impl::setup' and
// the second operand is 'void()' expression.
//
// SFINAE for 'Impl::setup' which accepts compile arguments.
template<int... IIs>
static auto setup_impl(const GMetaArgs &metaArgs, const GArgs &args,
GArg &state, const GCompileArgs &compileArgs,
detail::Seq<IIs...>) ->
decltype(Impl::setup(detail::get_in_meta<Ins>(metaArgs, args, IIs)...,
std::declval<typename std::add_lvalue_reference<
std::shared_ptr<typename Impl::State>
>::type
>(),
compileArgs)
, void())
{
// TODO: unique_ptr <-> shared_ptr conversion ?
// To check: Conversion is possible only if the state which should be passed to
// 'setup' user callback isn't required to have previous value
std::shared_ptr<typename Impl::State> stPtr;
Impl::setup(detail::get_in_meta<Ins>(metaArgs, args, IIs)..., stPtr, compileArgs);
state = GArg(stPtr);
}
// SFINAE for 'Impl::setup' which doesn't accept compile arguments.
template<int... IIs>
static auto setup_impl(const GMetaArgs &metaArgs, const GArgs &args,
GArg &state, const GCompileArgs &/* compileArgs */,
detail::Seq<IIs...>) ->
decltype(Impl::setup(detail::get_in_meta<Ins>(metaArgs, args, IIs)...,
std::declval<typename std::add_lvalue_reference<
std::shared_ptr<typename Impl::State>
>::type
>()
)
, void())
{
// The same comment as in 'setup' above.
std::shared_ptr<typename Impl::State> stPtr;
Impl::setup(detail::get_in_meta<Ins>(metaArgs, args, IIs)..., stPtr);
state = GArg(stPtr);
}
static void setup(const GMetaArgs &metaArgs, const GArgs &args,
GArg& state, const GCompileArgs &compileArgs)
{
setup_impl(metaArgs, args, state, compileArgs,
typename detail::MkSeq<sizeof...(Ins)>::type());
}
};
// OCVCallHelper is a helper class to call stateless OCV kernels and OCV kernel functors.
template<typename, typename, typename>
struct OCVCallHelper;
// FIXME: probably can be simplified with std::apply or analogue.
template<typename Impl, typename... Ins, typename... Outs>
struct OCVCallHelper<Impl, std::tuple<Ins...>, std::tuple<Outs...>>
{
template<typename... Inputs>
struct call_and_postprocess
{
template<typename... Outputs>
static void call(Inputs&&... ins, Outputs&&... outs)
{
//not using a std::forward on outs is deliberate in order to
//cause compilation error, by trying to bind rvalue references to lvalue references
Impl::run(std::forward<Inputs>(ins)..., outs...);
postprocess(outs...);
}
template<typename... Outputs>
static void call(Impl& impl, Inputs&&... ins, Outputs&&... outs)
{
impl(std::forward<Inputs>(ins)..., outs...);
}
};
template<int... IIs, int... OIs>
static void call_impl(GCPUContext &ctx, detail::Seq<IIs...>, detail::Seq<OIs...>)
{
//Make sure that OpenCV kernels do not reallocate memory for output parameters
//by comparing it's state (data ptr) before and after the call.
//This is done by converting each output Mat into tracked_cv_mat object, and binding
//them to parameters of ad-hoc function
call_and_postprocess<decltype(get_in<Ins>::get(ctx, IIs))...>
::call(get_in<Ins>::get(ctx, IIs)..., get_out<Outs>::get(ctx, OIs)...);
}
template<int... IIs, int... OIs>
static void call_impl(cv::GCPUContext &ctx, Impl& impl,
detail::Seq<IIs...>, detail::Seq<OIs...>)
{
call_and_postprocess<decltype(get_in<Ins>::get(ctx, IIs))...>
::call(impl, get_in<Ins>::get(ctx, IIs)..., get_out<Outs>::get(ctx, OIs)...);
}
static void call(GCPUContext &ctx)
{
call_impl(ctx,
typename detail::MkSeq<sizeof...(Ins)>::type(),
typename detail::MkSeq<sizeof...(Outs)>::type());
}
// NB: Same as call but calling the object
// This necessary for kernel implementations that have a state
// and are represented as an object
static void callFunctor(cv::GCPUContext &ctx, Impl& impl)
{
call_impl(ctx, impl,
typename detail::MkSeq<sizeof...(Ins)>::type(),
typename detail::MkSeq<sizeof...(Outs)>::type());
}
};
// OCVStCallHelper is a helper class to call stateful OCV kernels.
template<typename, typename, typename>
struct OCVStCallHelper;
template<typename Impl, typename... Ins, typename... Outs>
struct OCVStCallHelper<Impl, std::tuple<Ins...>, std::tuple<Outs...>> :
OCVCallHelper<Impl, std::tuple<Ins...>, std::tuple<Outs...>>
{
template<typename... Inputs>
struct call_and_postprocess
{
template<typename... Outputs>
static void call(typename Impl::State& st, Inputs&&... ins, Outputs&&... outs)
{
Impl::run(std::forward<Inputs>(ins)..., outs..., st);
postprocess(outs...);
}
};
template<int... IIs, int... OIs>
static void call_impl(GCPUContext &ctx, detail::Seq<IIs...>, detail::Seq<OIs...>)
{
auto& st = *ctx.state().get<std::shared_ptr<typename Impl::State>>();
call_and_postprocess<decltype(get_in<Ins>::get(ctx, IIs))...>
::call(st, get_in<Ins>::get(ctx, IIs)..., get_out<Outs>::get(ctx, OIs)...);
}
static void call(GCPUContext &ctx)
{
call_impl(ctx,
typename detail::MkSeq<sizeof...(Ins)>::type(),
typename detail::MkSeq<sizeof...(Outs)>::type());
}
};
} // namespace detail
template<class Impl, class K>
class GCPUKernelImpl: public cv::detail::KernelTag
{
using CallHelper = cv::detail::OCVCallHelper<Impl, typename K::InArgs, typename K::OutArgs>;
public:
using API = K;
static cv::gapi::GBackend backend() { return cv::gapi::cpu::backend(); }
static cv::GCPUKernel kernel() { return GCPUKernel(&CallHelper::call); }
};
template<class Impl, class K, class S>
class GCPUStKernelImpl: public cv::detail::KernelTag
{
using StSetupHelper = detail::OCVSetupHelper<Impl, typename K::InArgs>;
using StCallHelper = detail::OCVStCallHelper<Impl, typename K::InArgs, typename K::OutArgs>;
public:
using API = K;
using State = S;
static cv::gapi::GBackend backend() { return cv::gapi::cpu::backend(); }
static cv::GCPUKernel kernel() { return GCPUKernel(&StCallHelper::call,
&StSetupHelper::setup); }
};
#define GAPI_OCV_KERNEL(Name, API) struct Name: public cv::GCPUKernelImpl<Name, API>
// TODO: Reuse Anatoliy's logic for support of types with commas in macro.
// Retrieve the common part from Anatoliy's logic to the separate place.
#define GAPI_OCV_KERNEL_ST(Name, API, State) \
struct Name: public cv::GCPUStKernelImpl<Name, API, State> \
/// @private
class gapi::cpu::GOCVFunctor : public gapi::GFunctor
{
public:
using Impl = std::function<void(GCPUContext &)>;
using Meta = cv::GKernel::M;
GOCVFunctor(const char* id, const Meta &meta, const Impl& impl)
: gapi::GFunctor(id), impl_{GCPUKernel(impl), meta}
{
}
GKernelImpl impl() const override { return impl_; }
gapi::GBackend backend() const override { return gapi::cpu::backend(); }
private:
GKernelImpl impl_;
};
//! @cond IGNORED
template<typename K, typename Callable>
gapi::cpu::GOCVFunctor gapi::cpu::ocv_kernel(Callable& c)
{
using P = cv::detail::OCVCallHelper<Callable, typename K::InArgs, typename K::OutArgs>;
return GOCVFunctor{ K::id()
, &K::getOutMeta
, std::bind(&P::callFunctor, std::placeholders::_1, std::ref(c))
};
}
template<typename K, typename Callable>
gapi::cpu::GOCVFunctor gapi::cpu::ocv_kernel(const Callable& c)
{
using P = cv::detail::OCVCallHelper<Callable, typename K::InArgs, typename K::OutArgs>;
return GOCVFunctor{ K::id()
, &K::getOutMeta
, std::bind(&P::callFunctor, std::placeholders::_1, c)
};
}
//! @endcond
} // namespace cv
#if defined _MSC_VER
#pragma warning(pop)
#endif
#endif // OPENCV_GAPI_GCPUKERNEL_HPP
@@ -0,0 +1,27 @@
// This file is part of OpenCV project.
// It is subject to the license terms in the LICENSE file found in the top-level directory
// of this distribution and at http://opencv.org/license.html.
//
// Copyright (C) 2018 Intel Corporation
#ifndef OPENCV_GAPI_CPU_IMGPROC_API_HPP
#define OPENCV_GAPI_CPU_IMGPROC_API_HPP
#include <opencv2/core/cvdef.h> // GAPI_EXPORTS
#include <opencv2/gapi/gkernel.hpp> // GKernelPackage
namespace cv {
namespace gapi {
namespace imgproc {
namespace cpu {
GAPI_EXPORTS GKernelPackage kernels();
} // namespace cpu
} // namespace imgproc
} // namespace gapi
} // namespace cv
#endif // OPENCV_GAPI_CPU_IMGPROC_API_HPP
@@ -0,0 +1,48 @@
// This file is part of OpenCV project.
// It is subject to the license terms in the LICENSE file found in the top-level directory
// of this distribution and at http://opencv.org/license.html.
//
// Copyright (C) 2021 Intel Corporation
#ifndef OPENCV_GAPI_CPU_STEREO_API_HPP
#define OPENCV_GAPI_CPU_STEREO_API_HPP
#include <opencv2/gapi/gkernel.hpp> // GKernelPackage
namespace cv {
namespace gapi {
namespace calib3d {
namespace cpu {
GAPI_EXPORTS GKernelPackage kernels();
/** @brief Structure for the Stereo operation initialization parameters.*/
struct GAPI_EXPORTS StereoInitParam {
StereoInitParam(int nD, int bS, double bL, double f):
numDisparities(nD), blockSize(bS), baseline(bL), focus(f) {}
StereoInitParam() = default;
int numDisparities = 0;
int blockSize = 21;
double baseline = 63.5;
double focus = 3.6;
};
} // namespace cpu
} // namespace calib3d
} // namespace gapi
namespace detail {
template<> struct CompileArgTag<cv::gapi::calib3d::cpu::StereoInitParam> {
static const char* tag() {
return "org.opencv.stereoInit";
}
};
} // namespace detail
} // namespace cv
#endif // OPENCV_GAPI_CPU_STEREO_API_HPP
@@ -0,0 +1,25 @@
// This file is part of OpenCV project.
// It is subject to the license terms in the LICENSE file found in the top-level directory
// of this distribution and at http://opencv.org/license.html.
//
// Copyright (C) 2020 Intel Corporation
#ifndef OPENCV_GAPI_CPU_VIDEO_API_HPP
#define OPENCV_GAPI_CPU_VIDEO_API_HPP
#include <opencv2/gapi/gkernel.hpp> // GKernelPackage
namespace cv {
namespace gapi {
namespace video {
namespace cpu {
GAPI_EXPORTS GKernelPackage kernels();
} // namespace cpu
} // namespace video
} // namespace gapi
} // namespace cv
#endif // OPENCV_GAPI_CPU_VIDEO_API_HPP
@@ -0,0 +1,20 @@
// This file is part of OpenCV project.
// It is subject to the license terms in the LICENSE file found in the top-level directory
// of this distribution and at http://opencv.org/license.html.
//
// Copyright (C) 2018 Intel Corporation
#ifndef OPENCV_GAPI_FLUID_CORE_HPP
#define OPENCV_GAPI_FLUID_CORE_HPP
#include <opencv2/gapi/gkernel.hpp> // GKernelPackage
#include <opencv2/gapi/own/exports.hpp> // GAPI_EXPORTS
namespace cv { namespace gapi { namespace core { namespace fluid {
GAPI_EXPORTS_W cv::GKernelPackage kernels();
}}}}
#endif // OPENCV_GAPI_FLUID_CORE_HPP
@@ -0,0 +1,154 @@
// This file is part of OpenCV project.
// It is subject to the license terms in the LICENSE file found in the top-level directory
// of this distribution and at http://opencv.org/license.html.
//
// Copyright (C) 2018 Intel Corporation
#ifndef OPENCV_GAPI_FLUID_BUFFER_HPP
#define OPENCV_GAPI_FLUID_BUFFER_HPP
#include <list>
#include <numeric> // accumulate
#include <ostream> // ostream
#include <cstdint> // uint8_t
#include <opencv2/gapi/opencv_includes.hpp>
#include <opencv2/gapi/gmat.hpp>
#include <opencv2/gapi/util/optional.hpp>
namespace cv {
namespace gapi {
namespace fluid {
struct Border
{
// This constructor is required to support existing kernels which are part of G-API
Border(int _type, cv::Scalar _val) : type(_type), value(_val) {}
int type;
cv::Scalar value;
};
using BorderOpt = util::optional<Border>;
bool operator == (const Border& b1, const Border& b2);
class GAPI_EXPORTS Buffer;
class GAPI_EXPORTS View
{
public:
struct Cache
{
std::vector<const uint8_t*> m_linePtrs;
GMatDesc m_desc;
int m_border_size = 0;
inline const uint8_t* linePtr(int index) const
{
// "out_of_window" check:
// user must not request the lines which are outside of specified kernel window
GAPI_DbgAssert(index >= -m_border_size
&& index < -m_border_size + static_cast<int>(m_linePtrs.size()));
return m_linePtrs[index + m_border_size];
}
};
const inline uint8_t* InLineB(int index) const // -(w-1)/2...0...+(w-1)/2 for Filters
{
return m_cache->linePtr(index);
}
template<typename T> const inline T* InLine(int i) const
{
const uint8_t* ptr = this->InLineB(i);
return reinterpret_cast<const T*>(ptr);
}
inline operator bool() const { return m_priv != nullptr; }
bool ready() const;
inline int length() const { return m_cache->m_desc.size.width; }
int y() const;
inline const GMatDesc& meta() const { return m_cache->m_desc; }
class GAPI_EXPORTS Priv; // internal use only
Priv& priv(); // internal use only
const Priv& priv() const; // internal use only
View();
View(std::unique_ptr<Priv>&& p);
View(View&& v);
View& operator=(View&& v);
~View();
private:
std::unique_ptr<Priv> m_priv;
const Cache* m_cache = nullptr;
};
class GAPI_EXPORTS Buffer
{
public:
struct Cache
{
std::vector<uint8_t*> m_linePtrs;
GMatDesc m_desc;
};
// Default constructor (executable creation stage,
// all following initialization performed in Priv::init())
Buffer();
// Scratch constructor (user kernels)
Buffer(const cv::GMatDesc &desc);
// Constructor for intermediate buffers (for tests)
Buffer(const cv::GMatDesc &desc,
int max_line_consumption, int border_size,
int skew,
int wlpi,
BorderOpt border);
// Constructor for in/out buffers (for tests)
Buffer(const cv::Mat &data, bool is_input);
~Buffer();
Buffer& operator=(Buffer&&);
inline uint8_t* OutLineB(int index = 0)
{
return m_cache->m_linePtrs[index];
}
template<typename T> inline T* OutLine(int index = 0)
{
uint8_t* ptr = this->OutLineB(index);
return reinterpret_cast<T*>(ptr);
}
int y() const;
int linesReady() const;
void debug(std::ostream &os) const;
inline int length() const { return m_cache->m_desc.size.width; }
int lpi() const; // LPI for WRITER
inline const GMatDesc& meta() const { return m_cache->m_desc; }
View mkView(int borderSize, bool ownStorage);
void addView(const View* v);
class GAPI_EXPORTS Priv; // internal use only
Priv& priv(); // internal use only
const Priv& priv() const; // internal use only
private:
std::unique_ptr<Priv> m_priv;
const Cache* m_cache;
};
} // namespace cv::gapi::fluid
} // namespace cv::gapi
} // namespace cv
#endif // OPENCV_GAPI_FLUID_BUFFER_HPP
@@ -0,0 +1,442 @@
// This file is part of OpenCV project.
// It is subject to the license terms in the LICENSE file found in the top-level directory
// of this distribution and at http://opencv.org/license.html.
//
// Copyright (C) 2018-2019 Intel Corporation
#ifndef OPENCV_GAPI_FLUID_KERNEL_HPP
#define OPENCV_GAPI_FLUID_KERNEL_HPP
#include <vector>
#include <functional>
#include <map>
#include <unordered_map>
#include <opencv2/gapi/opencv_includes.hpp>
#include <opencv2/gapi/gcommon.hpp>
#include <opencv2/gapi/gkernel.hpp>
#include <opencv2/gapi/garg.hpp>
#include <opencv2/gapi/fluid/gfluidbuffer.hpp>
// FIXME: namespace scheme for backends?
namespace cv {
namespace gapi
{
/**
* @brief This namespace contains G-API Fluid backend functions, structures, and symbols.
*/
namespace fluid
{
/**
* \addtogroup gapi_std_backends G-API Standard Backends
* @{
*/
/**
* @brief Get a reference to Fluid backend.
*
* @sa gapi_std_backends
*/
GAPI_EXPORTS cv::gapi::GBackend backend();
/** @} */
} // namespace fluid
} // namespace gapi
class GAPI_EXPORTS GFluidKernel
{
public:
enum class Kind
{
Filter,
Resize,
YUV420toRGB //Color conversion of 4:2:0 chroma sub-sampling formats (NV12, I420 ..etc) to RGB
};
// This function is a generic "doWork" callback
using F = std::function<void(const cv::GArgs&, const std::vector<gapi::fluid::Buffer*> &)>;
// This function is a generic "initScratch" callback
using IS = std::function<void(const cv::GMetaArgs &, const cv::GArgs&, gapi::fluid::Buffer &)>;
// This function is a generic "resetScratch" callback
using RS = std::function<void(gapi::fluid::Buffer &)>;
// This function describes kernel metadata inference rule.
using M = std::function<GMetaArgs(const GMetaArgs &, const GArgs &)>;
// This function is a generic "getBorder" callback (extracts border-related data from kernel's input parameters)
using B = std::function<gapi::fluid::BorderOpt(const GMetaArgs&, const GArgs&)>;
// This function is a generic "getWindow" callback (extracts window-related data from kernel's input parameters)
using GW = std::function<int(const GMetaArgs&, const GArgs&)>;
// FIXME: move implementations out of header file
GFluidKernel() {}
GFluidKernel(Kind k, int l, bool scratch, const F& f, const IS &is, const RS &rs, const B& b, const GW& win)
: m_kind(k)
, m_lpi(l)
, m_scratch(scratch)
, m_f(f)
, m_is(is)
, m_rs(rs)
, m_b(b)
, m_gw(win) {}
Kind m_kind;
const int m_lpi = -1;
const bool m_scratch = false;
const F m_f;
const IS m_is;
const RS m_rs;
const B m_b;
const GW m_gw;
};
// FIXME!!!
// This is the temporary and experimental API
// which should be replaced by runtime roi-based scheduling
/** \addtogroup gapi_compile_args
* @{
*/
/**
* @brief This structure allows to control the output image region
* which Fluid backend will produce in the graph.
*
* This feature is useful for external tiling and parallelism, but
* will be deprecated in the future releases.
*/
struct GFluidOutputRois
{
std::vector<cv::Rect> rois;
};
/**
* @brief This structure forces Fluid backend to generate multiple
* parallel output regions in the graph. These regions execute in parallel.
*
* This feature may be deprecated in the future releases.
*/
struct GFluidParallelOutputRois
{
std::vector<GFluidOutputRois> parallel_rois;
};
/**
* @brief This structure allows to customize the way how Fluid executes
* parallel regions.
*
* For example, user can utilize his own threading runtime via this parameter.
* The `parallel_for` member functor is called by the Fluid runtime with the
* following arguments:
*
* @param size Size of the parallel range to process
* @param f A function which should be called for every integer index
* in this range by the specified parallel_for implementation.
*
* This feature may be deprecated in the future releases.
*/
struct GFluidParallelFor
{
//this function accepts:
// - size of the "parallel" range as the first argument
// - and a function to be called on the range items, designated by item index
std::function<void(std::size_t size, std::function<void(std::size_t index)>)> parallel_for;
};
/** @} gapi_compile_args */
namespace detail
{
template<> struct CompileArgTag<GFluidOutputRois>
{
static const char* tag() { return "gapi.fluid.outputRois"; }
};
template<> struct CompileArgTag<GFluidParallelFor>
{
static const char* tag() { return "gapi.fluid.parallelFor"; }
};
template<> struct CompileArgTag<GFluidParallelOutputRois>
{
static const char* tag() { return "gapi.fluid.parallelOutputRois"; }
};
} // namespace detail
namespace detail
{
template<class T> struct fluid_get_in;
template<> struct fluid_get_in<cv::GMat>
{
static const cv::gapi::fluid::View& get(const cv::GArgs &in_args, int idx)
{
return *in_args[idx].unsafe_get<cv::gapi::fluid::View*>();
}
};
template<> struct fluid_get_in<cv::GScalar>
{
// FIXME: change to return by reference when moved to own::Scalar
static cv::Scalar get(const cv::GArgs &in_args, int idx)
{
return in_args[idx].unsafe_get<cv::Scalar>();
}
};
template<typename U> struct fluid_get_in<cv::GArray<U>>
{
static const std::vector<U>& get(const cv::GArgs &in_args, int idx)
{
return in_args.at(idx).unsafe_get<cv::detail::VectorRef>().rref<U>();
}
};
template<typename U> struct fluid_get_in<cv::GOpaque<U>>
{
static const U& get(const cv::GArgs &in_args, int idx)
{
return in_args.at(idx).unsafe_get<cv::detail::OpaqueRef>().rref<U>();
}
};
template<class T> struct fluid_get_in
{
static const T& get(const cv::GArgs &in_args, int idx)
{
return in_args[idx].unsafe_get<T>();
}
};
template<bool, typename Impl, typename... Ins>
struct scratch_helper;
template<typename Impl, typename... Ins>
struct scratch_helper<true, Impl, Ins...>
{
// Init
template<int... IIs>
static void help_init_impl(const cv::GMetaArgs &metas,
const cv::GArgs &in_args,
gapi::fluid::Buffer &scratch_buf,
detail::Seq<IIs...>)
{
Impl::initScratch(get_in_meta<Ins>(metas, in_args, IIs)..., scratch_buf);
}
static void help_init(const cv::GMetaArgs &metas,
const cv::GArgs &in_args,
gapi::fluid::Buffer &b)
{
help_init_impl(metas, in_args, b, typename detail::MkSeq<sizeof...(Ins)>::type());
}
// Reset
static void help_reset(gapi::fluid::Buffer &b)
{
Impl::resetScratch(b);
}
};
template<typename Impl, typename... Ins>
struct scratch_helper<false, Impl, Ins...>
{
static void help_init(const cv::GMetaArgs &,
const cv::GArgs &,
gapi::fluid::Buffer &)
{
GAPI_Error("InternalError");
}
static void help_reset(gapi::fluid::Buffer &)
{
GAPI_Error("InternalError");
}
};
template<typename T> struct is_gmat_type
{
static const constexpr bool value = std::is_same<cv::GMat, T>::value;
};
template<bool CallCustomGetBorder, typename Impl, typename... Ins>
struct get_border_helper;
template<typename Impl, typename... Ins>
struct get_border_helper<true, Impl, Ins...>
{
template<int... IIs>
static gapi::fluid::BorderOpt get_border_impl(const GMetaArgs &metas,
const cv::GArgs &in_args,
cv::detail::Seq<IIs...>)
{
return util::make_optional(Impl::getBorder(cv::detail::get_in_meta<Ins>(metas, in_args, IIs)...));
}
static gapi::fluid::BorderOpt help(const GMetaArgs &metas,
const cv::GArgs &in_args)
{
return get_border_impl(metas, in_args, typename detail::MkSeq<sizeof...(Ins)>::type());
}
};
template<typename Impl, typename... Ins>
struct get_border_helper<false, Impl, Ins...>
{
static gapi::fluid::BorderOpt help(const cv::GMetaArgs &,
const cv::GArgs &)
{
return {};
}
};
template<bool CallCustomGetWindow, typename, typename... Ins>
struct get_window_helper;
template<typename Impl, typename... Ins>
struct get_window_helper<true, Impl, Ins...>
{
template<int... IIs>
static int get_window_impl(const GMetaArgs &metas,
const cv::GArgs &in_args,
cv::detail::Seq<IIs...>)
{
return Impl::getWindow(cv::detail::get_in_meta<Ins>(metas, in_args, IIs)...);
}
static int help(const GMetaArgs &metas, const cv::GArgs &in_args)
{
return get_window_impl(metas, in_args, typename detail::MkSeq<sizeof...(Ins)>::type());
}
};
template<typename Impl, typename... Ins>
struct get_window_helper<false, Impl, Ins...>
{
static int help(const cv::GMetaArgs &,
const cv::GArgs &)
{
return Impl::Window;
}
};
template<typename C, typename T>
struct has_Window
{
private:
template<class U>
static constexpr auto Check(U*) -> typename std::is_same<decltype(U::Window), T>::type;
template<typename>
static constexpr std::false_type Check(...);
typedef decltype(Check<C>(0)) Result;
public:
static constexpr bool value = Result::value;
};
template<bool hasWindow, typename Impl>
struct callCustomGetBorder;
template<typename Impl>
struct callCustomGetBorder<true, Impl>
{
static constexpr bool value = (Impl::Window != 1);
};
template<typename Impl>
struct callCustomGetBorder<false, Impl>
{
static constexpr bool value = true;
};
template<typename, typename, typename, bool UseScratch>
struct FluidCallHelper;
template<typename Impl, typename... Ins, typename... Outs, bool UseScratch>
struct FluidCallHelper<Impl, std::tuple<Ins...>, std::tuple<Outs...>, UseScratch>
{
static_assert(all_satisfy<is_gmat_type, Outs...>::value, "return type must be GMat");
static_assert(contains<GMat, Ins...>::value, "input must contain at least one GMat");
// Execution dispatcher ////////////////////////////////////////////////////
template<int... IIs, int... OIs>
static void call_impl(const cv::GArgs &in_args,
const std::vector<gapi::fluid::Buffer*> &out_bufs,
detail::Seq<IIs...>,
detail::Seq<OIs...>)
{
Impl::run(fluid_get_in<Ins>::get(in_args, IIs)..., *out_bufs[OIs]...);
}
static void call(const cv::GArgs &in_args,
const std::vector<gapi::fluid::Buffer*> &out_bufs)
{
constexpr int numOuts = (sizeof...(Outs)) + (UseScratch ? 1 : 0);
call_impl(in_args, out_bufs,
typename detail::MkSeq<sizeof...(Ins)>::type(),
typename detail::MkSeq<numOuts>::type());
}
// Scratch buffer initialization dispatcher ////////////////////////////////
static void init_scratch(const GMetaArgs &metas,
const cv::GArgs &in_args,
gapi::fluid::Buffer &b)
{
scratch_helper<UseScratch, Impl, Ins...>::help_init(metas, in_args, b);
}
// Scratch buffer reset dispatcher /////////////////////////////////////////
static void reset_scratch(gapi::fluid::Buffer &scratch_buf)
{
scratch_helper<UseScratch, Impl, Ins...>::help_reset(scratch_buf);
}
static gapi::fluid::BorderOpt getBorder(const GMetaArgs &metas, const cv::GArgs &in_args)
{
constexpr bool hasWindow = has_Window<Impl, const int>::value;
// User must provide "init" callback if Window != 1
// TODO: move to constexpr if when we enable C++17
return get_border_helper<callCustomGetBorder<hasWindow, Impl>::value, Impl, Ins...>::help(metas, in_args);
}
static int getWindow(const GMetaArgs &metas, const cv::GArgs &in_args)
{
constexpr bool callCustomGetWindow = !(has_Window<Impl, const int>::value);
return get_window_helper<callCustomGetWindow, Impl, Ins...>::help(metas, in_args);
}
};
} // namespace detail
template<class Impl, class K, bool UseScratch>
class GFluidKernelImpl : public cv::detail::KernelTag
{
static const int LPI = 1;
static const auto Kind = GFluidKernel::Kind::Filter;
using P = detail::FluidCallHelper<Impl, typename K::InArgs, typename K::OutArgs, UseScratch>;
public:
using API = K;
static GFluidKernel kernel()
{
// FIXME: call() and getOutMeta() needs to be renamed so it is clear these
// functions are internal wrappers, not user API
return GFluidKernel(Impl::Kind, Impl::LPI,
UseScratch,
&P::call, &P::init_scratch, &P::reset_scratch, &P::getBorder, &P::getWindow);
}
static cv::gapi::GBackend backend() { return cv::gapi::fluid::backend(); }
};
#define GAPI_FLUID_KERNEL(Name, API, Scratch) struct Name: public cv::GFluidKernelImpl<Name, API, Scratch>
} // namespace cv
#endif // OPENCV_GAPI_GCPUKERNEL_HPP
@@ -0,0 +1,20 @@
// This file is part of OpenCV project.
// It is subject to the license terms in the LICENSE file found in the top-level directory
// of this distribution and at http://opencv.org/license.html.
//
// Copyright (C) 2018 Intel Corporation
#ifndef OPENCV_GAPI_FLUID_IMGPROC_HPP
#define OPENCV_GAPI_FLUID_IMGPROC_HPP
#include <opencv2/gapi/gkernel.hpp> // GKernelPackage
#include <opencv2/gapi/own/exports.hpp> // GAPI_EXPORTS
namespace cv { namespace gapi { namespace imgproc { namespace fluid {
GAPI_EXPORTS_W GKernelPackage kernels();
}}}}
#endif // OPENCV_GAPI_FLUID_IMGPROC_HPP
@@ -0,0 +1,309 @@
// This file is part of OpenCV project.
// It is subject to the license terms in the LICENSE file found in the top-level directory
// of this distribution and at http://opencv.org/license.html.
//
// Copyright (C) 2018-2021 Intel Corporation
#ifndef OPENCV_GAPI_GARG_HPP
#define OPENCV_GAPI_GARG_HPP
#include <vector>
#include <unordered_map>
#include <type_traits>
#include <opencv2/gapi/opencv_includes.hpp>
#include <opencv2/gapi/own/mat.hpp>
#include <opencv2/gapi/media.hpp>
#include <opencv2/gapi/util/util.hpp>
#include <opencv2/gapi/util/any.hpp>
#include <opencv2/gapi/util/variant.hpp>
#include <opencv2/gapi/gmat.hpp>
#include <opencv2/gapi/gscalar.hpp>
#include <opencv2/gapi/garray.hpp>
#include <opencv2/gapi/gopaque.hpp>
#include <opencv2/gapi/gframe.hpp>
#include <opencv2/gapi/gtype_traits.hpp>
#include <opencv2/gapi/gmetaarg.hpp>
#include <opencv2/gapi/streaming/source.hpp>
#include <opencv2/gapi/rmat.hpp>
namespace cv {
class GArg;
namespace detail {
template<typename T>
using is_garg = std::is_same<GArg, typename std::decay<T>::type>;
}
// Parameter holder class for a node
// Depending on platform capabilities, can either support arbitrary types
// (as `boost::any`) or a limited number of types (as `boot::variant`).
// FIXME: put into "details" as a user shouldn't use it in his code
class GAPI_EXPORTS GArg
{
public:
GArg() {}
template<typename T, typename std::enable_if<!detail::is_garg<T>::value, int>::type = 0>
explicit GArg(const T &t)
: kind(detail::GTypeTraits<T>::kind)
, opaque_kind(detail::GOpaqueTraits<T>::kind)
, value(detail::wrap_gapi_helper<T>::wrap(t))
{
}
template<typename T, typename std::enable_if<!detail::is_garg<T>::value, int>::type = 0>
explicit GArg(T &&t)
: kind(detail::GTypeTraits<typename std::decay<T>::type>::kind)
, opaque_kind(detail::GOpaqueTraits<typename std::decay<T>::type>::kind)
, value(detail::wrap_gapi_helper<T>::wrap(t))
{
}
template<typename T> inline T& get()
{
return util::any_cast<typename std::remove_reference<T>::type>(value);
}
template<typename T> inline const T& get() const
{
return util::any_cast<typename std::remove_reference<T>::type>(value);
}
template<typename T> inline T& unsafe_get()
{
return util::unsafe_any_cast<typename std::remove_reference<T>::type>(value);
}
template<typename T> inline const T& unsafe_get() const
{
return util::unsafe_any_cast<typename std::remove_reference<T>::type>(value);
}
detail::ArgKind kind = detail::ArgKind::OPAQUE_VAL;
detail::OpaqueKind opaque_kind = detail::OpaqueKind::CV_UNKNOWN;
protected:
util::any value;
};
using GArgs = std::vector<GArg>;
// FIXME: Express as M<GProtoArg...>::type
// FIXME: Move to a separate file!
using GRunArgBase = util::variant<
#if !defined(GAPI_STANDALONE)
cv::UMat,
#endif // !defined(GAPI_STANDALONE)
cv::RMat,
cv::gapi::wip::IStreamSource::Ptr,
cv::Mat,
cv::Scalar,
cv::detail::VectorRef,
cv::detail::OpaqueRef,
cv::MediaFrame
>;
namespace detail {
template<typename,typename>
struct in_variant;
template<typename T, typename... Types>
struct in_variant<T, util::variant<Types...> >
: std::integral_constant<bool, cv::detail::contains<T, Types...>::value > {
};
} // namespace detail
struct GAPI_EXPORTS GRunArg: public GRunArgBase
{
// Metadata information here
using Meta = std::unordered_map<std::string, util::any>;
Meta meta;
// Mimic the old GRunArg semantics here, old of the times when
// GRunArg was an alias to variant<>
GRunArg();
GRunArg(const cv::GRunArg &arg);
GRunArg(cv::GRunArg &&arg);
GRunArg& operator= (const GRunArg &arg);
GRunArg& operator= (GRunArg &&arg);
template <typename T>
GRunArg(const T &t,
const Meta &m = Meta{},
typename std::enable_if< detail::in_variant<T, GRunArgBase>::value, int>::type = 0)
: GRunArgBase(t)
, meta(m)
{
}
template <typename T>
GRunArg(T &&t,
const Meta &m = Meta{},
typename std::enable_if< detail::in_variant<T, GRunArgBase>::value, int>::type = 0)
: GRunArgBase(std::move(t))
, meta(m)
{
}
template <typename T> auto operator= (const T &t)
-> typename std::enable_if< detail::in_variant<T, GRunArgBase>::value, cv::GRunArg>::type&
{
GRunArgBase::operator=(t);
return *this;
}
template <typename T> auto operator= (T&& t)
-> typename std::enable_if< detail::in_variant<T, GRunArgBase>::value, cv::GRunArg>::type&
{
GRunArgBase::operator=(std::move(t));
return *this;
}
};
using GRunArgs = std::vector<GRunArg>;
// TODO: Think about the addition operator
/**
* @brief This operator allows to complement the input vector at runtime.
*
* It's an ordinary overload of addition assignment operator.
*
* Example of usage:
* @snippet samples/cpp/tutorial_code/gapi/doc_snippets/dynamic_graph_snippets.cpp GRunArgs usage
*
*/
inline GRunArgs& operator += (GRunArgs &lhs, const GRunArgs &rhs)
{
lhs.reserve(lhs.size() + rhs.size());
lhs.insert(lhs.end(), rhs.begin(), rhs.end());
return lhs;
}
namespace gapi
{
namespace wip
{
/**
* @brief This aggregate type represents all types which G-API can
* handle (via variant).
*
* It only exists to overcome C++ language limitations (where a
* `using`-defined class can't be forward-declared).
*/
struct GAPI_EXPORTS Data: public GRunArg
{
using GRunArg::GRunArg;
template <typename T>
Data& operator= (const T& t) { GRunArg::operator=(t); return *this; }
template <typename T>
Data& operator= (T&& t) { GRunArg::operator=(std::move(t)); return *this; }
};
} // namespace wip
} // namespace gapi
using GRunArgP = util::variant<
#if !defined(GAPI_STANDALONE)
cv::UMat*,
#endif // !defined(GAPI_STANDALONE)
cv::Mat*,
cv::RMat*,
cv::Scalar*,
cv::MediaFrame*,
cv::detail::VectorRef,
cv::detail::OpaqueRef
>;
using GRunArgsP = std::vector<GRunArgP>;
// TODO: Think about the addition operator
/**
* @brief This operator allows to complement the output vector at runtime.
*
* It's an ordinary overload of addition assignment operator.
*
* Example of usage:
* @snippet samples/cpp/tutorial_code/gapi/doc_snippets/dynamic_graph_snippets.cpp GRunArgsP usage
*
*/
inline GRunArgsP& operator += (GRunArgsP &lhs, const GRunArgsP &rhs)
{
lhs.reserve(lhs.size() + rhs.size());
lhs.insert(lhs.end(), rhs.begin(), rhs.end());
return lhs;
}
namespace gapi
{
/**
* \addtogroup gapi_serialization
* @{
*
* @brief G-API functions and classes for serialization and deserialization.
*/
/** @brief Wraps deserialized output GRunArgs to GRunArgsP which can be used by GCompiled.
*
* Since it's impossible to get modifiable output arguments from deserialization
* it needs to be wrapped by this function.
*
* Example of usage:
* @snippet samples/cpp/tutorial_code/gapi/doc_snippets/api_ref_snippets.cpp bind after deserialization
*
* @param out_args deserialized GRunArgs.
* @return the same GRunArgs wrapped in GRunArgsP.
* @see deserialize
*/
GAPI_EXPORTS cv::GRunArgsP bind(cv::GRunArgs &out_args);
/** @brief Wraps output GRunArgsP available during graph execution to GRunArgs which can be serialized.
*
* GRunArgsP is pointer-to-value, so to be serialized they need to be binded to real values
* which this function does.
*
* Example of usage:
* @snippet samples/cpp/tutorial_code/gapi/doc_snippets/api_ref_snippets.cpp bind before serialization
*
* @param out output GRunArgsP available during graph execution.
* @return the same GRunArgsP wrapped in serializable GRunArgs.
* @see serialize
*/
GAPI_EXPORTS cv::GRunArg bind(cv::GRunArgP &out); // FIXME: think more about it
/** @} */
}
template<typename... Ts> inline GRunArgs gin(const Ts&... args)
{
return GRunArgs{ GRunArg(detail::wrap_host_helper<Ts>::wrap_in(args))... };
}
template<typename... Ts> inline GRunArgsP gout(Ts&... args)
{
return GRunArgsP{ GRunArgP(detail::wrap_host_helper<Ts>::wrap_out(args))... };
}
struct GTypeInfo;
using GTypesInfo = std::vector<GTypeInfo>;
// FIXME: Needed for python bridge, must be moved to more appropriate header
namespace detail {
struct ExtractArgsCallback
{
cv::GRunArgs operator()(const cv::GTypesInfo& info) const { return c(info); }
using CallBackT = std::function<cv::GRunArgs(const cv::GTypesInfo& info)>;
CallBackT c;
};
struct ExtractMetaCallback
{
cv::GMetaArgs operator()(const cv::GTypesInfo& info) const { return c(info); }
using CallBackT = std::function<cv::GMetaArgs(const cv::GTypesInfo& info)>;
CallBackT c;
};
void constructGraphOutputs(const cv::GTypesInfo &out_info,
cv::GRunArgs &args,
cv::GRunArgsP &outs);
} // namespace detail
} // namespace cv
#endif // OPENCV_GAPI_GARG_HPP

Some files were not shown because too many files have changed in this diff Show More