初次提交
This commit is contained in:
45
.clang-format
Normal file
45
.clang-format
Normal file
@ -0,0 +1,45 @@
|
||||
# 配置参考:
|
||||
# https://clang.llvm.org/docs/ClangFormatStyleOptions.html
|
||||
|
||||
# 基于那个配置文件
|
||||
BasedOnStyle: Microsoft
|
||||
# 使用TAB
|
||||
UseTab: Always
|
||||
# TAB宽度
|
||||
TabWidth: 4
|
||||
# 行长度限制
|
||||
ColumnLimit: 0
|
||||
# 允许短的块放在同一行
|
||||
AllowShortBlocksOnASingleLine: Empty
|
||||
# 是否允许短if单行 If true, if (a) return; 可以放到同一行
|
||||
AllowShortIfStatementsOnASingleLine: AllIfsAndElse
|
||||
# 允许短的枚举放在同一行
|
||||
AllowShortEnumsOnASingleLine: true
|
||||
# 允许短的case放在同一行
|
||||
AllowShortCaseLabelsOnASingleLine: true
|
||||
# 允许短的循环保持在同一行
|
||||
AllowShortLoopsOnASingleLine: true
|
||||
# 连续的空行保留几行
|
||||
MaxEmptyLinesToKeep: 2
|
||||
# 是否允许短方法单行
|
||||
AllowShortFunctionsOnASingleLine: InlineOnly
|
||||
# 是否对include进行排序
|
||||
SortIncludes: Never
|
||||
NamespaceIndentation: All
|
||||
# case前增加空白
|
||||
IndentCaseLabels: true
|
||||
# 大括号换行
|
||||
BraceWrapping:
|
||||
AfterEnum: false
|
||||
AfterStruct: false
|
||||
SplitEmptyFunction: false
|
||||
AfterClass: false
|
||||
AfterControlStatement: Never
|
||||
AfterFunction: true
|
||||
AfterNamespace: true
|
||||
AfterUnion: false
|
||||
AfterExternBlock: true
|
||||
BeforeCatch: true
|
||||
BeforeElse: true
|
||||
BeforeLambdaBody: false
|
||||
BeforeWhile: false
|
9
.gitignore
vendored
Normal file
9
.gitignore
vendored
Normal file
@ -0,0 +1,9 @@
|
||||
/.vscode
|
||||
/build
|
||||
/dist
|
||||
/node_modules
|
||||
/test_data
|
||||
/thirdpart/_source
|
||||
/thirdpart/OpenCV
|
||||
/typing
|
||||
/package-lock.json
|
89
CMakeLists.txt
Normal file
89
CMakeLists.txt
Normal file
@ -0,0 +1,89 @@
|
||||
cmake_minimum_required(VERSION 3.10.0)
|
||||
project(ai-box VERSION 0.1.0 LANGUAGES C CXX)
|
||||
set(CMAKE_CXX_STANDARD 17)
|
||||
|
||||
if (POLICY CMP0091)
|
||||
cmake_policy(SET CMP0091 NEW)
|
||||
endif (POLICY CMP0091)
|
||||
|
||||
if(NOT DEFINED CMAKE_BUILD_TYPE)
|
||||
set(CMAKE_BUILD_TYPE Release)
|
||||
endif()
|
||||
|
||||
set(NODE_COMMON_SOURCES)
|
||||
set(NODE_ADDON_FOUND OFF)
|
||||
|
||||
include_directories(cxx)
|
||||
|
||||
# NodeJS
|
||||
execute_process(
|
||||
COMMAND node ${CMAKE_SOURCE_DIR}/thirdpart/cmake-js-util.js --include
|
||||
RESULT_VARIABLE CMAKE_JS_RESULT
|
||||
OUTPUT_VARIABLE CMAKE_JS_INC
|
||||
OUTPUT_STRIP_TRAILING_WHITESPACE
|
||||
)
|
||||
if(CMAKE_JS_RESULT EQUAL 0)
|
||||
execute_process(
|
||||
COMMAND node ${CMAKE_SOURCE_DIR}/thirdpart/cmake-js-util.js --src
|
||||
RESULT_VARIABLE CMAKE_JS_RESULT
|
||||
OUTPUT_VARIABLE CMAKE_JS_SRC
|
||||
OUTPUT_STRIP_TRAILING_WHITESPACE
|
||||
)
|
||||
if(CMAKE_JS_RESULT EQUAL 0)
|
||||
execute_process(
|
||||
COMMAND node ${CMAKE_SOURCE_DIR}/thirdpart/cmake-js-util.js --lib
|
||||
RESULT_VARIABLE CMAKE_JS_RESULT
|
||||
OUTPUT_VARIABLE CMAKE_JS_LIB
|
||||
OUTPUT_STRIP_TRAILING_WHITESPACE
|
||||
)
|
||||
endif()
|
||||
|
||||
# NAPI
|
||||
if(CMAKE_JS_RESULT EQUAL 0)
|
||||
execute_process(
|
||||
COMMAND node ${CMAKE_SOURCE_DIR}/thirdpart/cmake-js-util.js --napi
|
||||
RESULT_VARIABLE CMAKE_JS_RESULT
|
||||
OUTPUT_VARIABLE NODE_ADDON_API_DIR
|
||||
)
|
||||
endif()
|
||||
if(CMAKE_JS_RESULT EQUAL 0)
|
||||
message(STATUS "CMAKE_JS_INC: ${CMAKE_JS_INC}")
|
||||
message(STATUS "CMAKE_JS_SRC: ${CMAKE_JS_SRC}")
|
||||
message(STATUS "CMAKE_JS_LIB: ${CMAKE_JS_LIB}")
|
||||
message(STATUS "NODE_ADDON_API_DIR: ${NODE_ADDON_API_DIR}")
|
||||
include_directories(${CMAKE_JS_INC} ${NODE_ADDON_API_DIR})
|
||||
set(NODE_ADDON_FOUND ON)
|
||||
endif()
|
||||
endif()
|
||||
if(NOT (CMAKE_JS_RESULT EQUAL 0))
|
||||
message(FATAL_ERROR "cmake js config failed")
|
||||
endif()
|
||||
|
||||
|
||||
# OpenCV
|
||||
set(OpenCV_CMAKE_FILE ${CMAKE_SOURCE_DIR}/thirdpart/OpenCV/${CMAKE_BUILD_TYPE}/config.cmake)
|
||||
if(EXISTS ${OpenCV_CMAKE_FILE})
|
||||
include(${OpenCV_CMAKE_FILE})
|
||||
message(STATUS "OpenCV_LIB_DIR: ${OpenCV_LIB_DIR}")
|
||||
message(STATUS "OpenCV_INCLUDE_DIR: ${OpenCV_INCLUDE_DIR}")
|
||||
message(STATUS "OpenCV_LIBS: ${OpenCV_LIBS}")
|
||||
include_directories(${OpenCV_INCLUDE_DIRS})
|
||||
link_directories(${OpenCV_LIB_DIR})
|
||||
else()
|
||||
message(FATAL_ERROR "OpenCV config failed")
|
||||
endif()
|
||||
|
||||
# 构建目标
|
||||
add_library(cv SHARED ${CMAKE_JS_SRC}
|
||||
cxx/node.cc
|
||||
cxx/mat.cc
|
||||
cxx/util.cc
|
||||
)
|
||||
target_link_libraries(cv ${CMAKE_JS_LIB} ${OpenCV_LIBS})
|
||||
set_target_properties(cv PROPERTIES PREFIX "" SUFFIX ".node")
|
||||
|
||||
|
||||
if(MSVC AND NODE_ADDON_FOUND)
|
||||
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} /MT")
|
||||
execute_process(COMMAND ${CMAKE_AR} /def:${CMAKE_JS_NODELIB_DEF} /out:${CMAKE_JS_NODELIB_TARGET} ${CMAKE_STATIC_LINKER_FLAGS})
|
||||
endif()
|
39
README.md
Normal file
39
README.md
Normal file
@ -0,0 +1,39 @@
|
||||
# @yizhi/opencv
|
||||
|
||||
简单的OpenCV封装
|
||||
|
||||
## 编译源码
|
||||
1. 依赖
|
||||
1. python3
|
||||
1. cmake
|
||||
1. ninja
|
||||
1. c++编译器(gcc,clang,Visual Studio ...)
|
||||
|
||||
1. 编译OpenCV
|
||||
```bash
|
||||
node thirdpart/install.js --with-opencv
|
||||
```
|
||||
1. 编译本模块
|
||||
```bash
|
||||
cmake -B build -G Ninja . -DCMAKE_BUILD_TYPE=Release
|
||||
cmake --build build --config Release
|
||||
```
|
||||
|
||||
## 安装
|
||||
```bash
|
||||
npm install @yizhi/opencv
|
||||
```
|
||||
|
||||
## 使用
|
||||
```typescript
|
||||
//导入opencv
|
||||
import cv from '@yizhi/opencv';
|
||||
|
||||
//配置addon路径
|
||||
cv.config("ADDON_PATH", "/path/to/cv.node");
|
||||
|
||||
//正常使用
|
||||
const im = cv.imread("/path/to/input");
|
||||
cv.resize(im, 640, 640);
|
||||
cv.imwrite("/path/to/output", im);
|
||||
```
|
450
cxx/mat.cc
Normal file
450
cxx/mat.cc
Normal file
@ -0,0 +1,450 @@
|
||||
#include "node.h"
|
||||
|
||||
using namespace Napi;
|
||||
|
||||
#define MAT_INSTANCE_METHOD(method) InstanceMethod<&CVMat::method>(#method, static_cast<napi_property_attributes>(napi_writable | napi_configurable))
|
||||
#define MAT_STATIC_METHOD(method) StaticMethod<&CVMat::method>(#method, static_cast<napi_property_attributes>(napi_writable | napi_configurable))
|
||||
|
||||
static FunctionReference *constructor = nullptr;
|
||||
|
||||
class CVMat : public ObjectWrap<CVMat> {
|
||||
public:
|
||||
static Napi::Object Init(Napi::Env env, Napi::Object exports)
|
||||
{
|
||||
Function func = DefineClass(env, "Mat", {
|
||||
MAT_STATIC_METHOD(ImRead),
|
||||
MAT_STATIC_METHOD(ImDecode),
|
||||
MAT_STATIC_METHOD(ImWrite),
|
||||
MAT_STATIC_METHOD(ImEncode),
|
||||
|
||||
MAT_STATIC_METHOD(Crop),
|
||||
MAT_STATIC_METHOD(Resize),
|
||||
MAT_STATIC_METHOD(WarpAffine),
|
||||
MAT_STATIC_METHOD(Blur),
|
||||
MAT_STATIC_METHOD(CopyMakeBorder),
|
||||
MAT_STATIC_METHOD(Flip),
|
||||
MAT_STATIC_METHOD(GetRotationMatrix2D),
|
||||
|
||||
MAT_STATIC_METHOD(Rectangle),
|
||||
MAT_STATIC_METHOD(Circle),
|
||||
MAT_STATIC_METHOD(Line),
|
||||
MAT_STATIC_METHOD(Ellipse),
|
||||
MAT_STATIC_METHOD(Polylines),
|
||||
MAT_STATIC_METHOD(FillPoly),
|
||||
MAT_STATIC_METHOD(FillConvexPoly),
|
||||
MAT_STATIC_METHOD(DrawMarker),
|
||||
MAT_STATIC_METHOD(PutText),
|
||||
|
||||
MAT_INSTANCE_METHOD(IsEmpty),
|
||||
MAT_INSTANCE_METHOD(GetCols),
|
||||
MAT_INSTANCE_METHOD(GetRows),
|
||||
MAT_INSTANCE_METHOD(GetType),
|
||||
MAT_INSTANCE_METHOD(GetDepth),
|
||||
MAT_INSTANCE_METHOD(GetChannels),
|
||||
MAT_INSTANCE_METHOD(GetFlags),
|
||||
MAT_INSTANCE_METHOD(GetDims),
|
||||
MAT_INSTANCE_METHOD(GetIsContinuous),
|
||||
MAT_INSTANCE_METHOD(GetIsSubmatrix),
|
||||
MAT_INSTANCE_METHOD(GetElemSize),
|
||||
MAT_INSTANCE_METHOD(GetElemSize1),
|
||||
MAT_INSTANCE_METHOD(GetTotal),
|
||||
MAT_INSTANCE_METHOD(GetTotalWithDim),
|
||||
MAT_INSTANCE_METHOD(GetSize),
|
||||
|
||||
MAT_INSTANCE_METHOD(Col),
|
||||
MAT_INSTANCE_METHOD(ColRange),
|
||||
MAT_INSTANCE_METHOD(Row),
|
||||
MAT_INSTANCE_METHOD(RowRange),
|
||||
MAT_INSTANCE_METHOD(Diag),
|
||||
|
||||
MAT_INSTANCE_METHOD(Clone),
|
||||
|
||||
});
|
||||
constructor = new FunctionReference();
|
||||
*constructor = Napi::Persistent(func);
|
||||
exports.Set("Mat", func);
|
||||
env.SetInstanceData<FunctionReference>(constructor);
|
||||
return exports;
|
||||
}
|
||||
|
||||
CVMat(const CallbackInfo &info)
|
||||
: ObjectWrap<CVMat>(info)
|
||||
{
|
||||
if (info.Length() >= 2) {
|
||||
auto sizesArray = info[0].As<Array>();
|
||||
std::vector<int> sizes(sizesArray.Length());
|
||||
for (auto i = 0; i < sizes.size(); ++i) sizes[i] = sizesArray.Get(i).As<Number>().Int32Value();
|
||||
auto type = info[1].As<Number>().Int32Value();
|
||||
// 使用sizes和type初始化
|
||||
if (info.Length() < 3) mat_ = cv::Mat(sizes, type);
|
||||
// 使用数据初始化
|
||||
else if (info[2].IsTypedArray()) {
|
||||
auto dataArray = info[2].As<TypedArray>();
|
||||
auto data = static_cast<uint8_t *>(dataArray.ArrayBuffer().Data()) + dataArray.ByteOffset();
|
||||
mat_ = cv::Mat(sizes, type, data);
|
||||
}
|
||||
// 其他 TODO
|
||||
else mat_ = cv::Mat(sizes, type);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static Napi::Value ImRead(const Napi::CallbackInfo &info)
|
||||
{
|
||||
auto filename = info[0].As<String>().Utf8Value();
|
||||
int flag = (info.Length() >= 2) ? info[1].As<Number>().Int32Value() : cv::IMREAD_COLOR_BGR;
|
||||
return CreateMat(info.Env(), [&](CVMat &mat) { mat.mat_ = cv::imread(filename, flag); });
|
||||
}
|
||||
static Napi::Value ImDecode(const Napi::CallbackInfo &info)
|
||||
{
|
||||
auto arr = info[0].As<TypedArray>();
|
||||
auto data = static_cast<uint8_t *>(arr.ArrayBuffer().Data()) + arr.ByteOffset();
|
||||
std::vector<uint8_t> buffer(data, data + arr.ByteLength());
|
||||
int flag = (info.Length() >= 2) ? info[1].As<Number>().Int32Value() : cv::IMREAD_COLOR_BGR;
|
||||
return CreateMat(info.Env(), [&](CVMat &mat) { mat.mat_ = cv::imdecode(buffer, flag); });
|
||||
}
|
||||
static Napi::Value ImWrite(const Napi::CallbackInfo &info)
|
||||
{
|
||||
auto &mat = GetMat(info[0].As<Object>());
|
||||
auto filename = info[1].As<String>().Utf8Value();
|
||||
std::vector<int> params;
|
||||
if (info[2].IsArray()) {
|
||||
auto paramArray = info[2].As<Array>();
|
||||
params.resize(paramArray.Length());
|
||||
for (auto i = 0; i < params.size(); ++i) params[i] = paramArray.Get(i).As<Number>().Int32Value();
|
||||
}
|
||||
auto res = cv::imwrite(filename, mat.mat_, params);
|
||||
return Boolean::New(info.Env(), res);
|
||||
}
|
||||
static Napi::Value ImEncode(const Napi::CallbackInfo &info)
|
||||
{
|
||||
auto &mat = GetMat(info[0].As<Object>());
|
||||
auto extname = info[1].As<String>().Utf8Value();
|
||||
std::vector<int> params;
|
||||
if (info[2].IsArray()) {
|
||||
auto paramArray = info[2].As<Array>();
|
||||
params.resize(paramArray.Length());
|
||||
for (auto i = 0; i < params.size(); ++i) params[i] = paramArray.Get(i).As<Number>().Int32Value();
|
||||
}
|
||||
std::vector<uchar> buf;
|
||||
if (!cv::imencode(extname, mat.mat_, buf, params)) return info.Env().Undefined();
|
||||
auto arrayBuffer = ArrayBuffer::New(info.Env(), buf.size());
|
||||
auto bufferPtr = static_cast<uchar *>(arrayBuffer.Data());
|
||||
for (auto ch : buf) {
|
||||
*bufferPtr = ch;
|
||||
bufferPtr++;
|
||||
}
|
||||
return arrayBuffer;
|
||||
}
|
||||
|
||||
|
||||
Napi::Value IsEmpty(const Napi::CallbackInfo &info) { return Boolean::New(info.Env(), mat_.empty()); }
|
||||
Napi::Value GetCols(const Napi::CallbackInfo &info) { return Number::New(info.Env(), mat_.cols); }
|
||||
Napi::Value GetRows(const Napi::CallbackInfo &info) { return Number::New(info.Env(), mat_.rows); }
|
||||
Napi::Value GetType(const Napi::CallbackInfo &info) { return Number::New(info.Env(), mat_.type()); }
|
||||
Napi::Value GetDepth(const Napi::CallbackInfo &info) { return Number::New(info.Env(), mat_.depth()); }
|
||||
Napi::Value GetChannels(const Napi::CallbackInfo &info) { return Number::New(info.Env(), mat_.channels()); }
|
||||
Napi::Value GetFlags(const Napi::CallbackInfo &info) { return Number::New(info.Env(), mat_.flags); }
|
||||
Napi::Value GetDims(const Napi::CallbackInfo &info) { return Number::New(info.Env(), mat_.dims); }
|
||||
Napi::Value GetIsContinuous(const Napi::CallbackInfo &info) { return Boolean::New(info.Env(), mat_.isContinuous()); }
|
||||
Napi::Value GetIsSubmatrix(const Napi::CallbackInfo &info) { return Boolean::New(info.Env(), mat_.isSubmatrix()); }
|
||||
Napi::Value GetElemSize(const Napi::CallbackInfo &info) { return Number::New(info.Env(), static_cast<double>(mat_.elemSize())); }
|
||||
Napi::Value GetElemSize1(const Napi::CallbackInfo &info) { return Number::New(info.Env(), static_cast<double>(mat_.elemSize1())); }
|
||||
Napi::Value GetTotal(const Napi::CallbackInfo &info) { return Number::New(info.Env(), static_cast<double>(mat_.total())); }
|
||||
Napi::Value GetTotalWithDim(const Napi::CallbackInfo &info) { return Number::New(info.Env(), static_cast<double>(mat_.total(info[0].As<Number>().Int32Value(), info[1].As<Number>().Int32Value()))); }
|
||||
Napi::Value GetSize(const Napi::CallbackInfo &info)
|
||||
{
|
||||
auto ret = Array::New(info.Env(), mat_.dims);
|
||||
auto &size = mat_.size;
|
||||
for (int i = 0; i < mat_.dims; ++i) ret.Set(i, size[i]);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
Napi::Value Col(const Napi::CallbackInfo &info)
|
||||
{
|
||||
auto col = info[0].As<Number>().Int32Value();
|
||||
return CreateMat(info.Env(), [&](CVMat &mat) { mat.mat_ = mat_.col(col); });
|
||||
}
|
||||
Napi::Value ColRange(const Napi::CallbackInfo &info)
|
||||
{
|
||||
auto start = info[0].As<Number>().Int32Value();
|
||||
auto end = info[1].As<Number>().Int32Value();
|
||||
return CreateMat(info.Env(), [&](CVMat &mat) { mat.mat_ = mat_.colRange(start, end); });
|
||||
}
|
||||
Napi::Value Row(const Napi::CallbackInfo &info)
|
||||
{
|
||||
auto row = info[0].As<Number>().Int32Value();
|
||||
return CreateMat(info.Env(), [&](CVMat &mat) { mat.mat_ = mat_.row(row); });
|
||||
}
|
||||
Napi::Value RowRange(const Napi::CallbackInfo &info)
|
||||
{
|
||||
auto start = info[0].As<Number>().Int32Value();
|
||||
auto end = info[1].As<Number>().Int32Value();
|
||||
return CreateMat(info.Env(), [&](CVMat &mat) { mat.mat_ = mat_.rowRange(start, end); });
|
||||
}
|
||||
Napi::Value Diag(const Napi::CallbackInfo &info)
|
||||
{
|
||||
auto d = info[0].As<Number>().Int32Value();
|
||||
return CreateMat(info.Env(), [&](CVMat &mat) { mat.mat_ = mat_.diag(d); });
|
||||
}
|
||||
|
||||
|
||||
Napi::Value Clone(const Napi::CallbackInfo &info)
|
||||
{
|
||||
return CreateMat(info.Env(), [&](CVMat &mat) { mat.mat_ = mat_.clone(); });
|
||||
}
|
||||
|
||||
|
||||
static Napi::Value Crop(const Napi::CallbackInfo &info)
|
||||
{
|
||||
auto &src = GetMat(info[0].As<Object>());
|
||||
auto &dst = GetMat(info[1].As<Object>());
|
||||
auto rangeArray = info[2].As<Array>();
|
||||
std::vector<cv::Range> ranges(rangeArray.Length());
|
||||
for (int i = 0; i < ranges.size(); ++i) {
|
||||
auto item = rangeArray.Get(i).As<Object>();
|
||||
auto start = item.Get("start").As<Number>().Int32Value();
|
||||
auto end = item.Get("end").As<Number>().Int32Value();
|
||||
ranges[i] = cv::Range(start, end);
|
||||
}
|
||||
dst.mat_ = src.mat_(ranges);
|
||||
return info[1];
|
||||
}
|
||||
static Napi::Value Resize(const Napi::CallbackInfo &info)
|
||||
{
|
||||
auto &src = GetMat(info[0].As<Object>());
|
||||
auto &dst = GetMat(info[1].As<Object>());
|
||||
auto width = info[2].As<Number>().Int32Value();
|
||||
auto height = info[3].As<Number>().Int32Value();
|
||||
auto fx = info[4].As<Number>().DoubleValue();
|
||||
auto fy = info[5].As<Number>().DoubleValue();
|
||||
auto interpolation = info[6].As<Number>().Int32Value();
|
||||
cv::resize(src.mat_, dst.mat_, cv::Size(width, height), fx, fy, interpolation);
|
||||
return info[1];
|
||||
}
|
||||
static Napi::Value WarpAffine(const Napi::CallbackInfo &info)
|
||||
{
|
||||
auto &src = GetMat(info[0].As<Object>());
|
||||
auto &dst = GetMat(info[1].As<Object>());
|
||||
auto &transformMat = GetMat(info[2].As<Object>());
|
||||
auto dwidth = info[3].As<Number>().Int32Value();
|
||||
auto dheight = info[4].As<Number>().Int32Value();
|
||||
auto flags = info[5].As<Number>().Int32Value();
|
||||
auto borderMode = info[6].As<Number>().Int32Value();
|
||||
cv::warpAffine(src.mat_, dst.mat_, transformMat.mat_, cv::Size(dwidth, dheight), flags, borderMode);
|
||||
return info[1];
|
||||
}
|
||||
static Napi::Value Blur(const Napi::CallbackInfo &info)
|
||||
{
|
||||
auto &src = GetMat(info[0].As<Object>());
|
||||
auto &dst = GetMat(info[1].As<Object>());
|
||||
auto kwidth = info[2].As<Number>().Int32Value();
|
||||
auto kheight = info[3].As<Number>().Int32Value();
|
||||
auto anchorX = info[4].As<Number>().Int32Value();
|
||||
auto anchorY = info[5].As<Number>().Int32Value();
|
||||
auto borderType = info[6].As<Number>().Int32Value();
|
||||
cv::blur(src.mat_, dst.mat_, cv::Size(kwidth, kheight), cv::Point(anchorX, anchorY), borderType);
|
||||
return info[1];
|
||||
}
|
||||
static Napi::Value CopyMakeBorder(const Napi::CallbackInfo &info)
|
||||
{
|
||||
auto &src = GetMat(info[0].As<Object>());
|
||||
auto &dst = GetMat(info[1].As<Object>());
|
||||
auto top = info[2].As<Number>().Int32Value();
|
||||
auto bottom = info[3].As<Number>().Int32Value();
|
||||
auto left = info[4].As<Number>().Int32Value();
|
||||
auto right = info[5].As<Number>().Int32Value();
|
||||
auto borderType = info[6].As<Number>().Int32Value();
|
||||
cv::copyMakeBorder(src.mat_, dst.mat_, top, bottom, left, right, borderType);
|
||||
return info[1];
|
||||
}
|
||||
static Napi::Value Flip(const Napi::CallbackInfo &info)
|
||||
{
|
||||
auto &src = GetMat(info[0].As<Object>());
|
||||
auto &dst = GetMat(info[1].As<Object>());
|
||||
auto flipCode = info[2].As<Number>().Int32Value();
|
||||
cv::flip(src.mat_, dst.mat_, flipCode);
|
||||
return info[1];
|
||||
}
|
||||
static Napi::Value GetRotationMatrix2D(const Napi::CallbackInfo &info)
|
||||
{
|
||||
auto centerX = info[0].As<Number>().FloatValue();
|
||||
auto centerY = info[1].As<Number>().FloatValue();
|
||||
auto angle = info[2].As<Number>().DoubleValue();
|
||||
auto scale = info[3].As<Number>().DoubleValue();
|
||||
return CreateMat(info.Env(), [&](CVMat &mat) { mat.mat_ = cv::getRotationMatrix2D(cv::Point2f(centerX, centerY), angle, scale); });
|
||||
}
|
||||
|
||||
|
||||
static Napi::Value Rectangle(const Napi::CallbackInfo &info)
|
||||
{
|
||||
auto &img = GetMat(info[0].As<Object>());
|
||||
auto x1 = info[1].As<Number>().Int32Value();
|
||||
auto y1 = info[2].As<Number>().Int32Value();
|
||||
auto x2 = info[3].As<Number>().Int32Value();
|
||||
auto y2 = info[4].As<Number>().Int32Value();
|
||||
auto b = info[5].As<Number>().DoubleValue();
|
||||
auto g = info[6].As<Number>().DoubleValue();
|
||||
auto r = info[7].As<Number>().DoubleValue();
|
||||
auto thickness = info[8].As<Number>().Int32Value();
|
||||
auto lineType = info[9].As<Number>().Int32Value();
|
||||
auto shift = info[10].As<Number>().Int32Value();
|
||||
cv::rectangle(img.mat_, cv::Point(x1, y1), cv::Point(x2, y2), cv::Scalar(b, g, r), thickness, lineType, shift);
|
||||
return info.Env().Undefined();
|
||||
}
|
||||
static Napi::Value Circle(const Napi::CallbackInfo &info)
|
||||
{
|
||||
auto &img = GetMat(info[0].As<Object>());
|
||||
auto x = info[1].As<Number>().Int32Value();
|
||||
auto y = info[2].As<Number>().Int32Value();
|
||||
auto radius = info[3].As<Number>().Int32Value();
|
||||
auto b = info[4].As<Number>().DoubleValue();
|
||||
auto g = info[5].As<Number>().DoubleValue();
|
||||
auto r = info[6].As<Number>().DoubleValue();
|
||||
auto thickness = info[7].As<Number>().Int32Value();
|
||||
auto lineType = info[8].As<Number>().Int32Value();
|
||||
auto shift = info[9].As<Number>().Int32Value();
|
||||
cv::circle(img.mat_, cv::Point(x, y), radius, cv::Scalar(b, g, r), thickness, lineType, shift);
|
||||
return info.Env().Undefined();
|
||||
}
|
||||
static Napi::Value Line(const Napi::CallbackInfo &info)
|
||||
{
|
||||
auto &img = GetMat(info[0].As<Object>());
|
||||
auto x1 = info[1].As<Number>().Int32Value();
|
||||
auto y1 = info[2].As<Number>().Int32Value();
|
||||
auto x2 = info[3].As<Number>().Int32Value();
|
||||
auto y2 = info[4].As<Number>().Int32Value();
|
||||
auto b = info[5].As<Number>().DoubleValue();
|
||||
auto g = info[6].As<Number>().DoubleValue();
|
||||
auto r = info[7].As<Number>().DoubleValue();
|
||||
auto thickness = info[8].As<Number>().Int32Value();
|
||||
auto lineType = info[9].As<Number>().Int32Value();
|
||||
auto shift = info[10].As<Number>().Int32Value();
|
||||
cv::line(img.mat_, cv::Point(x1, y1), cv::Point(x2, y2), cv::Scalar(b, g, r), thickness, lineType, shift);
|
||||
return info.Env().Undefined();
|
||||
}
|
||||
static Napi::Value Ellipse(const Napi::CallbackInfo &info)
|
||||
{
|
||||
auto &img = GetMat(info[0].As<Object>());
|
||||
auto x = info[1].As<Number>().Int32Value();
|
||||
auto y = info[2].As<Number>().Int32Value();
|
||||
auto width = info[3].As<Number>().Int32Value();
|
||||
auto height = info[4].As<Number>().Int32Value();
|
||||
auto angle = info[5].As<Number>().DoubleValue();
|
||||
auto startAngle = info[6].As<Number>().DoubleValue();
|
||||
auto endAngle = info[7].As<Number>().DoubleValue();
|
||||
auto b = info[8].As<Number>().DoubleValue();
|
||||
auto g = info[9].As<Number>().DoubleValue();
|
||||
auto r = info[10].As<Number>().DoubleValue();
|
||||
auto thickness = info[11].As<Number>().Int32Value();
|
||||
auto lineType = info[12].As<Number>().Int32Value();
|
||||
auto shift = info[13].As<Number>().Int32Value();
|
||||
cv::ellipse(img.mat_, cv::Point(x, y), cv::Size(width, height), angle, startAngle, endAngle, cv::Scalar(b, g, r), thickness, lineType, shift);
|
||||
return info.Env().Undefined();
|
||||
}
|
||||
static Napi::Value Polylines(const Napi::CallbackInfo &info)
|
||||
{
|
||||
auto &img = GetMat(info[0].As<Object>());
|
||||
auto points_ = info[1].As<Array>();
|
||||
std::vector<cv::Point> points(points_.Length());
|
||||
for (uint32_t i = 0; i < points_.Length(); i++) {
|
||||
auto pt = points_.Get(i).As<Array>();
|
||||
points[i] = cv::Point(pt.Get(0U).As<Number>().Int32Value(), pt.Get(1U).As<Number>().Int32Value());
|
||||
}
|
||||
auto isClosed = info[2].As<Boolean>().Value();
|
||||
auto b = info[3].As<Number>().DoubleValue();
|
||||
auto g = info[4].As<Number>().DoubleValue();
|
||||
auto r = info[5].As<Number>().DoubleValue();
|
||||
auto thickness = info[6].As<Number>().Int32Value();
|
||||
auto lineType = info[7].As<Number>().Int32Value();
|
||||
auto shift = info[8].As<Number>().Int32Value();
|
||||
cv::polylines(img.mat_, points, isClosed, cv::Scalar(b, g, r), thickness, lineType, shift);
|
||||
return info.Env().Undefined();
|
||||
}
|
||||
static Napi::Value FillPoly(const Napi::CallbackInfo &info)
|
||||
{
|
||||
auto &img = GetMat(info[0].As<Object>());
|
||||
auto points_ = info[1].As<Array>();
|
||||
std::vector<cv::Point> points(points_.Length());
|
||||
for (uint32_t i = 0; i < points_.Length(); i++) {
|
||||
auto pt = points_.Get(i).As<Array>();
|
||||
points[i] = cv::Point(pt.Get(0U).As<Number>().Int32Value(), pt.Get(1U).As<Number>().Int32Value());
|
||||
}
|
||||
auto b = info[2].As<Number>().DoubleValue();
|
||||
auto g = info[3].As<Number>().DoubleValue();
|
||||
auto r = info[4].As<Number>().DoubleValue();
|
||||
auto lineType = info[5].As<Number>().Int32Value();
|
||||
auto shift = info[6].As<Number>().Int32Value();
|
||||
cv::fillPoly(img.mat_, points, cv::Scalar(b, g, r), lineType, shift);
|
||||
return info.Env().Undefined();
|
||||
}
|
||||
static Napi::Value FillConvexPoly(const Napi::CallbackInfo &info)
|
||||
{
|
||||
auto &img = GetMat(info[0].As<Object>());
|
||||
auto points_ = info[1].As<Array>();
|
||||
std::vector<cv::Point> points(points_.Length());
|
||||
for (uint32_t i = 0; i < points_.Length(); i++) {
|
||||
auto pt = points_.Get(i).As<Array>();
|
||||
points[i] = cv::Point(pt.Get(0U).As<Number>().Int32Value(), pt.Get(1U).As<Number>().Int32Value());
|
||||
}
|
||||
auto b = info[2].As<Number>().DoubleValue();
|
||||
auto g = info[3].As<Number>().DoubleValue();
|
||||
auto r = info[4].As<Number>().DoubleValue();
|
||||
auto lineType = info[5].As<Number>().Int32Value();
|
||||
auto shift = info[6].As<Number>().Int32Value();
|
||||
cv::fillConvexPoly(img.mat_, points, cv::Scalar(b, g, r), lineType, shift);
|
||||
return info.Env().Undefined();
|
||||
}
|
||||
static Napi::Value DrawMarker(const Napi::CallbackInfo &info)
|
||||
{
|
||||
auto &img = GetMat(info[0].As<Object>());
|
||||
auto x = info[1].As<Number>().Int32Value();
|
||||
auto y = info[2].As<Number>().Int32Value();
|
||||
auto b = info[3].As<Number>().DoubleValue();
|
||||
auto g = info[4].As<Number>().DoubleValue();
|
||||
auto r = info[5].As<Number>().DoubleValue();
|
||||
auto markerType = info[6].As<Number>().Int32Value();
|
||||
auto markerSize = info[7].As<Number>().Int32Value();
|
||||
auto lineType = info[8].As<Number>().Int32Value();
|
||||
auto shift = info[9].As<Number>().Int32Value();
|
||||
cv::drawMarker(img.mat_, cv::Point(x, y), cv::Scalar(b, g, r), markerType, markerSize, lineType, shift);
|
||||
return info.Env().Undefined();
|
||||
}
|
||||
static Napi::Value PutText(const Napi::CallbackInfo &info)
|
||||
{
|
||||
auto &img = GetMat(info[0].As<Object>());
|
||||
auto text = info[1].As<String>().Utf8Value();
|
||||
auto x = info[2].As<Number>().Int32Value();
|
||||
auto y = info[3].As<Number>().Int32Value();
|
||||
auto fontFace = info[4].As<Number>().Int32Value();
|
||||
auto fontScale = info[5].As<Number>().DoubleValue();
|
||||
auto b = info[6].As<Number>().DoubleValue();
|
||||
auto g = info[7].As<Number>().DoubleValue();
|
||||
auto r = info[8].As<Number>().DoubleValue();
|
||||
auto thickness = info[9].As<Number>().Int32Value();
|
||||
auto lineType = info[10].As<Number>().Int32Value();
|
||||
auto shift = info[11].As<Number>().Int32Value();
|
||||
auto bottomLeftOrigin = info[12].As<Boolean>().Value();
|
||||
cv::putText(img.mat_, text, cv::Point(x, y), fontFace, fontScale, cv::Scalar(b, g, r), thickness, lineType, bottomLeftOrigin);
|
||||
return info.Env().Undefined();
|
||||
}
|
||||
|
||||
private:
|
||||
inline static Napi::Object EmptyMat(Napi::Env env) { return constructor->New({}).As<Object>(); }
|
||||
inline static CVMat &GetMat(Napi::Object obj) { return *ObjectWrap<CVMat>::Unwrap(obj); }
|
||||
inline static Napi::Object CreateMat(Napi::Env env, std::function<void(CVMat &mat)> callback)
|
||||
{
|
||||
auto obj = EmptyMat(env);
|
||||
callback(GetMat(obj));
|
||||
return obj;
|
||||
}
|
||||
|
||||
private:
|
||||
cv::Mat mat_;
|
||||
};
|
||||
|
||||
void InitMatAPI(Napi::Env env, Napi::Object exports)
|
||||
{
|
||||
CVMat::Init(env, exports);
|
||||
}
|
11
cxx/node.cc
Normal file
11
cxx/node.cc
Normal file
@ -0,0 +1,11 @@
|
||||
|
||||
#include "node.h"
|
||||
|
||||
|
||||
static Napi::Object Init(Napi::Env env, Napi::Object exports)
|
||||
{
|
||||
InitMatAPI(env, exports);
|
||||
InitProcAPI(env, exports);
|
||||
return exports;
|
||||
}
|
||||
NODE_API_MODULE(addon, Init);
|
10
cxx/node.h
Normal file
10
cxx/node.h
Normal file
@ -0,0 +1,10 @@
|
||||
#ifndef __CV_NODE_H__
|
||||
#define __CV_NODE_H__
|
||||
|
||||
#include <napi.h>
|
||||
#include <opencv2/opencv.hpp>
|
||||
|
||||
void InitMatAPI(Napi::Env env, Napi::Object exports);
|
||||
void InitProcAPI(Napi::Env env, Napi::Object exports);
|
||||
|
||||
#endif
|
35
cxx/util.cc
Normal file
35
cxx/util.cc
Normal file
@ -0,0 +1,35 @@
|
||||
#include "node.h"
|
||||
using namespace Napi;
|
||||
|
||||
#define ADD_PROC_FUNCTION(name) obj.Set(#name, Napi::Function::New<name>(env));
|
||||
#define DEFINE_FUNCTION(name) Napi::Value name(Napi::CallbackInfo const &info)
|
||||
|
||||
DEFINE_FUNCTION(GetTextSize)
|
||||
{
|
||||
auto text = info[0].As<Napi::String>().Utf8Value();
|
||||
auto fontFace = info[1].As<Napi::Number>().Int32Value();
|
||||
auto fontScale = info[2].As<Napi::Number>().DoubleValue();
|
||||
auto thickness = info[3].As<Napi::Number>().Int32Value();
|
||||
int baseLine;
|
||||
auto size = cv::getTextSize(text, fontFace, fontScale, thickness, &baseLine);
|
||||
auto res = Object::New(info.Env());
|
||||
res.Set("width", Number::New(info.Env(), size.width));
|
||||
res.Set("height", Number::New(info.Env(), size.height));
|
||||
res.Set("baseLine", Number::New(info.Env(), baseLine));
|
||||
return res;
|
||||
}
|
||||
|
||||
DEFINE_FUNCTION(GetFontScaleFromHeight)
|
||||
{
|
||||
auto fontFace = info[0].As<Napi::Number>().Int32Value();
|
||||
auto pixelHeight = info[1].As<Napi::Number>().Int32Value();
|
||||
auto thickness = info[2].As<Napi::Number>().Int32Value();
|
||||
return Number::New(info.Env(), cv::getFontScaleFromHeight(fontFace, pixelHeight, thickness));
|
||||
}
|
||||
|
||||
void InitProcAPI(Napi::Env env, Napi::Object exports)
|
||||
{
|
||||
auto obj = Object::New(env);
|
||||
ADD_PROC_FUNCTION(GetTextSize);
|
||||
exports.Set("util", obj);
|
||||
}
|
19
package.json
Normal file
19
package.json
Normal file
@ -0,0 +1,19 @@
|
||||
{
|
||||
"name": "@yizhi/cv",
|
||||
"version": "1.0.0",
|
||||
"main": "dist/index.js",
|
||||
"types": "typing/index.d.ts",
|
||||
"scripts": {
|
||||
"build": "tsc",
|
||||
"watch": "tsc -w --inlineSourceMap"
|
||||
},
|
||||
"keywords": [],
|
||||
"author": "",
|
||||
"license": "ISC",
|
||||
"description": "",
|
||||
"devDependencies": {
|
||||
"@types/node": "^22.13.5",
|
||||
"cmake-js": "^7.3.0",
|
||||
"node-addon-api": "^8.3.1"
|
||||
}
|
||||
}
|
19
src/cv/addon.ts
Normal file
19
src/cv/addon.ts
Normal file
@ -0,0 +1,19 @@
|
||||
import { LINE_8, MARKER_CROSS } from "./consts";
|
||||
|
||||
export const config = {
|
||||
ADDON_PATH: "../../build/cv.node",
|
||||
DEFAULT_LINE_TYPE: LINE_8,
|
||||
DEFAULT_THICKNESS: 1,
|
||||
DEFAULT_MARKER: MARKER_CROSS,
|
||||
};
|
||||
|
||||
export function getAddon() {
|
||||
return require(config.ADDON_PATH);
|
||||
}
|
||||
|
||||
export function getConfig<N extends keyof typeof config>(name: N): typeof config[N] { return config[name]; }
|
||||
export function setConfig<N extends keyof typeof config>(name: N, value: typeof config[N]) { config[name] = value; }
|
||||
|
||||
export function CVMat() { return getAddon().Mat; }
|
||||
|
||||
export function CVUtil() { return getAddon().util; }
|
14
src/cv/common.ts
Normal file
14
src/cv/common.ts
Normal file
@ -0,0 +1,14 @@
|
||||
import { getAddon, getConfig } from "./addon";
|
||||
import { Mat } from "./mat";
|
||||
|
||||
export type TypedArray = Uint8Array | Int8Array | Uint16Array | Int16Array | Uint32Array | Int32Array | BigUint64Array | BigInt64Array | Float32Array | Float64Array
|
||||
|
||||
export function FromCV(mat: any): Mat { return new (Mat as any)(mat); }
|
||||
export function M(mat: Mat) { return (mat as any).__mat__; }
|
||||
export function C(name: Parameters<typeof getConfig>[0]) { return getConfig(name); }
|
||||
|
||||
export function resolveArgs<R extends Array<any>>(args: any[], checker: boolean | (() => boolean)): R {
|
||||
if (typeof checker === "function") checker = checker();
|
||||
if (checker) return args as R;
|
||||
return [args[0], new Mat(), ...args.slice(1)] as R;
|
||||
}
|
166
src/cv/consts.ts
Normal file
166
src/cv/consts.ts
Normal file
@ -0,0 +1,166 @@
|
||||
export const IMREAD_UNCHANGED = -1;
|
||||
export const IMREAD_GRAYSCALE = 0;
|
||||
export const IMREAD_COLOR_BGR = 1;
|
||||
export const IMREAD_COLOR = 1;
|
||||
export const IMREAD_ANYDEPTH = 2;
|
||||
export const IMREAD_ANYCOLOR = 4;
|
||||
export const IMREAD_LOAD_GDAL = 8;
|
||||
export const IMREAD_REDUCED_GRAYSCALE_2 = 16;
|
||||
export const IMREAD_REDUCED_COLOR_2 = 17;
|
||||
export const IMREAD_REDUCED_GRAYSCALE_4 = 32;
|
||||
export const IMREAD_REDUCED_COLOR_4 = 33;
|
||||
export const IMREAD_REDUCED_GRAYSCALE_8 = 64;
|
||||
export const IMREAD_REDUCED_COLOR_8 = 65;
|
||||
export const IMREAD_IGNORE_ORIENTATION = 128;
|
||||
export const IMREAD_COLOR_RGB = 256;
|
||||
|
||||
export const CV_8U = 0;
|
||||
export const CV_8S = 1;
|
||||
export const CV_16U = 2;
|
||||
export const CV_16S = 3;
|
||||
export const CV_32S = 4;
|
||||
export const CV_32F = 5;
|
||||
export const CV_64F = 6;
|
||||
export const CV_16F = 7;
|
||||
|
||||
const CV_CN_MAX = 512
|
||||
const CV_CN_SHIFT = 3
|
||||
const CV_DEPTH_MAX = (1 << CV_CN_SHIFT)
|
||||
const CV_MAT_DEPTH_MASK = (CV_DEPTH_MAX - 1)
|
||||
const CV_MAT_DEPTH = (flags: number) => ((flags) & CV_MAT_DEPTH_MASK)
|
||||
const CV_MAKETYPE = (depth: number, cn: number) => (CV_MAT_DEPTH(depth) + (((cn) - 1) << CV_CN_SHIFT))
|
||||
|
||||
export const CV_8UC1 = CV_MAKETYPE(CV_8U, 1);
|
||||
export const CV_8UC2 = CV_MAKETYPE(CV_8U, 2);
|
||||
export const CV_8UC3 = CV_MAKETYPE(CV_8U, 3);
|
||||
export const CV_8UC4 = CV_MAKETYPE(CV_8U, 4);
|
||||
export const CV_8UC = (n: number) => CV_MAKETYPE(CV_8U, (n));
|
||||
|
||||
export const CV_8SC1 = CV_MAKETYPE(CV_8S, 1);
|
||||
export const CV_8SC2 = CV_MAKETYPE(CV_8S, 2);
|
||||
export const CV_8SC3 = CV_MAKETYPE(CV_8S, 3);
|
||||
export const CV_8SC4 = CV_MAKETYPE(CV_8S, 4);
|
||||
export const CV_8SC = (n: number) => CV_MAKETYPE(CV_8S, (n));
|
||||
|
||||
export const CV_16UC1 = CV_MAKETYPE(CV_16U, 1);
|
||||
export const CV_16UC2 = CV_MAKETYPE(CV_16U, 2);
|
||||
export const CV_16UC3 = CV_MAKETYPE(CV_16U, 3);
|
||||
export const CV_16UC4 = CV_MAKETYPE(CV_16U, 4);
|
||||
export const CV_16UC = (n: number) => CV_MAKETYPE(CV_16U, (n));
|
||||
|
||||
export const CV_16SC1 = CV_MAKETYPE(CV_16S, 1);
|
||||
export const CV_16SC2 = CV_MAKETYPE(CV_16S, 2);
|
||||
export const CV_16SC3 = CV_MAKETYPE(CV_16S, 3);
|
||||
export const CV_16SC4 = CV_MAKETYPE(CV_16S, 4);
|
||||
export const CV_16SC = (n: number) => CV_MAKETYPE(CV_16S, (n));
|
||||
|
||||
export const CV_32SC1 = CV_MAKETYPE(CV_32S, 1);
|
||||
export const CV_32SC2 = CV_MAKETYPE(CV_32S, 2);
|
||||
export const CV_32SC3 = CV_MAKETYPE(CV_32S, 3);
|
||||
export const CV_32SC4 = CV_MAKETYPE(CV_32S, 4);
|
||||
export const CV_32SC = (n: number) => CV_MAKETYPE(CV_32S, (n));
|
||||
|
||||
export const CV_32FC1 = CV_MAKETYPE(CV_32F, 1);
|
||||
export const CV_32FC2 = CV_MAKETYPE(CV_32F, 2);
|
||||
export const CV_32FC3 = CV_MAKETYPE(CV_32F, 3);
|
||||
export const CV_32FC4 = CV_MAKETYPE(CV_32F, 4);
|
||||
export const CV_32FC = (n: number) => CV_MAKETYPE(CV_32F, (n));
|
||||
|
||||
export const CV_64FC1 = CV_MAKETYPE(CV_64F, 1);
|
||||
export const CV_64FC2 = CV_MAKETYPE(CV_64F, 2);
|
||||
export const CV_64FC3 = CV_MAKETYPE(CV_64F, 3);
|
||||
export const CV_64FC4 = CV_MAKETYPE(CV_64F, 4);
|
||||
export const CV_64FC = (n: number) => CV_MAKETYPE(CV_64F, (n));
|
||||
|
||||
export const CV_16FC1 = CV_MAKETYPE(CV_16F, 1);
|
||||
export const CV_16FC2 = CV_MAKETYPE(CV_16F, 2);
|
||||
export const CV_16FC3 = CV_MAKETYPE(CV_16F, 3);
|
||||
export const CV_16FC4 = CV_MAKETYPE(CV_16F, 4);
|
||||
export const CV_16FC = (n: number) => CV_MAKETYPE(CV_16F, (n));
|
||||
|
||||
|
||||
export const INTER_NEAREST = 0;
|
||||
export const INTER_LINEAR = 1;
|
||||
export const INTER_CUBIC = 2;
|
||||
export const INTER_AREA = 3;
|
||||
export const INTER_LANCZOS4 = 4;
|
||||
export const INTER_LINEAR_EXACT = 5;
|
||||
export const INTER_NEAREST_EXACT = 6;
|
||||
export const INTER_MAX = 7;
|
||||
export const WARP_FILL_OUTLIERS = 8;
|
||||
export const WARP_INVERSE_MAP = 16;
|
||||
export const WARP_RELATIVE_MAP = 32;
|
||||
|
||||
|
||||
export const BORDER_CONSTANT = 0;
|
||||
export const BORDER_REPLICATE = 1;
|
||||
export const BORDER_REFLECT = 2;
|
||||
export const BORDER_WRAP = 3;
|
||||
export const BORDER_REFLECT_101 = 4;
|
||||
export const BORDER_TRANSPARENT = 5;
|
||||
export const BORDER_REFLECT101 = BORDER_REFLECT_101;
|
||||
export const BORDER_DEFAULT = BORDER_REFLECT_101;
|
||||
export const BORDER_ISOLATED = 16;
|
||||
|
||||
export const IMWRITE_JPEG_QUALITY = 1;
|
||||
export const IMWRITE_JPEG_PROGRESSIVE = 2;
|
||||
export const IMWRITE_JPEG_OPTIMIZE = 3;
|
||||
export const IMWRITE_JPEG_RST_INTERVAL = 4;
|
||||
export const IMWRITE_JPEG_LUMA_QUALITY = 5;
|
||||
export const IMWRITE_JPEG_CHROMA_QUALITY = 6;
|
||||
export const IMWRITE_JPEG_SAMPLING_FACTOR = 7;
|
||||
export const IMWRITE_PNG_COMPRESSION = 16;
|
||||
export const IMWRITE_PNG_STRATEGY = 17;
|
||||
export const IMWRITE_PNG_BILEVEL = 18;
|
||||
export const IMWRITE_PXM_BINARY = 32;
|
||||
export const IMWRITE_EXR_TYPE = (3 << 4) + 0;
|
||||
export const IMWRITE_EXR_COMPRESSION = (3 << 4) + 1;
|
||||
export const IMWRITE_EXR_DWA_COMPRESSION_LEVEL = (3 << 4) + 2;
|
||||
export const IMWRITE_WEBP_QUALITY = 64;
|
||||
export const IMWRITE_HDR_COMPRESSION = (5 << 4) + 0;
|
||||
export const IMWRITE_PAM_TUPLETYPE = 128;
|
||||
export const IMWRITE_TIFF_RESUNIT = 256;
|
||||
export const IMWRITE_TIFF_XDPI = 257;
|
||||
export const IMWRITE_TIFF_YDPI = 258;
|
||||
export const IMWRITE_TIFF_COMPRESSION = 259;
|
||||
export const IMWRITE_TIFF_ROWSPERSTRIP = 278;
|
||||
export const IMWRITE_TIFF_PREDICTOR = 317;
|
||||
export const IMWRITE_JPEG2000_COMPRESSION_X1000 = 272;
|
||||
export const IMWRITE_AVIF_QUALITY = 512;
|
||||
export const IMWRITE_AVIF_DEPTH = 513;
|
||||
export const IMWRITE_AVIF_SPEED = 514;
|
||||
export const IMWRITE_JPEGXL_QUALITY = 640;
|
||||
export const IMWRITE_JPEGXL_EFFORT = 641;
|
||||
export const IMWRITE_JPEGXL_DISTANCE = 642;
|
||||
export const IMWRITE_JPEGXL_DECODING_SPEED = 643;
|
||||
export const IMWRITE_GIF_LOOP = 1024;
|
||||
export const IMWRITE_GIF_SPEED = 1025;
|
||||
export const IMWRITE_GIF_QUALITY = 1026;
|
||||
export const IMWRITE_GIF_DITHER = 1027;
|
||||
export const IMWRITE_GIF_TRANSPARENCY = 1028;
|
||||
export const IMWRITE_GIF_COLORTABLE = 1029;
|
||||
|
||||
export const FILLED = -1;
|
||||
export const LINE_4 = 4;
|
||||
export const LINE_8 = 8;
|
||||
export const LINE_AA = 16;
|
||||
|
||||
|
||||
export const FONT_HERSHEY_SIMPLEX = 0;
|
||||
export const FONT_HERSHEY_PLAIN = 1;
|
||||
export const FONT_HERSHEY_DUPLEX = 2;
|
||||
export const FONT_HERSHEY_COMPLEX = 3;
|
||||
export const FONT_HERSHEY_TRIPLEX = 4;
|
||||
export const FONT_HERSHEY_COMPLEX_SMALL = 5;
|
||||
export const FONT_HERSHEY_SCRIPT_SIMPLEX = 6;
|
||||
export const FONT_HERSHEY_SCRIPT_COMPLEX = 7;
|
||||
export const FONT_ITALIC = 16;
|
||||
|
||||
|
||||
export const MARKER_CROSS = 0;
|
||||
export const MARKER_TILTED_CROSS = 1;
|
||||
export const MARKER_STAR = 2;
|
||||
export const MARKER_DIAMOND = 3;
|
||||
export const MARKER_SQUARE = 4;
|
||||
export const MARKER_TRIANGLE_UP = 5;
|
||||
export const MARKER_TRIANGLE_DOWN = 6;
|
104
src/cv/draw.ts
Normal file
104
src/cv/draw.ts
Normal file
@ -0,0 +1,104 @@
|
||||
import { CVMat, CVUtil } from "./addon";
|
||||
import { C, M } from "./common";
|
||||
import { Mat } from "./mat";
|
||||
|
||||
type Color = [b: number, g: number, r: number] | { r: number, g: number, b: number };
|
||||
type Point = [x: number, y: number] | { x: number, y: number };
|
||||
type Size = [width: number, height: number] | { width: number, height: number };
|
||||
type Rect = [x: number, y: number, width: number, height: number] | { x: number, y: number, width: number, height: number };
|
||||
|
||||
function __checkType(val: any, props: string[]) {
|
||||
if (!val) return false;
|
||||
if (val instanceof Array) return val.length === props.length && val.every(i => typeof i === "number");
|
||||
return Object.keys(props).length === props.length && props.every(p => typeof val?.[p] === "number");
|
||||
}
|
||||
function __typeToArray<R = number[]>(val: any, props: string[]) {
|
||||
if (val instanceof Array) return val as R;
|
||||
return props.map(p => val[p]) as R;
|
||||
}
|
||||
|
||||
function isColor(val: any): val is Color { return __checkType(val, ["r", "g", "b"]); }
|
||||
function isPoint(val: any): val is Point { return __checkType(val, ["x", "y"]); }
|
||||
function isSize(val: any): val is Size { return __checkType(val, ["width", "height"]); }
|
||||
function isRect(val: any): val is Rect { return __checkType(val, ["x", "y", "width", "height"]); }
|
||||
|
||||
function getColor(color: Color) { return __typeToArray<[number, number, number]>(color, ["b", "g", "r"]); }
|
||||
function getPoint(point: Point) { return __typeToArray<[number, number]>(point, ["x", "y"]); }
|
||||
function getSize(point: Size) { return __typeToArray<[number, number]>(point, ["width", "height"]); }
|
||||
function getRectPoints(rect: Rect): [number, number, number, number] {
|
||||
const [x, y, width, height] = __typeToArray(rect, ["x", "y", "width", "height"]);
|
||||
return [x, y, x + width, y + height];
|
||||
}
|
||||
|
||||
export function rectangle(img: Mat, rect: Rect, color: Color, thickness?: number, lineType?: number, shift?: number): void
|
||||
export function rectangle(img: Mat, pt1: Point, pt2: Point, color: Color, thickness?: number, lineType?: number, shift?: number): void
|
||||
export function rectangle(img: Mat, x1: number, y1: number, x2: number, y2: number, b: number, g: number, r: number, thickness?: number, lineType?: number, shift?: number): void
|
||||
export function rectangle(img: Mat, ...rest: any[]) {
|
||||
if (isPoint(rest[0])) rectangle(img, ...getPoint(rest[0]), ...getPoint(rest[1]), ...getColor(rest[2]), ...rest.slice(3));
|
||||
else if (isRect(rest[0])) rectangle(img, ...getRectPoints(rest[0]), ...getColor(rest[1]), ...rest.slice(2));
|
||||
else CVMat().Rectangle(M(img), rest[0], rest[1], rest[2], rest[3], rest[4], rest[5], rest[6], rest[7] ?? C("DEFAULT_THICKNESS"), rest[8] ?? C("DEFAULT_LINE_TYPE"), rest[9] ?? 0);
|
||||
}
|
||||
|
||||
export function circle(img: Mat, center: Point, radius: number, color: Color, thickness?: number, lineType?: number, shift?: number): void
|
||||
export function circle(img: Mat, x: number, y: number, radius: number, b: number, g: number, r: number, thickness?: number, lineType?: number, shift?: number): void
|
||||
export function circle(img: Mat, ...rest: any[]) {
|
||||
if (isPoint(rest[0])) circle(img, ...getPoint(rest[0]), rest[1], ...getColor(rest[2]), ...rest.slice(3));
|
||||
else CVMat().Circle(M(img), rest[0], rest[1], rest[2], rest[3], rest[4], rest[5], rest[6] ?? C("DEFAULT_THICKNESS"), rest[7] ?? C("DEFAULT_LINE_TYPE"), rest[8] ?? 0);
|
||||
}
|
||||
|
||||
export function line(img: Mat, pt1: Point, pt2: Point, color: Color, thickness?: number, lineType?: number, shift?: number): void
|
||||
export function line(img: Mat, x1: number, y1: number, x2: number, y2: number, b: number, g: number, r: number, thickness?: number, lineType?: number, shift?: number): void
|
||||
export function line(img: Mat, ...rest: any[]) {
|
||||
if (isPoint(rest[0])) line(img, ...getPoint(rest[0]), ...getPoint(rest[1]), ...getColor(rest[2]), ...rest.slice(3));
|
||||
else CVMat().Line(M(img), rest[0], rest[1], rest[2], rest[3], rest[4], rest[5], rest[6], rest[7] ?? C("DEFAULT_THICKNESS"), rest[8] ?? C("DEFAULT_LINE_TYPE"), rest[9] ?? 0);
|
||||
}
|
||||
|
||||
export function ellipse(img: Mat, center: Point, size: Size, angle: number, startAngle: number, endAngle: number, color: Color, thickness?: number, lineType?: number, shift?: number): void
|
||||
export function ellipse(img: Mat, centerX: number, centerY: number, width: number, height: number, angle: number, startAngle: number, endAngle: number, b: number, g: number, r: number, thickness?: number, lineType?: number, shift?: number): void
|
||||
export function ellipse(img: Mat, ...rest: any[]) {
|
||||
if (isPoint(rest[0])) ellipse(img, ...getPoint(rest[0]), ...getSize(rest[1]), rest[2], rest[3], rest[4], ...getColor(rest[5]), ...rest.slice(6));
|
||||
else CVMat().Ellipse(M(img), rest[0], rest[1], rest[2], rest[3], rest[4], rest[5], rest[6], rest[7], rest[8], rest[9], rest[10] ?? C("DEFAULT_THICKNESS"), rest[11] ?? C("DEFAULT_LINE_TYPE"), rest[12] ?? 0);
|
||||
}
|
||||
|
||||
export function polylines(img: Mat, points: Point[], isClosed: boolean, color: Color, thickness?: number, lineType?: number, shift?: number): void;
|
||||
export function polylines(img: Mat, points: Point[], isClosed: boolean, b: number, g: number, r: number, thickness?: number, lineType?: number, shift?: number): void;
|
||||
export function polylines(img: Mat, points: Point[], isClosed: boolean, ...rest: any[]) {
|
||||
if (isColor(rest[0])) polylines(img, points, isClosed, ...getColor(rest[0]), rest[1], rest[2], rest[3]);
|
||||
else CVMat().Polylines(M(img), points.map(p => getPoint(p)), isClosed, rest[0], rest[1], rest[2], rest[3] ?? C("DEFAULT_THICKNESS"), rest[4] ?? C("DEFAULT_LINE_TYPE"), rest[5] ?? 0);
|
||||
}
|
||||
|
||||
export function fillPoly(img: Mat, points: Point[], color: Color, lineType?: number, shift?: number): void;
|
||||
export function fillPoly(img: Mat, points: Point[], b: number, g: number, r: number, lineType?: number, shift?: number): void;
|
||||
export function fillPoly(img: Mat, points: Point[], ...rest: any[]) {
|
||||
if (isColor(rest[0])) fillPoly(img, points, ...getColor(rest[0]), rest[1], rest[2]);
|
||||
else CVMat().FillPoly(M(img), points.map(p => getPoint(p)), rest[0], rest[1], rest[2], rest[3] ?? C("DEFAULT_LINE_TYPE"), rest[4] ?? 0);
|
||||
}
|
||||
|
||||
export function fillConvexPoly(img: Mat, points: Point[], color: Color, lineType?: number, shift?: number): void;
|
||||
export function fillConvexPoly(img: Mat, points: Point[], b: number, g: number, r: number, lineType?: number, shift?: number): void;
|
||||
export function fillConvexPoly(img: Mat, points: Point[], ...rest: any[]) {
|
||||
if (isColor(rest[0])) fillConvexPoly(img, points, ...getColor(rest[0]), rest[1], rest[2]);
|
||||
else CVMat().FillConvexPoly(M(img), points.map(p => getPoint(p)), rest[0], rest[1], rest[2], rest[3] ?? C("DEFAULT_LINE_TYPE"), rest[4] ?? 0);
|
||||
}
|
||||
|
||||
export function drawMarker(img: Mat, pos: Point, color: Color, markerType?: number, markerSize?: number, lineType?: number, shift?: number): void;
|
||||
export function drawMarker(img: Mat, x: number, y: number, b: number, g: number, r: number, markerType?: number, markerSize?: number, lineType?: number, shift?: number): void;
|
||||
export function drawMarker(img: Mat, ...rest: any[]) {
|
||||
if (isPoint(rest[0])) drawMarker(img, ...getPoint(rest[0]), ...getColor(rest[1]), rest[2], rest[3], rest[4], rest[5]);
|
||||
else CVMat().DrawMarker(M(img), rest[0], rest[1], rest[2], rest[3], rest[4], rest[5] ?? C("DEFAULT_MARKER"), rest[6] ?? 20, rest[7] ?? 1, rest[8] ?? C("DEFAULT_LINE_TYPE"));
|
||||
}
|
||||
|
||||
export function putText(img: Mat, text: string, org: Point, fontFace: number, fontScale: number, color: Color, thickness?: number, lineType?: number, bottomLeftOrigin?: boolean): void
|
||||
export function putText(img: Mat, text: string, x: number, y: number, fontFace: number, fontScale: number, b: number, g: number, r: number, thickness?: number, lineType?: number, bottomLeftOrigin?: boolean): void
|
||||
export function putText(img: Mat, text: string, ...rest: any[]) {
|
||||
if (isPoint(rest[0])) putText(img, text, ...getPoint(rest[0]), rest[1], rest[2], ...getColor(rest[3]), rest[4], rest[5], rest[6]);
|
||||
else CVMat().PutText(M(img), text, rest[0], rest[1], rest[2], rest[3], rest[4], rest[5], rest[6], rest[7] ?? C("DEFAULT_THICKNESS"), rest[8] ?? C("DEFAULT_LINE_TYPE"), rest[9] ?? false);
|
||||
}
|
||||
|
||||
export function getTextSize(text: string, fontFace: number, fontScale: number, thickness?: number) {
|
||||
return CVUtil().GetTextSize(text, fontFace, fontScale, thickness ?? C("DEFAULT_THICKNESS"));
|
||||
}
|
||||
|
||||
export function getFontScaleFromHeight(fontFace: number, pixelHeight: number, thickness?: number) {
|
||||
return CVUtil().GetFontScaleFromHeight(fontFace, pixelHeight, thickness ?? C("DEFAULT_THICKNESS"));
|
||||
}
|
5
src/cv/index.ts
Normal file
5
src/cv/index.ts
Normal file
@ -0,0 +1,5 @@
|
||||
export { setConfig as config } from "./addon";
|
||||
export * from "./mat";
|
||||
export * from "./consts";
|
||||
export * from "./proc";
|
||||
export * from "./draw";
|
55
src/cv/mat.ts
Normal file
55
src/cv/mat.ts
Normal file
@ -0,0 +1,55 @@
|
||||
import { CVMat } from "./addon";
|
||||
import { FromCV, M, TypedArray } from "./common";
|
||||
import { IMREAD_COLOR_BGR } from "./consts";
|
||||
|
||||
|
||||
export class Mat {
|
||||
#mat: any
|
||||
public constructor();
|
||||
public constructor(rows: number, cols: number, type: number, data?: TypedArray);
|
||||
public constructor(sizes: number[], type: number, data?: TypedArray);
|
||||
public constructor(va?: any, vb?: any, vc?: any, vd?: any) {
|
||||
const _CVMat = CVMat();
|
||||
if (va instanceof _CVMat) this.#mat = va;
|
||||
if (typeof va === "number") this.#mat = new _CVMat([va, vb], vc, vd);
|
||||
else if (typeof va === "undefined") this.#mat = new _CVMat();
|
||||
else if (va instanceof Array) this.#mat = new _CVMat(va, vb, vc);
|
||||
}
|
||||
|
||||
private get __mat__() { return this.#mat; }
|
||||
|
||||
public get empty(): boolean { return this.#mat.IsEmpty(); }
|
||||
public get cols(): number { return this.#mat.GetCols(); }
|
||||
public get rows(): number { return this.#mat.GetRows(); }
|
||||
public get type(): number { return this.#mat.GetType(); }
|
||||
public get depth(): number { return this.#mat.GetDepth(); }
|
||||
public get channels(): number { return this.#mat.GetChannels(); }
|
||||
public get flags(): number { return this.#mat.GetFlags(); }
|
||||
public get dims(): number { return this.#mat.GetDims(); }
|
||||
public get isContinuous(): boolean { return this.#mat.GetIsContinuous(); }
|
||||
public get isSubmatrix(): boolean { return this.#mat.GetIsSubmatrix(); }
|
||||
public get elemSize(): number { return this.#mat.GetElemSize(); }
|
||||
public get elemSize1(): number { return this.#mat.GetElemSize1(); }
|
||||
public total(startDim?: number, endDim?: number): number {
|
||||
if (startDim === null || startDim === undefined) return this.#mat.GetTotal();
|
||||
else return this.#mat.GetTotalWithDim(startDim, endDim ?? 2147483647);
|
||||
}
|
||||
public get size(): number[] { return this.#mat.GetSize(); }
|
||||
|
||||
public clone() { return FromCV(this.#mat.Clone()); }
|
||||
|
||||
public row(y: number) { return FromCV(this.#mat.Row(y)); }
|
||||
public col(x: number) { return FromCV(this.#mat.Col(x)); }
|
||||
public rowRange(start: number, end: number) { return FromCV(this.#mat.RowRange(start, end)); }
|
||||
public colRange(start: number, end: number) { return FromCV(this.#mat.ColRange(start, end)); }
|
||||
public diag(d: number) { return FromCV(this.#mat.Diag(d)); }
|
||||
};
|
||||
|
||||
export function imread(filename: string, flag?: number) { return FromCV(CVMat().ImRead(filename, flag ?? IMREAD_COLOR_BGR)); }
|
||||
export function imdecode(data: Uint8Array, flag?: number) { return FromCV(CVMat().ImDecode(data, flag ?? IMREAD_COLOR_BGR)); }
|
||||
export function imwrite(filename: string, mat: Mat, params?: number[]): boolean { return CVMat().ImWrite(M(mat), filename, params); }
|
||||
export function imencode(ext: string, mat: Mat, params?: number[]): Uint8Array | null {
|
||||
const res = CVMat().ImEncode(M(mat), ext, params);
|
||||
if (!res) return null;
|
||||
return new Uint8Array(res);
|
||||
}
|
53
src/cv/proc.ts
Normal file
53
src/cv/proc.ts
Normal file
@ -0,0 +1,53 @@
|
||||
import { Mat } from "./mat";
|
||||
import { FromCV, M, resolveArgs } from "./common";
|
||||
import { BORDER_CONSTANT, BORDER_DEFAULT, INTER_LINEAR } from "./consts";
|
||||
import { CVMat } from "./addon";
|
||||
|
||||
type CropRange = { x: number, y: number, width: number, height: number } | Array<{ start: number, end: number }>;
|
||||
export function crop(src: Mat, range: CropRange): Mat;
|
||||
export function crop(src: Mat, dst: Mat, range: CropRange): Mat;
|
||||
export function crop(...args: any[]) {
|
||||
let [src, dst, range] = resolveArgs<[src: Mat, dst: Mat, range: CropRange]>(args, args[1] instanceof Mat);
|
||||
if (!(range instanceof Array)) range = [{ start: range.y, end: range.y + range.height }, { start: range.x, end: range.x + range.width }];
|
||||
return FromCV(CVMat().Crop(M(src), M(dst), range));
|
||||
}
|
||||
|
||||
export function resize(src: Mat, width: number, height: number, fx?: number, fy?: number, interpolation?: number): Mat;
|
||||
export function resize(src: Mat, dst: Mat, width: number, height: number, fx?: number, fy?: number, interpolation?: number): Mat;
|
||||
export function resize(...args: any[]) {
|
||||
const [src, dst, width, height, fx, fy, interpolation] = resolveArgs<[src: Mat, dst: Mat, width: number, height: number, fx?: number, fy?: number, interpolation?: number]>(args, args[1] instanceof Mat);
|
||||
return FromCV(CVMat().Resize(M(src), M(dst), width, height, fx ?? 0, fy ?? 0, interpolation ?? INTER_LINEAR));
|
||||
}
|
||||
|
||||
export function blur(src: Mat, kernelWidth: number, kernelHeight: number, anchorX?: number, anchorY?: number, borderType?: number): Mat
|
||||
export function blur(src: Mat, dst: Mat, kernelWidth: number, kernelHeight: number, anchorX?: number, anchorY?: number, borderType?: number): Mat
|
||||
export function blur(...args: any[]) {
|
||||
let [src, dst, kernelWidth, kernelHeight, anchorX = -1, anchorY = -1, borderType = BORDER_DEFAULT] = resolveArgs<[src: Mat, dst: Mat, kernelWidth: number, kernelHeight: number, anchorX?: number, anchorY?: number, borderType?: number]>(args, args[1] instanceof Mat);
|
||||
if (anchorX == -1 || anchorY == -1) anchorX = anchorY = -1;
|
||||
return FromCV(CVMat().Blur(M(src), M(dst), kernelWidth, kernelHeight, anchorX, anchorY, borderType));
|
||||
}
|
||||
|
||||
export function copyMakeBorder(src: Mat, top: number, bottom: number, left: number, right: number, borderType: number): Mat
|
||||
export function copyMakeBorder(src: Mat, dst: Mat, top: number, bottom: number, left: number, right: number, borderType: number): Mat
|
||||
export function copyMakeBorder(...args: any[]) {
|
||||
const [src, dst, top, bottom, left, right, borderType] = resolveArgs<[src: Mat, dst: Mat, top: number, bottom: number, left: number, right: number, borderType: number]>(args, args[1] instanceof Mat);
|
||||
return FromCV(CVMat().CopyMakeBorder(M(src), M(dst), top, bottom, left, right, borderType));
|
||||
}
|
||||
|
||||
type FlipCode = 0 | 1 | -1 | "x" | "y" | "both";
|
||||
export function flip(src: Mat, flipCode: FlipCode): Mat
|
||||
export function flip(src: Mat, dst: Mat, flipCode: FlipCode): Mat
|
||||
export function flip(...args: any[]) {
|
||||
let [src, dst, flipCode] = resolveArgs<[src: Mat, dst: Mat, flipCode: FlipCode]>(args, args[1] instanceof Mat);
|
||||
if (typeof flipCode == "string") flipCode = ({ x: 0, y: 1, both: -1 } as Record<"x" | "y" | "both", 0 | 1 | -1>)[flipCode];
|
||||
return FromCV(CVMat().Flip(M(src), M(dst), flipCode));
|
||||
}
|
||||
|
||||
export function warpAffine(src: Mat, transfromMat: Mat, dwidth: number, dheight: number, flags?: number, borderMode?: number): Mat;
|
||||
export function warpAffine(src: Mat, dst: Mat, transfromMat: Mat, dwidth: number, dheight: number, flags?: number, borderMode?: number): Mat;
|
||||
export function warpAffine(...args: any[]) {
|
||||
const [src, dst, transfromMat, dwidth, dheight, flags, borderMode] = resolveArgs<[src: Mat, dst: Mat, transfromMat: Mat, dwidth: number, dheight: number, flags?: number, borderMode?: number]>(args, () => args[2] instanceof Mat);
|
||||
return FromCV(CVMat().WarpAffine(M(src), M(dst), M(transfromMat), dwidth, dheight, flags ?? INTER_LINEAR, borderMode ?? BORDER_CONSTANT));
|
||||
}
|
||||
|
||||
export function getRotationMatrix2D(centerX: number, centerY: number, angle: number, scale: number) { return FromCV(CVMat().GetRotationMatrix2D(centerX, centerY, angle, scale)); }
|
3
src/index.ts
Normal file
3
src/index.ts
Normal file
@ -0,0 +1,3 @@
|
||||
import * as cv from "./cv";
|
||||
export default cv;
|
||||
export { cv };
|
34
src/test.ts
Normal file
34
src/test.ts
Normal file
@ -0,0 +1,34 @@
|
||||
import cv from ".";
|
||||
|
||||
|
||||
async function test() {
|
||||
const fs = await import("fs");
|
||||
// const buffer = fs.readFileSync("data/im1.jpeg")
|
||||
const res = await cv.imread("test_data/im1.jpeg");
|
||||
// const res = await cv.imdecode(buffer);
|
||||
const cropIm = cv.crop(res, { x: 10, y: 10, width: 300, height: 200 });
|
||||
|
||||
console.log(cv.imwrite("test_data/cropIm.jpg", cropIm));
|
||||
fs.writeFileSync("test_data/base.jpg", cv.imencode(".jpg", res)!);
|
||||
|
||||
const rotated = cv.warpAffine(res, cv.getRotationMatrix2D(100, 100, 50, 1), res.cols, res.rows);
|
||||
cv.imwrite("test_data/rotated.jpg", rotated);
|
||||
|
||||
cv.imwrite("test_data/blur.jpg", cv.blur(res, 20, 20));
|
||||
|
||||
cv.imwrite("test_data/copyMakeBorder.jpg", cv.copyMakeBorder(res, 100, 30, 50, 40, cv.BORDER_CONSTANT));
|
||||
|
||||
cv.imwrite("test_data/flip.jpg", cv.flip(res, "both"));
|
||||
|
||||
cv.rectangle(res, [10, 10, 200, 200], [0, 0, 255], 3);
|
||||
cv.circle(res, [200, 200], 200, [255, 0, 0], 4);
|
||||
cv.line(res, [100, 100], [300, 500], [0, 0, 255], 3);
|
||||
cv.ellipse(res, [300, 500], [200, 100], 45, 0, 360, [0, 255, 0], 3);
|
||||
cv.polylines(res, [[50, 50], [150, 150], [50, 150]], true, [255, 0, 255], 3);
|
||||
cv.fillConvexPoly(res, [[50, 50], [150, 150], [150, 50]], [255, 0, 255]);
|
||||
cv.drawMarker(res, [300, 200], [255, 255, 0]);
|
||||
cv.imwrite("test_data/draw.jpg", res);
|
||||
}
|
||||
|
||||
|
||||
test();
|
BIN
test_data/im1.jpeg
Normal file
BIN
test_data/im1.jpeg
Normal file
Binary file not shown.
After Width: | Height: | Size: 84 KiB |
18
thirdpart/cmake-js-util.js
Normal file
18
thirdpart/cmake-js-util.js
Normal file
@ -0,0 +1,18 @@
|
||||
const os = require("os");
|
||||
const path = require("path");
|
||||
const { execSync, spawnSync, exec } = require("child_process");
|
||||
|
||||
const args = process.argv.slice(2);
|
||||
|
||||
const cmakeJS = path.join(__dirname, "../node_modules/.bin/cmake-js")
|
||||
|
||||
|
||||
function runCmakeJS(args) {
|
||||
const child = exec(`${cmakeJS} ${args.join(" ")}`, (err, stdout, stderr) => console.log(stdout));
|
||||
child.once("close", code => process.exit(code));
|
||||
}
|
||||
|
||||
if (args.includes("--include")) runCmakeJS(["print-cmakejs-include"]);
|
||||
else if (args.includes("--src")) runCmakeJS(["print-cmakejs-src"]);
|
||||
else if (args.includes("--lib")) runCmakeJS(["print-cmakejs-lib"]);
|
||||
else if (args.includes("--napi")) console.log(require("node-addon-api").include.replace(/^"/, "").replace(/"$/, ""));
|
175
thirdpart/install.js
Normal file
175
thirdpart/install.js
Normal file
@ -0,0 +1,175 @@
|
||||
|
||||
|
||||
const fs = require("fs");
|
||||
const os = require("os");
|
||||
const path = require("path");
|
||||
const { spawnSync } = require("child_process");
|
||||
const args = process.argv.slice(2);
|
||||
const THIRDPARTY_DIR = __dirname;
|
||||
|
||||
function findArg(argname, isBoolean) {
|
||||
let prefix = `--${argname}`;
|
||||
let idx = args.findIndex(arg => arg == prefix);
|
||||
if (idx >= 0) {
|
||||
if (isBoolean) return true;
|
||||
else return args[idx + 1];
|
||||
}
|
||||
idx = args.findIndex(arg => arg.startsWith(prefix))
|
||||
if (idx >= 0) return args[idx].substring(prefix.length + 1);
|
||||
return null;
|
||||
}
|
||||
|
||||
function assert(cond, message) {
|
||||
if (!cond) {
|
||||
console.error(message);
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
const buildOptions = {
|
||||
withOpenCV: findArg("with-opencv", true) ?? false,
|
||||
buildType: findArg("build-type", false) ?? "Release",
|
||||
proxy: findArg("proxy"),
|
||||
generator: findArg("generator"),
|
||||
}
|
||||
|
||||
const spawnOption = {
|
||||
stdio: "inherit", env: {
|
||||
...process.env, ...buildOptions.proxy ? {
|
||||
"http_proxy": buildOptions.proxy,
|
||||
"https_proxy": buildOptions.proxy,
|
||||
} : {}
|
||||
}
|
||||
};
|
||||
|
||||
function P(path) { return path.replace(/\\/g, "/"); }
|
||||
|
||||
function checkFile(...items) {
|
||||
return fs.existsSync(path.resolve(...items));
|
||||
}
|
||||
|
||||
function downloadSource(name, repo, branch, recursive, checkedFile) {
|
||||
const sourceRootDir = path.join(THIRDPARTY_DIR, "_source");
|
||||
fs.mkdirSync(sourceRootDir, { recursive: true });
|
||||
const sourceDir = path.join(sourceRootDir, name);
|
||||
if (checkFile(sourceDir, checkedFile)) return sourceDir;
|
||||
|
||||
console.log(`${name}源代码不存在,正在下载...`);
|
||||
assert(spawnSync("git", [
|
||||
"clone",
|
||||
...recursive ? ["--recursive"] : [],
|
||||
repo, "-b", branch,
|
||||
sourceDir
|
||||
], { ...spawnOption }).status == 0, `下载${name}源代码失败`);
|
||||
|
||||
return sourceDir;
|
||||
}
|
||||
|
||||
function cmakeBuildFromSource(name, repo, branch, downloader, buildArgs, cmakeCreator) {
|
||||
const sourceDir = downloader ? downloader(name, repo, branch) : downloadSource(name, repo, branch, false, "CMakeLists.txt");
|
||||
const buildDir = path.join(sourceDir, "build", buildOptions.buildType);
|
||||
const installDir = path.join(THIRDPARTY_DIR, name, buildOptions.buildType);
|
||||
const configFile = path.join(installDir, "config.cmake");
|
||||
|
||||
if (typeof buildArgs == "function") buildArgs = buildArgs(sourceDir, buildDir, installDir);
|
||||
|
||||
//编译源代码
|
||||
if (checkFile(configFile)) return
|
||||
console.log(`${name}目标不存在,正在编译...`);
|
||||
fs.rmSync(buildDir, { recursive: true, force: true });
|
||||
fs.mkdirSync(buildDir, { recursive: true });
|
||||
|
||||
assert(spawnSync("cmake", [
|
||||
"-G", buildOptions.generator ?? "Ninja", sourceDir,
|
||||
"-DCMAKE_BUILD_TYPE=" + buildOptions.buildType,
|
||||
"-DCMAKE_INSTALL_PREFIX=" + installDir,
|
||||
...(buildArgs ?? []).map(arg => arg.startsWith("-D") ? arg : `-D${arg}`),
|
||||
], { ...spawnOption, cwd: buildDir }).status == 0, `配置${name}失败`);
|
||||
|
||||
assert(spawnSync("cmake", [
|
||||
"--build", ".",
|
||||
"--parallel", os.cpus().length.toString(),
|
||||
"--config", buildOptions.buildType,
|
||||
"--target", "install",
|
||||
], { ...spawnOption, cwd: buildDir }).status == 0, `编译${name}失败`);
|
||||
|
||||
fs.writeFileSync(configFile, cmakeCreator(installDir));
|
||||
}
|
||||
|
||||
async function downloadFromURL(name, url, resolver) {
|
||||
const cacheDir = path.join(THIRDPARTY_DIR, "_cache");
|
||||
fs.mkdirSync(cacheDir, { recursive: true });
|
||||
const saveName = path.join(cacheDir, path.basename(url));
|
||||
const outputDir = path.join(cacheDir, name);
|
||||
fs.rmSync(outputDir, { force: true, recursive: true });
|
||||
|
||||
if (!checkFile(saveName)) {
|
||||
console.log(`开始下载${name}, 地址:${url}`);
|
||||
await fetch(url).then(res => {
|
||||
console.log(res.status)
|
||||
})
|
||||
const result = spawnSync("curl", ["-o", saveName + ".cache", "-L", url, "-s", "-w", "%{http_code}"], { ...spawnOption, stdio: "pipe" });
|
||||
assert(result.status == 0 && result.stdout.toString() == "200", `下载${name}失败`);
|
||||
fs.renameSync(saveName + ".cache", saveName);
|
||||
}
|
||||
let currentName = saveName;
|
||||
|
||||
if (saveName.endsWith(".bz2")) {
|
||||
currentName = saveName.substring(0, saveName.length - 4);
|
||||
const input = fs.createReadStream(saveName);
|
||||
const out = fs.createWriteStream(currentName);
|
||||
const bz2 = require("unbzip2-stream")();
|
||||
await new Promise((resolve, reject) => {
|
||||
input.pipe(bz2).pipe(out);
|
||||
out.on("close", resolve);
|
||||
input.on("error", reject);
|
||||
bz2.on("error", reject);
|
||||
out.on("error", reject);
|
||||
});
|
||||
}
|
||||
|
||||
const format = path.extname(currentName);
|
||||
assert([".tar", ".gz", ".tgz", ".zip"].includes(format), "仅支持.tar, .gz, .tgz, .zip格式的压缩文件");
|
||||
const method = {
|
||||
".tar": require("compressing").tar,
|
||||
".gz": require("compressing").gzip,
|
||||
".tgz": require("compressing").tgz,
|
||||
".zip": require("compressing").zip,
|
||||
}[format];
|
||||
await method.uncompress(currentName, outputDir);
|
||||
|
||||
await resolver(outputDir);
|
||||
|
||||
fs.rmSync(outputDir, { recursive: true, force: true });
|
||||
}
|
||||
|
||||
|
||||
async function main() {
|
||||
//OpenCV
|
||||
if (buildOptions.withOpenCV) cmakeBuildFromSource("OpenCV", "https://github.com/opencv/opencv.git", "4.11.0", null, [
|
||||
"-DBUILD_SHARED_LIBS=OFF",
|
||||
"-DBUILD_opencv_apps=OFF",
|
||||
"-DBUILD_opencv_js=OFF",
|
||||
"-DBUILD_opencv_python2=OFF",
|
||||
"-DBUILD_opencv_python3=OFF",
|
||||
"-DBUILD_ANDROID_PROJECTS=OFF",
|
||||
"-DBUILD_ANDROID_EXAMPLES=OFF",
|
||||
"-DBUILD_TESTS=OFF",
|
||||
"-DBUILD_FAT_JAVA_LIB=OFF",
|
||||
"-DBUILD_ANDROID_SERVICE=OFF",
|
||||
"-DBUILD_JAVA=OFF",
|
||||
"-DBUILD_PERF_TESTS=OFF"
|
||||
], (root) => [
|
||||
`set(OpenCV_STATIC ON)`,
|
||||
os.platform() == "win32" ?
|
||||
`include(${JSON.stringify(P(path.join(root, "OpenCVConfig.cmake")))})` :
|
||||
`include(${JSON.stringify(P(path.join(root, "lib/cmake/opencv4/OpenCVConfig.cmake")))})`,
|
||||
`set(OpenCV_INCLUDE_DIR \${OpenCV_INCLUDE_DIRS})`,
|
||||
os.platform() == "win32" ?
|
||||
"set(OpenCV_LIB_DIR ${OpenCV_LIB_PATH})" :
|
||||
`set(OpenCV_LIB_DIR ${JSON.stringify(P(path.join(root, "lib")))})`,
|
||||
// `set(OpenCV_LIBS OpenCV_LIBS)`,
|
||||
].join("\n"));
|
||||
}
|
||||
|
||||
main();
|
108
tsconfig.json
Normal file
108
tsconfig.json
Normal file
@ -0,0 +1,108 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
/* Visit https://aka.ms/tsconfig to read more about this file */
|
||||
|
||||
/* Projects */
|
||||
// "incremental": true, /* Save .tsbuildinfo files to allow for incremental compilation of projects. */
|
||||
// "composite": true, /* Enable constraints that allow a TypeScript project to be used with project references. */
|
||||
// "tsBuildInfoFile": "./.tsbuildinfo", /* Specify the path to .tsbuildinfo incremental compilation file. */
|
||||
// "disableSourceOfProjectReferenceRedirect": true, /* Disable preferring source files instead of declaration files when referencing composite projects. */
|
||||
// "disableSolutionSearching": true, /* Opt a project out of multi-project reference checking when editing. */
|
||||
// "disableReferencedProjectLoad": true, /* Reduce the number of projects loaded automatically by TypeScript. */
|
||||
|
||||
/* Language and Environment */
|
||||
"target": "ESNext", /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */
|
||||
// "lib": [], /* Specify a set of bundled library declaration files that describe the target runtime environment. */
|
||||
// "jsx": "preserve", /* Specify what JSX code is generated. */
|
||||
// "experimentalDecorators": true, /* Enable experimental support for legacy experimental decorators. */
|
||||
// "emitDecoratorMetadata": true, /* Emit design-type metadata for decorated declarations in source files. */
|
||||
// "jsxFactory": "", /* Specify the JSX factory function used when targeting React JSX emit, e.g. 'React.createElement' or 'h'. */
|
||||
// "jsxFragmentFactory": "", /* Specify the JSX Fragment reference used for fragments when targeting React JSX emit e.g. 'React.Fragment' or 'Fragment'. */
|
||||
// "jsxImportSource": "", /* Specify module specifier used to import the JSX factory functions when using 'jsx: react-jsx*'. */
|
||||
// "reactNamespace": "", /* Specify the object invoked for 'createElement'. This only applies when targeting 'react' JSX emit. */
|
||||
// "noLib": true, /* Disable including any library files, including the default lib.d.ts. */
|
||||
// "useDefineForClassFields": true, /* Emit ECMAScript-standard-compliant class fields. */
|
||||
// "moduleDetection": "auto", /* Control what method is used to detect module-format JS files. */
|
||||
|
||||
/* Modules */
|
||||
"module": "commonjs", /* Specify what module code is generated. */
|
||||
"rootDir": "./src", /* Specify the root folder within your source files. */
|
||||
// "moduleResolution": "node10", /* Specify how TypeScript looks up a file from a given module specifier. */
|
||||
// "baseUrl": "./", /* Specify the base directory to resolve non-relative module names. */
|
||||
// "paths": {}, /* Specify a set of entries that re-map imports to additional lookup locations. */
|
||||
// "rootDirs": [], /* Allow multiple folders to be treated as one when resolving modules. */
|
||||
// "typeRoots": [], /* Specify multiple folders that act like './node_modules/@types'. */
|
||||
// "types": [], /* Specify type package names to be included without being referenced in a source file. */
|
||||
// "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */
|
||||
// "moduleSuffixes": [], /* List of file name suffixes to search when resolving a module. */
|
||||
// "allowImportingTsExtensions": true, /* Allow imports to include TypeScript file extensions. Requires '--moduleResolution bundler' and either '--noEmit' or '--emitDeclarationOnly' to be set. */
|
||||
// "resolvePackageJsonExports": true, /* Use the package.json 'exports' field when resolving package imports. */
|
||||
// "resolvePackageJsonImports": true, /* Use the package.json 'imports' field when resolving imports. */
|
||||
// "customConditions": [], /* Conditions to set in addition to the resolver-specific defaults when resolving imports. */
|
||||
// "resolveJsonModule": true, /* Enable importing .json files. */
|
||||
// "allowArbitraryExtensions": true, /* Enable importing files with any extension, provided a declaration file is present. */
|
||||
// "noResolve": true, /* Disallow 'import's, 'require's or '<reference>'s from expanding the number of files TypeScript should add to a project. */
|
||||
|
||||
/* JavaScript Support */
|
||||
// "allowJs": true, /* Allow JavaScript files to be a part of your program. Use the 'checkJS' option to get errors from these files. */
|
||||
// "checkJs": true, /* Enable error reporting in type-checked JavaScript files. */
|
||||
// "maxNodeModuleJsDepth": 1, /* Specify the maximum folder depth used for checking JavaScript files from 'node_modules'. Only applicable with 'allowJs'. */
|
||||
|
||||
/* Emit */
|
||||
"declaration": true, /* Generate .d.ts files from TypeScript and JavaScript files in your project. */
|
||||
// "declarationMap": true, /* Create sourcemaps for d.ts files. */
|
||||
// "emitDeclarationOnly": true, /* Only output d.ts files and not JavaScript files. */
|
||||
// "sourceMap": true, /* Create source map files for emitted JavaScript files. */
|
||||
// "inlineSourceMap": true, /* Include sourcemap files inside the emitted JavaScript. */
|
||||
// "outFile": "./", /* Specify a file that bundles all outputs into one JavaScript file. If 'declaration' is true, also designates a file that bundles all .d.ts output. */
|
||||
"outDir": "./dist", /* Specify an output folder for all emitted files. */
|
||||
// "removeComments": true, /* Disable emitting comments. */
|
||||
// "noEmit": true, /* Disable emitting files from a compilation. */
|
||||
// "importHelpers": true, /* Allow importing helper functions from tslib once per project, instead of including them per-file. */
|
||||
// "downlevelIteration": true, /* Emit more compliant, but verbose and less performant JavaScript for iteration. */
|
||||
// "sourceRoot": "", /* Specify the root path for debuggers to find the reference source code. */
|
||||
// "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */
|
||||
// "inlineSources": true, /* Include source code in the sourcemaps inside the emitted JavaScript. */
|
||||
// "emitBOM": true, /* Emit a UTF-8 Byte Order Mark (BOM) in the beginning of output files. */
|
||||
// "newLine": "crlf", /* Set the newline character for emitting files. */
|
||||
// "stripInternal": true, /* Disable emitting declarations that have '@internal' in their JSDoc comments. */
|
||||
// "noEmitHelpers": true, /* Disable generating custom helper functions like '__extends' in compiled output. */
|
||||
// "noEmitOnError": true, /* Disable emitting files if any type checking errors are reported. */
|
||||
// "preserveConstEnums": true, /* Disable erasing 'const enum' declarations in generated code. */
|
||||
"declarationDir": "./typing", /* Specify the output directory for generated declaration files. */
|
||||
|
||||
/* Interop Constraints */
|
||||
// "isolatedModules": true, /* Ensure that each file can be safely transpiled without relying on other imports. */
|
||||
// "verbatimModuleSyntax": true, /* Do not transform or elide any imports or exports not marked as type-only, ensuring they are written in the output file's format based on the 'module' setting. */
|
||||
// "isolatedDeclarations": true, /* Require sufficient annotation on exports so other tools can trivially generate declaration files. */
|
||||
// "allowSyntheticDefaultImports": true, /* Allow 'import x from y' when a module doesn't have a default export. */
|
||||
"esModuleInterop": true, /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility. */
|
||||
// "preserveSymlinks": true, /* Disable resolving symlinks to their realpath. This correlates to the same flag in node. */
|
||||
"forceConsistentCasingInFileNames": true, /* Ensure that casing is correct in imports. */
|
||||
|
||||
/* Type Checking */
|
||||
"strict": true, /* Enable all strict type-checking options. */
|
||||
// "noImplicitAny": true, /* Enable error reporting for expressions and declarations with an implied 'any' type. */
|
||||
// "strictNullChecks": true, /* When type checking, take into account 'null' and 'undefined'. */
|
||||
// "strictFunctionTypes": true, /* When assigning functions, check to ensure parameters and the return values are subtype-compatible. */
|
||||
// "strictBindCallApply": true, /* Check that the arguments for 'bind', 'call', and 'apply' methods match the original function. */
|
||||
// "strictPropertyInitialization": true, /* Check for class properties that are declared but not set in the constructor. */
|
||||
// "noImplicitThis": true, /* Enable error reporting when 'this' is given the type 'any'. */
|
||||
// "useUnknownInCatchVariables": true, /* Default catch clause variables as 'unknown' instead of 'any'. */
|
||||
// "alwaysStrict": true, /* Ensure 'use strict' is always emitted. */
|
||||
// "noUnusedLocals": true, /* Enable error reporting when local variables aren't read. */
|
||||
// "noUnusedParameters": true, /* Raise an error when a function parameter isn't read. */
|
||||
// "exactOptionalPropertyTypes": true, /* Interpret optional property types as written, rather than adding 'undefined'. */
|
||||
// "noImplicitReturns": true, /* Enable error reporting for codepaths that do not explicitly return in a function. */
|
||||
// "noFallthroughCasesInSwitch": true, /* Enable error reporting for fallthrough cases in switch statements. */
|
||||
// "noUncheckedIndexedAccess": true, /* Add 'undefined' to a type when accessed using an index. */
|
||||
// "noImplicitOverride": true, /* Ensure overriding members in derived classes are marked with an override modifier. */
|
||||
// "noPropertyAccessFromIndexSignature": true, /* Enforces using indexed accessors for keys declared using an indexed type. */
|
||||
// "allowUnusedLabels": true, /* Disable error reporting for unused labels. */
|
||||
// "allowUnreachableCode": true, /* Disable error reporting for unreachable code. */
|
||||
|
||||
/* Completeness */
|
||||
// "skipDefaultLibCheck": true, /* Skip type checking .d.ts files that are included with TypeScript. */
|
||||
"skipLibCheck": true /* Skip type checking all .d.ts files. */
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user