commit 6c29336660e9945f36d1d76f1c8f5babbbf2b778 Author: yizhi <946185759@qq.com> Date: Mon Mar 17 10:19:27 2025 +0800 初次提交 diff --git a/.clang-format b/.clang-format new file mode 100644 index 0000000..ecab9ed --- /dev/null +++ b/.clang-format @@ -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 \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..6b86c06 --- /dev/null +++ b/.gitignore @@ -0,0 +1,9 @@ +/.vscode +/build +/dist +/node_modules +/test_data +/thirdpart/_source +/thirdpart/OpenCV +/typing +/package-lock.json diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..12d96da --- /dev/null +++ b/CMakeLists.txt @@ -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() diff --git a/README.md b/README.md new file mode 100644 index 0000000..656f700 --- /dev/null +++ b/README.md @@ -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); +``` diff --git a/cxx/mat.cc b/cxx/mat.cc new file mode 100644 index 0000000..fd89313 --- /dev/null +++ b/cxx/mat.cc @@ -0,0 +1,450 @@ +#include "node.h" + +using namespace Napi; + +#define MAT_INSTANCE_METHOD(method) InstanceMethod<&CVMat::method>(#method, static_cast(napi_writable | napi_configurable)) +#define MAT_STATIC_METHOD(method) StaticMethod<&CVMat::method>(#method, static_cast(napi_writable | napi_configurable)) + +static FunctionReference *constructor = nullptr; + +class CVMat : public ObjectWrap { + 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(constructor); + return exports; + } + + CVMat(const CallbackInfo &info) + : ObjectWrap(info) + { + if (info.Length() >= 2) { + auto sizesArray = info[0].As(); + std::vector sizes(sizesArray.Length()); + for (auto i = 0; i < sizes.size(); ++i) sizes[i] = sizesArray.Get(i).As().Int32Value(); + auto type = info[1].As().Int32Value(); + // 使用sizes和type初始化 + if (info.Length() < 3) mat_ = cv::Mat(sizes, type); + // 使用数据初始化 + else if (info[2].IsTypedArray()) { + auto dataArray = info[2].As(); + auto data = static_cast(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().Utf8Value(); + int flag = (info.Length() >= 2) ? info[1].As().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(); + auto data = static_cast(arr.ArrayBuffer().Data()) + arr.ByteOffset(); + std::vector buffer(data, data + arr.ByteLength()); + int flag = (info.Length() >= 2) ? info[1].As().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()); + auto filename = info[1].As().Utf8Value(); + std::vector params; + if (info[2].IsArray()) { + auto paramArray = info[2].As(); + params.resize(paramArray.Length()); + for (auto i = 0; i < params.size(); ++i) params[i] = paramArray.Get(i).As().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()); + auto extname = info[1].As().Utf8Value(); + std::vector params; + if (info[2].IsArray()) { + auto paramArray = info[2].As(); + params.resize(paramArray.Length()); + for (auto i = 0; i < params.size(); ++i) params[i] = paramArray.Get(i).As().Int32Value(); + } + std::vector 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(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(mat_.elemSize())); } + Napi::Value GetElemSize1(const Napi::CallbackInfo &info) { return Number::New(info.Env(), static_cast(mat_.elemSize1())); } + Napi::Value GetTotal(const Napi::CallbackInfo &info) { return Number::New(info.Env(), static_cast(mat_.total())); } + Napi::Value GetTotalWithDim(const Napi::CallbackInfo &info) { return Number::New(info.Env(), static_cast(mat_.total(info[0].As().Int32Value(), info[1].As().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().Int32Value(); + return CreateMat(info.Env(), [&](CVMat &mat) { mat.mat_ = mat_.col(col); }); + } + Napi::Value ColRange(const Napi::CallbackInfo &info) + { + auto start = info[0].As().Int32Value(); + auto end = info[1].As().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().Int32Value(); + return CreateMat(info.Env(), [&](CVMat &mat) { mat.mat_ = mat_.row(row); }); + } + Napi::Value RowRange(const Napi::CallbackInfo &info) + { + auto start = info[0].As().Int32Value(); + auto end = info[1].As().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().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()); + auto &dst = GetMat(info[1].As()); + auto rangeArray = info[2].As(); + std::vector ranges(rangeArray.Length()); + for (int i = 0; i < ranges.size(); ++i) { + auto item = rangeArray.Get(i).As(); + auto start = item.Get("start").As().Int32Value(); + auto end = item.Get("end").As().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()); + auto &dst = GetMat(info[1].As()); + auto width = info[2].As().Int32Value(); + auto height = info[3].As().Int32Value(); + auto fx = info[4].As().DoubleValue(); + auto fy = info[5].As().DoubleValue(); + auto interpolation = info[6].As().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()); + auto &dst = GetMat(info[1].As()); + auto &transformMat = GetMat(info[2].As()); + auto dwidth = info[3].As().Int32Value(); + auto dheight = info[4].As().Int32Value(); + auto flags = info[5].As().Int32Value(); + auto borderMode = info[6].As().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()); + auto &dst = GetMat(info[1].As()); + auto kwidth = info[2].As().Int32Value(); + auto kheight = info[3].As().Int32Value(); + auto anchorX = info[4].As().Int32Value(); + auto anchorY = info[5].As().Int32Value(); + auto borderType = info[6].As().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()); + auto &dst = GetMat(info[1].As()); + auto top = info[2].As().Int32Value(); + auto bottom = info[3].As().Int32Value(); + auto left = info[4].As().Int32Value(); + auto right = info[5].As().Int32Value(); + auto borderType = info[6].As().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()); + auto &dst = GetMat(info[1].As()); + auto flipCode = info[2].As().Int32Value(); + cv::flip(src.mat_, dst.mat_, flipCode); + return info[1]; + } + static Napi::Value GetRotationMatrix2D(const Napi::CallbackInfo &info) + { + auto centerX = info[0].As().FloatValue(); + auto centerY = info[1].As().FloatValue(); + auto angle = info[2].As().DoubleValue(); + auto scale = info[3].As().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()); + auto x1 = info[1].As().Int32Value(); + auto y1 = info[2].As().Int32Value(); + auto x2 = info[3].As().Int32Value(); + auto y2 = info[4].As().Int32Value(); + auto b = info[5].As().DoubleValue(); + auto g = info[6].As().DoubleValue(); + auto r = info[7].As().DoubleValue(); + auto thickness = info[8].As().Int32Value(); + auto lineType = info[9].As().Int32Value(); + auto shift = info[10].As().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()); + auto x = info[1].As().Int32Value(); + auto y = info[2].As().Int32Value(); + auto radius = info[3].As().Int32Value(); + auto b = info[4].As().DoubleValue(); + auto g = info[5].As().DoubleValue(); + auto r = info[6].As().DoubleValue(); + auto thickness = info[7].As().Int32Value(); + auto lineType = info[8].As().Int32Value(); + auto shift = info[9].As().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()); + auto x1 = info[1].As().Int32Value(); + auto y1 = info[2].As().Int32Value(); + auto x2 = info[3].As().Int32Value(); + auto y2 = info[4].As().Int32Value(); + auto b = info[5].As().DoubleValue(); + auto g = info[6].As().DoubleValue(); + auto r = info[7].As().DoubleValue(); + auto thickness = info[8].As().Int32Value(); + auto lineType = info[9].As().Int32Value(); + auto shift = info[10].As().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()); + auto x = info[1].As().Int32Value(); + auto y = info[2].As().Int32Value(); + auto width = info[3].As().Int32Value(); + auto height = info[4].As().Int32Value(); + auto angle = info[5].As().DoubleValue(); + auto startAngle = info[6].As().DoubleValue(); + auto endAngle = info[7].As().DoubleValue(); + auto b = info[8].As().DoubleValue(); + auto g = info[9].As().DoubleValue(); + auto r = info[10].As().DoubleValue(); + auto thickness = info[11].As().Int32Value(); + auto lineType = info[12].As().Int32Value(); + auto shift = info[13].As().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()); + auto points_ = info[1].As(); + std::vector points(points_.Length()); + for (uint32_t i = 0; i < points_.Length(); i++) { + auto pt = points_.Get(i).As(); + points[i] = cv::Point(pt.Get(0U).As().Int32Value(), pt.Get(1U).As().Int32Value()); + } + auto isClosed = info[2].As().Value(); + auto b = info[3].As().DoubleValue(); + auto g = info[4].As().DoubleValue(); + auto r = info[5].As().DoubleValue(); + auto thickness = info[6].As().Int32Value(); + auto lineType = info[7].As().Int32Value(); + auto shift = info[8].As().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()); + auto points_ = info[1].As(); + std::vector points(points_.Length()); + for (uint32_t i = 0; i < points_.Length(); i++) { + auto pt = points_.Get(i).As(); + points[i] = cv::Point(pt.Get(0U).As().Int32Value(), pt.Get(1U).As().Int32Value()); + } + auto b = info[2].As().DoubleValue(); + auto g = info[3].As().DoubleValue(); + auto r = info[4].As().DoubleValue(); + auto lineType = info[5].As().Int32Value(); + auto shift = info[6].As().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()); + auto points_ = info[1].As(); + std::vector points(points_.Length()); + for (uint32_t i = 0; i < points_.Length(); i++) { + auto pt = points_.Get(i).As(); + points[i] = cv::Point(pt.Get(0U).As().Int32Value(), pt.Get(1U).As().Int32Value()); + } + auto b = info[2].As().DoubleValue(); + auto g = info[3].As().DoubleValue(); + auto r = info[4].As().DoubleValue(); + auto lineType = info[5].As().Int32Value(); + auto shift = info[6].As().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()); + auto x = info[1].As().Int32Value(); + auto y = info[2].As().Int32Value(); + auto b = info[3].As().DoubleValue(); + auto g = info[4].As().DoubleValue(); + auto r = info[5].As().DoubleValue(); + auto markerType = info[6].As().Int32Value(); + auto markerSize = info[7].As().Int32Value(); + auto lineType = info[8].As().Int32Value(); + auto shift = info[9].As().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()); + auto text = info[1].As().Utf8Value(); + auto x = info[2].As().Int32Value(); + auto y = info[3].As().Int32Value(); + auto fontFace = info[4].As().Int32Value(); + auto fontScale = info[5].As().DoubleValue(); + auto b = info[6].As().DoubleValue(); + auto g = info[7].As().DoubleValue(); + auto r = info[8].As().DoubleValue(); + auto thickness = info[9].As().Int32Value(); + auto lineType = info[10].As().Int32Value(); + auto shift = info[11].As().Int32Value(); + auto bottomLeftOrigin = info[12].As().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(); } + inline static CVMat &GetMat(Napi::Object obj) { return *ObjectWrap::Unwrap(obj); } + inline static Napi::Object CreateMat(Napi::Env env, std::function 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); +} \ No newline at end of file diff --git a/cxx/node.cc b/cxx/node.cc new file mode 100644 index 0000000..c3087eb --- /dev/null +++ b/cxx/node.cc @@ -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); \ No newline at end of file diff --git a/cxx/node.h b/cxx/node.h new file mode 100644 index 0000000..1913d0f --- /dev/null +++ b/cxx/node.h @@ -0,0 +1,10 @@ +#ifndef __CV_NODE_H__ +#define __CV_NODE_H__ + +#include +#include + +void InitMatAPI(Napi::Env env, Napi::Object exports); +void InitProcAPI(Napi::Env env, Napi::Object exports); + +#endif diff --git a/cxx/util.cc b/cxx/util.cc new file mode 100644 index 0000000..913333f --- /dev/null +++ b/cxx/util.cc @@ -0,0 +1,35 @@ +#include "node.h" +using namespace Napi; + +#define ADD_PROC_FUNCTION(name) obj.Set(#name, Napi::Function::New(env)); +#define DEFINE_FUNCTION(name) Napi::Value name(Napi::CallbackInfo const &info) + +DEFINE_FUNCTION(GetTextSize) +{ + auto text = info[0].As().Utf8Value(); + auto fontFace = info[1].As().Int32Value(); + auto fontScale = info[2].As().DoubleValue(); + auto thickness = info[3].As().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().Int32Value(); + auto pixelHeight = info[1].As().Int32Value(); + auto thickness = info[2].As().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); +} \ No newline at end of file diff --git a/package.json b/package.json new file mode 100644 index 0000000..59a9ca5 --- /dev/null +++ b/package.json @@ -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" + } +} \ No newline at end of file diff --git a/src/cv/addon.ts b/src/cv/addon.ts new file mode 100644 index 0000000..178e2df --- /dev/null +++ b/src/cv/addon.ts @@ -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(name: N): typeof config[N] { return config[name]; } +export function setConfig(name: N, value: typeof config[N]) { config[name] = value; } + +export function CVMat() { return getAddon().Mat; } + +export function CVUtil() { return getAddon().util; } diff --git a/src/cv/common.ts b/src/cv/common.ts new file mode 100644 index 0000000..44caade --- /dev/null +++ b/src/cv/common.ts @@ -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[0]) { return getConfig(name); } + +export function resolveArgs>(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; +} diff --git a/src/cv/consts.ts b/src/cv/consts.ts new file mode 100644 index 0000000..3b98369 --- /dev/null +++ b/src/cv/consts.ts @@ -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; diff --git a/src/cv/draw.ts b/src/cv/draw.ts new file mode 100644 index 0000000..fdaa35c --- /dev/null +++ b/src/cv/draw.ts @@ -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(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")); +} diff --git a/src/cv/index.ts b/src/cv/index.ts new file mode 100644 index 0000000..102451b --- /dev/null +++ b/src/cv/index.ts @@ -0,0 +1,5 @@ +export { setConfig as config } from "./addon"; +export * from "./mat"; +export * from "./consts"; +export * from "./proc"; +export * from "./draw"; diff --git a/src/cv/mat.ts b/src/cv/mat.ts new file mode 100644 index 0000000..00277b2 --- /dev/null +++ b/src/cv/mat.ts @@ -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); +} diff --git a/src/cv/proc.ts b/src/cv/proc.ts new file mode 100644 index 0000000..5265700 --- /dev/null +++ b/src/cv/proc.ts @@ -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)); } diff --git a/src/index.ts b/src/index.ts new file mode 100644 index 0000000..4a58679 --- /dev/null +++ b/src/index.ts @@ -0,0 +1,3 @@ +import * as cv from "./cv"; +export default cv; +export { cv }; \ No newline at end of file diff --git a/src/test.ts b/src/test.ts new file mode 100644 index 0000000..814932d --- /dev/null +++ b/src/test.ts @@ -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(); \ No newline at end of file diff --git a/test_data/im1.jpeg b/test_data/im1.jpeg new file mode 100644 index 0000000..be2ceb9 Binary files /dev/null and b/test_data/im1.jpeg differ diff --git a/thirdpart/cmake-js-util.js b/thirdpart/cmake-js-util.js new file mode 100644 index 0000000..2189177 --- /dev/null +++ b/thirdpart/cmake-js-util.js @@ -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(/"$/, "")); \ No newline at end of file diff --git a/thirdpart/install.js b/thirdpart/install.js new file mode 100644 index 0000000..89569c3 --- /dev/null +++ b/thirdpart/install.js @@ -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(); \ No newline at end of file diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..838b119 --- /dev/null +++ b/tsconfig.json @@ -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 ''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. */ + } +}