a
@@ -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;
|
||||
}
|
||||
@@ -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})
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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
|
||||
);
|
||||
};
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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_;
|
||||
};
|
||||
@@ -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_; // 候选定位块数量(用于杂物检测)
|
||||
};
|
||||
@@ -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) {}
|
||||
};
|
||||
@@ -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]` 始终靠近小定位块
|
||||
|
||||
---
|
||||
|
||||
*文档结束*
|
||||
@@ -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. 验证测试结果
|
||||
|
||||
---
|
||||
|
||||
*文档结束*
|
||||
@@ -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集成完成,多试纸处理,色块编号修正 |
|
||||
|
||||
---
|
||||
|
||||
*文档结束*
|
||||
|
After Width: | Height: | Size: 342 KiB |
|
After Width: | Height: | Size: 420 KiB |
|
After Width: | Height: | Size: 331 KiB |
|
After Width: | Height: | Size: 293 KiB |
|
After Width: | Height: | Size: 32 KiB |
|
After Width: | Height: | Size: 25 KiB |
|
After Width: | Height: | Size: 23 KiB |
|
After Width: | Height: | Size: 33 KiB |
|
After Width: | Height: | Size: 4.6 KiB |
|
After Width: | Height: | Size: 402 KiB |
|
After Width: | Height: | Size: 507 KiB |
|
After Width: | Height: | Size: 4.1 MiB |
|
After Width: | Height: | Size: 21 KiB |
|
After Width: | Height: | Size: 17 KiB |
|
After Width: | Height: | Size: 17 KiB |
|
After Width: | Height: | Size: 24 KiB |
|
After Width: | Height: | Size: 12 KiB |
@@ -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); }
|
||||
@@ -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 */
|
||||
@@ -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 */
|
||||
@@ -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
|
||||
@@ -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 ¶ms) \
|
||||
{ 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 ¶ms)
|
||||
{
|
||||
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 ¶ms);
|
||||
|
||||
//! 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
|
||||
@@ -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_ */
|
||||
@@ -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, ¢ers[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, ¢ers[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_
|
||||
@@ -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
|
||||
@@ -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
|
||||