From 398400e397f6f748ed77959964b82a7e4f0c3cd8 Mon Sep 17 00:00:00 2001 From: Stefan-Gabriel Muscalu Date: Wed, 23 Oct 2019 15:18:17 +0300 Subject: [PATCH 1/4] refactor calcHist and add calcHistAsync --- cc/imgproc/imgproc.cc | 109 +++----------------------- cc/imgproc/imgproc.h | 3 +- cc/imgproc/imgprocBindings.h | 121 +++++++++++++++++++++++++++++ lib/typings/cv.d.ts | 1 + test/tests/imgproc/imgprocTests.js | 4 + 5 files changed, 138 insertions(+), 100 deletions(-) diff --git a/cc/imgproc/imgproc.cc b/cc/imgproc/imgproc.cc index fddf6b666..a2c55f7dd 100644 --- a/cc/imgproc/imgproc.cc +++ b/cc/imgproc/imgproc.cc @@ -6,29 +6,10 @@ #include "imgprocBindings.h" #include "imgprocConstants.h" -#define FF_DEFINE_CALC_HIST(name, n, constRangesArray) \ - cv::MatND name(cv::Mat img, cv::Mat mask, int channels[], int histSize[], std::vector rangesVec) { \ - const float* ranges[] = constRangesArray; \ - cv::MatND hist; \ - cv::calcHist(&img, 1, channels, mask, hist, n, histSize, ranges, true, false); \ - return hist; \ - } - -#define FF_HIST_RANGE_1 { rangesVec.at(0) } -#define FF_HIST_RANGE_2 { rangesVec.at(0), rangesVec.at(1) } -#define FF_HIST_RANGE_3 { rangesVec.at(0), rangesVec.at(1), rangesVec.at(2) } -#define FF_HIST_RANGE_4 { rangesVec.at(0), rangesVec.at(1), rangesVec.at(2), rangesVec.at(3) } - -FF_DEFINE_CALC_HIST(calcHist1, 1, FF_HIST_RANGE_1); -FF_DEFINE_CALC_HIST(calcHist2, 2, FF_HIST_RANGE_2); -FF_DEFINE_CALC_HIST(calcHist3, 3, FF_HIST_RANGE_3); -FF_DEFINE_CALC_HIST(calcHist4, 4, FF_HIST_RANGE_4); - NAN_MODULE_INIT(Imgproc::Init) { ImgprocConstants::Init(target); Nan::SetMethod(target, "getStructuringElement", GetStructuringElement); Nan::SetMethod(target, "getRotationMatrix2D", GetRotationMatrix2D); - Nan::SetMethod(target, "calcHist", CalcHist); Nan::SetMethod(target, "plot1DHist", Plot1DHist); Nan::SetMethod(target, "fitLine", FitLine); Nan::SetMethod(target, "getAffineTransform", GetAffineTransform); @@ -61,7 +42,8 @@ NAN_MODULE_INIT(Imgproc::Init) { Nan::SetMethod(target, "accumulateSquareAsync", AccumulateSquareAsync); Nan::SetMethod(target, "accumulateWeighted", AccumulateWeighted); Nan::SetMethod(target, "accumulateWeightedAsync", AccumulateWeightedAsync); - + Nan::SetMethod(target, "calcHist", CalcHist); + Nan::SetMethod(target, "calcHistAsync", CalcHistAsync); Moments::Init(target); Contour::Init(target); @@ -128,85 +110,6 @@ NAN_METHOD(Imgproc::GetPerspectiveTransform) { info.GetReturnValue().Set(Mat::Converter::wrap(cv::getPerspectiveTransform(srcPoints, dstPoints))); } -NAN_METHOD(Imgproc::CalcHist) { - FF::TryCatch tryCatch("Imgproc::CalcHist"); - cv::Mat img, mask = cv::noArray().getMat(); - std::vector> _ranges; - if ( - Mat::Converter::arg(0, &img, info) || - Mat::Converter::optArg(2, &mask, info) - ) { - return tryCatch.reThrow(); - } - if (!info[1]->IsArray()) { - return tryCatch.throwError("expected arg 1 to be an array"); - } - v8::Local jsHistAxes = v8::Local::Cast(info[1]); - - cv::Mat inputImg = img; - int inputType = CV_MAKETYPE(CV_32F, img.channels()); - if (inputType != img.type()) { - img.convertTo(inputImg, inputType); - } - - int dims = jsHistAxes->Length(); - int* channels = new int[dims]; - int* histSize = new int[dims]; - std::vector ranges; - // TODO replace old macros - for (int i = 0; i < dims; ++i) { - ranges.push_back(new float[dims]); - v8::Local jsAxis = Nan::To((Nan::Get(jsHistAxes, i).ToLocalChecked())).ToLocalChecked(); - if (!FF::hasOwnProperty(jsAxis, "ranges")) { - return tryCatch.throwError("expected axis object to have ranges property"); - } - v8::Local jsRangesVal = Nan::Get(jsAxis, Nan::New("ranges").ToLocalChecked()).ToLocalChecked(); - if (!jsRangesVal->IsArray()) { - return tryCatch.throwError("expected ranges to be an array"); - } - v8::Local jsRanges = v8::Local::Cast(jsRangesVal); - if (jsRanges->Length() != 2) { - return tryCatch.throwError("expected ranges to be an array of length 2"); - } - ranges.at(i)[0] = FF::DoubleConverter::unwrapUnchecked(Nan::Get(jsRanges, 0).ToLocalChecked()); - ranges.at(i)[1] = FF::DoubleConverter::unwrapUnchecked(Nan::Get(jsRanges, 1).ToLocalChecked()); - int channel, bins; - - if (FF::IntConverter::prop(&channel, "channel", jsAxis) || FF::IntConverter::prop(&bins, "bins", jsAxis)) { - return tryCatch.reThrow(); - } - channels[i] = channel; - histSize[i] = bins; - } - - cv::MatND hist; - if (dims == 1) { - hist = calcHist1(inputImg, mask, channels, histSize, ranges); - } - else if (dims == 2) { - hist = calcHist2(inputImg, mask, channels, histSize, ranges); - } - else if (dims == 3) { - hist = calcHist3(inputImg, mask, channels, histSize, ranges); - } - else if (dims == 4) { - hist = calcHist4(inputImg, mask, channels, histSize, ranges); - } - - for (int i = 0; i < dims; ++i) { - delete[] ranges.at(i); - } - delete[] channels; - delete[] histSize; - - int outputType = CV_MAKETYPE(CV_64F, img.channels()); - if (outputType != hist.type()) { - hist.convertTo(hist, outputType); - } - - info.GetReturnValue().Set(Mat::Converter::wrap(hist)); -} - NAN_METHOD(Imgproc::Plot1DHist) { FF::TryCatch tryCatch("Imgproc::Plot1DHist"); @@ -431,4 +334,12 @@ NAN_METHOD(Imgproc::AccumulateWeightedAsync) { FF::asyncBinding("Imgproc", "AccumulateWeighted", info); } +NAN_METHOD(Imgproc::CalcHist) { + FF::syncBinding("Imgproc", "CalcHist", info); +} + +NAN_METHOD(Imgproc::CalcHistAsync) { + FF::asyncBinding("Imgproc", "CalcHist", info); +} + #endif \ No newline at end of file diff --git a/cc/imgproc/imgproc.h b/cc/imgproc/imgproc.h index ba79c53a8..846c35fc0 100644 --- a/cc/imgproc/imgproc.h +++ b/cc/imgproc/imgproc.h @@ -18,7 +18,6 @@ class Imgproc { static NAN_METHOD(GetRotationMatrix2D); static NAN_METHOD(GetAffineTransform); static NAN_METHOD(GetPerspectiveTransform); - static NAN_METHOD(CalcHist); static NAN_METHOD(Plot1DHist); static NAN_METHOD(FitLine); static NAN_METHOD(GetTextSize); @@ -49,6 +48,8 @@ class Imgproc { static NAN_METHOD(AccumulateSquareAsync); static NAN_METHOD(AccumulateWeighted); static NAN_METHOD(AccumulateWeightedAsync); + static NAN_METHOD(CalcHist); + static NAN_METHOD(CalcHistAsync); }; #endif diff --git a/cc/imgproc/imgprocBindings.h b/cc/imgproc/imgprocBindings.h index 5d761c7f5..fc47d07ac 100644 --- a/cc/imgproc/imgprocBindings.h +++ b/cc/imgproc/imgprocBindings.h @@ -269,6 +269,127 @@ namespace ImgprocBindings { }; }; }; + + typedef struct HistAxes { + float range[2]; + int channel; + int bins; + } HistAxes; + + class HistAxesConverterImpl : public FF::UnwrapperBase { + public: + + typedef HistAxes Type; + + static std::string getTypeName() { + return std::string("HistAxes"); + } + + static bool assertType(v8::Local jsVal) { + if (!jsVal->IsObject()) return false; + + auto jsObj = Nan::To(jsVal).ToLocalChecked(); + + if (!FF::hasOwnProperty(jsObj, "ranges")) return false; + + auto jsRangesVal = Nan::Get(jsObj, Nan::New("ranges").ToLocalChecked()).ToLocalChecked(); + if (!jsRangesVal->IsArray()) return false; + auto jsRanges = v8::Local::Cast(jsRangesVal); + if (jsRanges->Length() != 2) return false; + + return ( + Nan::Get(jsObj, Nan::New("channel").ToLocalChecked()).ToLocalChecked()->IsNumber() || + Nan::Get(jsObj, Nan::New("bins").ToLocalChecked()).ToLocalChecked()->IsNumber() + ); + } + + static HistAxes unwrapUnchecked(v8::Local jsVal) { + FF::TryCatch tryCatch("Imgproc::CalcHist"); + HistAxes ret; + auto jsAxis = Nan::To(jsVal).ToLocalChecked(); + + v8::Local jsRangesVal = Nan::Get(jsAxis, Nan::New("ranges").ToLocalChecked()).ToLocalChecked(); + v8::Local jsRanges = v8::Local::Cast(jsRangesVal); + + ret.range[0] = FF::DoubleConverter::unwrapUnchecked(Nan::Get(jsRanges, 0).ToLocalChecked()); + ret.range[1] = FF::DoubleConverter::unwrapUnchecked(Nan::Get(jsRanges, 1).ToLocalChecked()); + FF::IntConverter::prop(&ret.channel, "channel", jsAxis); + FF::IntConverter::prop(&ret.bins, "bins", jsAxis); + return ret; + } + + static v8::Local wrap(HistAxes val) { + v8::Local ret = Nan::New(); + Nan::Set(ret, FF::newString("bins"), FF::IntConverter::wrap(val.bins)); + Nan::Set(ret, FF::newString("channel"), FF::IntConverter::wrap(val.channel)); + Nan::Set(ret, FF::newString("ranges"), FF::FloatArrayConverter::wrap({val.range[0], val.range[1]})); + + return ret; + } + }; + typedef FF::ArrayConverterTemplate ArrayHistAxesConverter; + + class CalcHist : public CvBinding { + private: + cv::MatND hist; + public: + v8::Local getReturnValue(){ + return Mat::Converter::wrap(hist); + } + + void setup() { + + auto src = req(); + auto jsHistAxes = req(); + auto mask = opt("mask", cv::noArray().getMat()); + + executeBinding = [=]() { + auto histAxes = jsHistAxes->ref(); + + const int dims = histAxes.size(); + + auto **ranges = new float*[dims]; + int *channels = new int[dims]; + int *bins = new int[dims]; + + for (int i = 0; i < dims; i++) { + auto entry = histAxes.at(i); + ranges[i] = new float[2]; + ranges[i][0] = entry.range[0]; + ranges[i][1] = entry.range[1]; + channels[i] = entry.channel; + bins[i] = entry.bins; + } + + auto img = src->ref(); + + cv::calcHist( + &img, + 1, + channels, + mask->ref(), + hist, + dims, + bins, + (const float **)(ranges), + true, + false + ); + + for (int i = 0; i < dims; ++i) { + delete[] ranges[i]; + } + delete[] ranges; + delete[] channels; + delete[] bins; + + int outputType = CV_MAKETYPE(CV_64F, img.channels()); + if (outputType != hist.type()) { + hist.convertTo(hist, outputType); + } + }; + } + }; } #endif \ No newline at end of file diff --git a/lib/typings/cv.d.ts b/lib/typings/cv.d.ts index a308eb0cd..8dd5fc927 100644 --- a/lib/typings/cv.d.ts +++ b/lib/typings/cv.d.ts @@ -38,6 +38,7 @@ export function blur(mat: Mat, kSize: Size, anchor?: Point2, borderType?: number export function blurAsync(mat: Mat, kSize: Size, anchor?: Point2, borderType?: number): Promise; export function NMSBoxes(bboxes: Rect[], scores: number[], scoreThreshold: number, nmsThreshold: number): number[]; export function calcHist(img: Mat, histAxes: HistAxes[], mask?: Mat): Mat; +export function calcHistAsync(img: Mat, histAxes: HistAxes[], mask?: Mat): Promise; export function calibrateCamera(objectPoints: Point3[], imagePoints: Point2[], imageSize: Size, cameraMatrix: Mat, distCoeffs: number[], flags?: number, criteria?: TermCriteria): { returnValue: number, rvecs: Vec3[], tvecs: Vec3[], distCoeffs: number[] }; export function calibrateCameraAsync(objectPoints: Point3[], imagePoints: Point2[], imageSize: Size, cameraMatrix: Mat, distCoeffs: number[], flags?: number, criteria?: TermCriteria): Promise<{ returnValue: number, rvecs: Vec3[], tvecs: Vec3[], distCoeffs: number[] }>; export function calibrateCameraExtended(objectPoints: Point3[], imagePoints: Point2[], imageSize: Size, cameraMatrix: Mat, distCoeffs: number[], flags?: number, criteria?: TermCriteria): { returnValue: number, rvecs: Vec3[], tvecs: Vec3[], distCoeffs: number[], stdDeviationsIntrinsics: Mat, stdDeviationsExtrinsics: Mat, perViewErrors: number[] }; diff --git a/test/tests/imgproc/imgprocTests.js b/test/tests/imgproc/imgprocTests.js index a8e03e28c..bf856baa7 100644 --- a/test/tests/imgproc/imgprocTests.js +++ b/test/tests/imgproc/imgprocTests.js @@ -145,6 +145,10 @@ module.exports = ({ cv, utils, getTestImg }) => { expect(() => cv.calcHist()).to.throw('Imgproc::CalcHist - Error: expected argument 0 to be of type'); }); + it('should throw if no HistAxes arg', () => { + expect(() => cv.calcHist(getTestImg())).to.throw('Imgproc::CalcHist - Error: expected argument 1 to be of type array of HistAxes'); + }); + it('should return 1 dimensional hist', () => { const histAxes = [ { From 3112f00e8602f170531557e8146442cd17f53dda Mon Sep 17 00:00:00 2001 From: Stefan-Gabriel Muscalu Date: Wed, 23 Oct 2019 17:18:24 +0300 Subject: [PATCH 2/4] update calcHist binding style to be in line with others --- cc/imgproc/imgprocBindings.h | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/cc/imgproc/imgprocBindings.h b/cc/imgproc/imgprocBindings.h index fc47d07ac..9b8d60a1c 100644 --- a/cc/imgproc/imgprocBindings.h +++ b/cc/imgproc/imgprocBindings.h @@ -330,21 +330,18 @@ namespace ImgprocBindings { typedef FF::ArrayConverterTemplate ArrayHistAxesConverter; class CalcHist : public CvBinding { - private: - cv::MatND hist; public: - v8::Local getReturnValue(){ - return Mat::Converter::wrap(hist); - } - void setup() { auto src = req(); auto jsHistAxes = req(); auto mask = opt("mask", cv::noArray().getMat()); + auto retHist = ret("hist"); executeBinding = [=]() { auto histAxes = jsHistAxes->ref(); + auto img = src->ref(); + cv::MatND hist; const int dims = histAxes.size(); @@ -361,8 +358,6 @@ namespace ImgprocBindings { bins[i] = entry.bins; } - auto img = src->ref(); - cv::calcHist( &img, 1, @@ -387,6 +382,8 @@ namespace ImgprocBindings { if (outputType != hist.type()) { hist.convertTo(hist, outputType); } + + retHist->ref() = hist; }; } }; From 2b6716409b02f817eb4e8d9c31033af1fcbc0634 Mon Sep 17 00:00:00 2001 From: Stefan-Gabriel Muscalu Date: Mon, 4 Nov 2019 17:43:49 +0200 Subject: [PATCH 3/4] update!: calcHist arg1 changes & tests Breaking Change: calcHist now takes an array of type cv.HistAxis instead of a plain object --- binding.gyp | 1 + cc/core/HistAxes.cc | 64 ++++++++++++++++++++++++++++++ cc/core/HistAxes.h | 36 +++++++++++++++++ cc/core/core.cc | 1 + cc/core/core.h | 1 + cc/imgproc/imgproc.h | 1 + cc/imgproc/imgprocBindings.h | 63 +---------------------------- lib/typings/cv.d.ts | 4 +- test/tests/imgproc/imgprocTests.js | 45 +++++++++++++++++++-- 9 files changed, 151 insertions(+), 65 deletions(-) create mode 100644 cc/core/HistAxes.cc create mode 100644 cc/core/HistAxes.h diff --git a/binding.gyp b/binding.gyp index a19da5993..2eb83b574 100644 --- a/binding.gyp +++ b/binding.gyp @@ -20,6 +20,7 @@ "cc/ExternalMemTracking.cc", "cc/core/core.cc", "cc/core/coreConstants.cc", + "cc/core/HistAxes.cc", "cc/core/Mat.cc", "cc/core/Point.cc", "cc/core/Vec.cc", diff --git a/cc/core/HistAxes.cc b/cc/core/HistAxes.cc new file mode 100644 index 000000000..49e3463ba --- /dev/null +++ b/cc/core/HistAxes.cc @@ -0,0 +1,64 @@ +// +// Created by stefan on 11/4/19. +// + +#include "HistAxes.h" + +Nan::Persistent HistAxes::constructor; + +NAN_MODULE_INIT(HistAxes::Init) { + v8::Local ctor = Nan::New(HistAxes::New); + HistAxes::constructor.Reset(ctor); + ctor->InstanceTemplate()->SetInternalFieldCount(1); + ctor->SetClassName(Nan::New("HistAxes").ToLocalChecked()); + + Nan::SetAccessor(ctor->InstanceTemplate(), Nan::New("bins").ToLocalChecked(), HistAxes::bins_getter); + Nan::SetAccessor(ctor->InstanceTemplate(), Nan::New("channel").ToLocalChecked(), HistAxes::channel_getter); + Nan::SetAccessor(ctor->InstanceTemplate(), Nan::New("ranges").ToLocalChecked(), HistAxes::ranges_getter); + + Nan::Set(target, Nan::New("HistAxes").ToLocalChecked(), FF::getFunction(ctor)); +} + +NAN_METHOD(HistAxes::New) { + FF::TryCatch tryCatch("HistAxes::New"); + FF_ASSERT_CONSTRUCT_CALL(); + if (info.Length() != 1) { + return tryCatch.throwError("expected one argument"); + } + if (!info[0]->IsObject()) { + return tryCatch.throwError("expected arg0 to be an object"); + } + + HistAxes *self = new HistAxes(); + + auto jsAxis = Nan::To(info[0]).ToLocalChecked(); + + if (!FF::hasOwnProperty(jsAxis, "ranges")) { + return tryCatch.throwError("expected object to have ranges"); + } + if (!FF::hasOwnProperty(jsAxis, "bins")) { + return tryCatch.throwError("expected object to have bins"); + } + if (!FF::hasOwnProperty(jsAxis, "channel")) { + return tryCatch.throwError("expected object to have channel"); + } + + v8::Local jsRangesVal = Nan::Get(jsAxis, Nan::New("ranges").ToLocalChecked()).ToLocalChecked(); + v8::Local jsRanges = v8::Local::Cast(jsRangesVal); + + if ( + jsRanges->Length() != 2 || + !Nan::Get(jsRanges, 0).ToLocalChecked()->IsNumber() || + !Nan::Get(jsRanges, 1).ToLocalChecked()->IsNumber() + ) { + return tryCatch.throwError("expected ranges to be an array with 2 numbers"); + } + + FF::DoubleArrayConverter::unwrapTo(&self->self.range, jsRanges); + + FF::IntConverter::prop(&self->self.channel, "channel", jsAxis); + FF::IntConverter::prop(&self->self.bins, "bins", jsAxis); + + self->Wrap(info.Holder()); + info.GetReturnValue().Set(info.Holder()); +} \ No newline at end of file diff --git a/cc/core/HistAxes.h b/cc/core/HistAxes.h new file mode 100644 index 000000000..189bcf1f5 --- /dev/null +++ b/cc/core/HistAxes.h @@ -0,0 +1,36 @@ +// +// Created by stefan on 11/4/19. +// +#include "macros.h" + +#ifndef __FF_HISTAXES_H__ +#define __FF_HISTAXES_H__ + +namespace internal { + class HistAxes { + public: + std::vector range = {}; + int channel = 0; + int bins = 0; + }; +} + +class HistAxes: public FF::ObjectWrap { +public: + static Nan::Persistent constructor; + + static const char* getClassName() { + return "HistAxes"; + } + + static NAN_METHOD(New); + + static NAN_MODULE_INIT(Init); + + FF_GETTER_CUSTOM(bins, FF::IntConverter, self.bins); + FF_GETTER_CUSTOM(channel, FF::IntConverter, self.channel); + FF_GETTER_CUSTOM(ranges, FF::DoubleArrayConverter, self.range); + +}; + +#endif //__FF_HISTAXES_H__ diff --git a/cc/core/core.cc b/cc/core/core.cc index 17798bd0f..3728b2b73 100644 --- a/cc/core/core.cc +++ b/cc/core/core.cc @@ -13,6 +13,7 @@ NAN_MODULE_INIT(Core::Init) { Rect::Init(target); RotatedRect::Init(target); TermCriteria::Init(target); + HistAxes::Init(target); Nan::SetMethod(target, "getBuildInformation", GetBuildInformation); Nan::SetMethod(target, "partition", Partition); diff --git a/cc/core/core.h b/cc/core/core.h index 26343e048..ee6cb1a0c 100644 --- a/cc/core/core.h +++ b/cc/core/core.h @@ -5,6 +5,7 @@ #include "Rect.h" #include "RotatedRect.h" #include "TermCriteria.h" +#include "HistAxes.h" #include "macros.h" #include diff --git a/cc/imgproc/imgproc.h b/cc/imgproc/imgproc.h index 846c35fc0..6905dc241 100644 --- a/cc/imgproc/imgproc.h +++ b/cc/imgproc/imgproc.h @@ -6,6 +6,7 @@ #include "Mat.h" #include "Contour.h" #include "Moments.h" +#include "HistAxes.h" #ifndef __FF_IMGPROC_H__ #define __FF_IMGPROC_H__ diff --git a/cc/imgproc/imgprocBindings.h b/cc/imgproc/imgprocBindings.h index 9b8d60a1c..5f5aa267d 100644 --- a/cc/imgproc/imgprocBindings.h +++ b/cc/imgproc/imgprocBindings.h @@ -206,7 +206,7 @@ namespace ImgprocBindings { }; class Accumulate : public CvBinding { - public: + public: void setup() { auto src = req(); auto dst = req(); @@ -270,71 +270,12 @@ namespace ImgprocBindings { }; }; - typedef struct HistAxes { - float range[2]; - int channel; - int bins; - } HistAxes; - - class HistAxesConverterImpl : public FF::UnwrapperBase { - public: - - typedef HistAxes Type; - - static std::string getTypeName() { - return std::string("HistAxes"); - } - - static bool assertType(v8::Local jsVal) { - if (!jsVal->IsObject()) return false; - - auto jsObj = Nan::To(jsVal).ToLocalChecked(); - - if (!FF::hasOwnProperty(jsObj, "ranges")) return false; - - auto jsRangesVal = Nan::Get(jsObj, Nan::New("ranges").ToLocalChecked()).ToLocalChecked(); - if (!jsRangesVal->IsArray()) return false; - auto jsRanges = v8::Local::Cast(jsRangesVal); - if (jsRanges->Length() != 2) return false; - - return ( - Nan::Get(jsObj, Nan::New("channel").ToLocalChecked()).ToLocalChecked()->IsNumber() || - Nan::Get(jsObj, Nan::New("bins").ToLocalChecked()).ToLocalChecked()->IsNumber() - ); - } - - static HistAxes unwrapUnchecked(v8::Local jsVal) { - FF::TryCatch tryCatch("Imgproc::CalcHist"); - HistAxes ret; - auto jsAxis = Nan::To(jsVal).ToLocalChecked(); - - v8::Local jsRangesVal = Nan::Get(jsAxis, Nan::New("ranges").ToLocalChecked()).ToLocalChecked(); - v8::Local jsRanges = v8::Local::Cast(jsRangesVal); - - ret.range[0] = FF::DoubleConverter::unwrapUnchecked(Nan::Get(jsRanges, 0).ToLocalChecked()); - ret.range[1] = FF::DoubleConverter::unwrapUnchecked(Nan::Get(jsRanges, 1).ToLocalChecked()); - FF::IntConverter::prop(&ret.channel, "channel", jsAxis); - FF::IntConverter::prop(&ret.bins, "bins", jsAxis); - return ret; - } - - static v8::Local wrap(HistAxes val) { - v8::Local ret = Nan::New(); - Nan::Set(ret, FF::newString("bins"), FF::IntConverter::wrap(val.bins)); - Nan::Set(ret, FF::newString("channel"), FF::IntConverter::wrap(val.channel)); - Nan::Set(ret, FF::newString("ranges"), FF::FloatArrayConverter::wrap({val.range[0], val.range[1]})); - - return ret; - } - }; - typedef FF::ArrayConverterTemplate ArrayHistAxesConverter; - class CalcHist : public CvBinding { public: void setup() { auto src = req(); - auto jsHistAxes = req(); + auto jsHistAxes = req(); auto mask = opt("mask", cv::noArray().getMat()); auto retHist = ret("hist"); diff --git a/lib/typings/cv.d.ts b/lib/typings/cv.d.ts index 8dd5fc927..5f1a2e442 100644 --- a/lib/typings/cv.d.ts +++ b/lib/typings/cv.d.ts @@ -13,10 +13,12 @@ import { TermCriteria } from './TermCriteria.d'; import { OCRHMMClassifier } from './OCRHMMClassifier.d'; import { Net } from './Net.d'; -export interface HistAxes { +export class HistAxes { channel: number; bins: number; ranges: number[]; + + constructor(opts: { channel: number, bins: number, ranges: [number, number] }); } export function accumulate(src: Mat, dst: Mat, mask?: Mat): void; diff --git a/test/tests/imgproc/imgprocTests.js b/test/tests/imgproc/imgprocTests.js index bf856baa7..a3311b8a2 100644 --- a/test/tests/imgproc/imgprocTests.js +++ b/test/tests/imgproc/imgprocTests.js @@ -140,6 +140,45 @@ module.exports = ({ cv, utils, getTestImg }) => { }); }); + describe('HistAxes', () => { + it('should throw if no args', () => { + expect(() => new cv.HistAxes()).to.throw('HistAxes::New - expected one argument'); + }); + it('should throw if incomplete args', () => { + expect(() => new cv.HistAxes({})).to.throw('HistAxes::New - expected object to have ranges'); + expect(() => new cv.HistAxes({ranges: []})).to.throw('HistAxes::New - expected object to have bins'); + expect(() => new cv.HistAxes({ranges: [], bins: 0})).to.throw('HistAxes::New - expected object to have channel'); + expect(() => new cv.HistAxes({ + ranges: [], + bins: 0, + channel: 0 + })).to.throw('HistAxes::New - expected ranges to be an array with 2 numbers'); + expect(() => new cv.HistAxes({ + ranges: [1], + bins: 0, + channel: 0 + })).to.throw('HistAxes::New - expected ranges to be an array with 2 numbers'); + expect(() => new cv.HistAxes({ + ranges: [1,2,3], + bins: 0, + channel: 0 + })).to.throw('HistAxes::New - expected ranges to be an array with 2 numbers'); + expect(() => new cv.HistAxes({ + ranges: [1,"2"], + bins: 0, + channel: 0 + })).to.throw('HistAxes::New - expected ranges to be an array with 2 numbers'); + }); + it('should return HistAxes', () => { + const h = new cv.HistAxes({ + channel: 0, + bins: 8, + ranges: [0, 256] + }); + assertPropsWithValue(h)({channel: 0, bins: 8, ranges: [0, 256]}); + }); + }); + describe('calcHist', () => { it('should throw if no args', () => { expect(() => cv.calcHist()).to.throw('Imgproc::CalcHist - Error: expected argument 0 to be of type'); @@ -156,7 +195,7 @@ module.exports = ({ cv, utils, getTestImg }) => { bins: 8, ranges: [0, 256] } - ]; + ].map(x => new cv.HistAxes(x)); const hist1D = cv.calcHist(getTestImg(), histAxes); assertPropsWithValue(hist1D)({ rows: 8, cols: 1, dims: 2 }); }); @@ -173,7 +212,7 @@ module.exports = ({ cv, utils, getTestImg }) => { bins: 32, ranges: [0, 256] } - ]; + ].map(x => new cv.HistAxes(x)); const hist2D = cv.calcHist(getTestImg(), histAxes); assertPropsWithValue(hist2D)({ rows: 8, cols: 32, dims: 2 }); }); @@ -196,7 +235,7 @@ module.exports = ({ cv, utils, getTestImg }) => { bins: 8, ranges: [0, 256] } - ]; + ].map(x => new cv.HistAxes(x)); const hist3D = cv.calcHist(getTestImg(), histAxes); assertPropsWithValue(hist3D)({ dims: 3 }); }); From ed1cacea29c70af3c7d86dccec9c5629e5bdddd2 Mon Sep 17 00:00:00 2001 From: Stefan-Gabriel Muscalu Date: Thu, 14 Nov 2019 12:39:45 +0200 Subject: [PATCH 4/4] add: deprecation wrapper to support old calcHist API usage --- lib/src/deprecations.js | 34 ++++++++++++++++++++++++++++++ lib/src/index.js | 4 ++++ lib/typings/cv.d.ts | 2 ++ test/tests/imgproc/imgprocTests.js | 12 +++++++++++ 4 files changed, 52 insertions(+) create mode 100644 lib/src/deprecations.js diff --git a/lib/src/deprecations.js b/lib/src/deprecations.js new file mode 100644 index 000000000..b99db0e13 --- /dev/null +++ b/lib/src/deprecations.js @@ -0,0 +1,34 @@ + +const assert = require('assert'); + +module.exports = function (cv) { + + // deprecate wrapper for the old calcHist API + const _calcHist = cv.calcHist; + cv.calcHist = function calcHist(img, histAxes, mask) { + assert(img instanceof cv.Mat, 'Imgproc::CalcHist - Error: expected argument 0 to be of type Mat'); + assert(Array.isArray(histAxes), 'Imgproc::CalcHist - Error: expected argument 1 to be of type array of HistAxes'); + + histAxes = histAxes.slice(); + + let warningThrown = false; + const len = histAxes.length; + + for (let i = 0; i < len; ++i) { + const entry = histAxes[i]; + if (!(entry instanceof cv.HistAxes)) { + if (!warningThrown) { + warningThrown = true; + console.warn(`Imgproc::CalcHist - Deprecated support for object in argument 1 at index ${i}. Please switch to using HistAxes instances.`); + } + histAxes[i] = new cv.HistAxes(entry); + } + } + + if (mask) { + return _calcHist(img, histAxes, mask); + } + return _calcHist(img, histAxes); + }; + +}; diff --git a/lib/src/index.js b/lib/src/index.js index c5107c6ac..a2e5bcfb3 100644 --- a/lib/src/index.js +++ b/lib/src/index.js @@ -1,4 +1,5 @@ const makeDrawUtils = require('./drawUtils') +const deprecations = require('./deprecations') module.exports = function(cv) { const { @@ -8,5 +9,8 @@ module.exports = function(cv) { cv.drawTextBox = drawTextBox cv.drawDetection = drawDetection + + deprecations(cv) + return cv } \ No newline at end of file diff --git a/lib/typings/cv.d.ts b/lib/typings/cv.d.ts index 5f1a2e442..f4e949bab 100644 --- a/lib/typings/cv.d.ts +++ b/lib/typings/cv.d.ts @@ -39,6 +39,8 @@ export function blobFromImagesAsync(image: Mat[], scaleFactor?: number, size?: S export function blur(mat: Mat, kSize: Size, anchor?: Point2, borderType?: number): Mat; export function blurAsync(mat: Mat, kSize: Size, anchor?: Point2, borderType?: number): Promise; export function NMSBoxes(bboxes: Rect[], scores: number[], scoreThreshold: number, nmsThreshold: number): number[]; +/** @deprecated */ +export function calcHist(img: Mat, histAxes: { channel: number, bins: number, ranges: [number, number] }[], mask?: Mat): Mat; export function calcHist(img: Mat, histAxes: HistAxes[], mask?: Mat): Mat; export function calcHistAsync(img: Mat, histAxes: HistAxes[], mask?: Mat): Promise; export function calibrateCamera(objectPoints: Point3[], imagePoints: Point2[], imageSize: Size, cameraMatrix: Mat, distCoeffs: number[], flags?: number, criteria?: TermCriteria): { returnValue: number, rvecs: Vec3[], tvecs: Vec3[], distCoeffs: number[] }; diff --git a/test/tests/imgproc/imgprocTests.js b/test/tests/imgproc/imgprocTests.js index a3311b8a2..5a693c53d 100644 --- a/test/tests/imgproc/imgprocTests.js +++ b/test/tests/imgproc/imgprocTests.js @@ -188,6 +188,18 @@ module.exports = ({ cv, utils, getTestImg }) => { expect(() => cv.calcHist(getTestImg())).to.throw('Imgproc::CalcHist - Error: expected argument 1 to be of type array of HistAxes'); }); + it('should return when using the deprecated API', () => { + const histAxes = [ + { + channel: 0, + bins: 8, + ranges: [0, 256] + } + ]; + const hist1D = cv.calcHist(getTestImg(), histAxes); + assertPropsWithValue(hist1D)({ rows: 8, cols: 1, dims: 2 }); + }); + it('should return 1 dimensional hist', () => { const histAxes = [ {