Skip to content

Commit

Permalink
refactor calcHist and add calcHistAsync
Browse files Browse the repository at this point in the history
  • Loading branch information
legraphista committed Oct 23, 2019
1 parent a7cb01f commit 398400e
Show file tree
Hide file tree
Showing 5 changed files with 138 additions and 100 deletions.
109 changes: 10 additions & 99 deletions cc/imgproc/imgproc.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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<float*> 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);
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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<std::vector<float>> _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<v8::Array> jsHistAxes = v8::Local<v8::Array>::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<float*> ranges;
// TODO replace old macros
for (int i = 0; i < dims; ++i) {
ranges.push_back(new float[dims]);
v8::Local<v8::Object> jsAxis = Nan::To<v8::Object>((Nan::Get(jsHistAxes, i).ToLocalChecked())).ToLocalChecked();
if (!FF::hasOwnProperty(jsAxis, "ranges")) {
return tryCatch.throwError("expected axis object to have ranges property");
}
v8::Local<v8::Value> jsRangesVal = Nan::Get(jsAxis, Nan::New("ranges").ToLocalChecked()).ToLocalChecked();
if (!jsRangesVal->IsArray()) {
return tryCatch.throwError("expected ranges to be an array");
}
v8::Local<v8::Array> jsRanges = v8::Local<v8::Array>::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");

Expand Down Expand Up @@ -431,4 +334,12 @@ NAN_METHOD(Imgproc::AccumulateWeightedAsync) {
FF::asyncBinding<ImgprocBindings::AccumulateWeighted>("Imgproc", "AccumulateWeighted", info);
}

NAN_METHOD(Imgproc::CalcHist) {
FF::syncBinding<ImgprocBindings::CalcHist>("Imgproc", "CalcHist", info);
}

NAN_METHOD(Imgproc::CalcHistAsync) {
FF::asyncBinding<ImgprocBindings::CalcHist>("Imgproc", "CalcHist", info);
}

#endif
3 changes: 2 additions & 1 deletion cc/imgproc/imgproc.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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
121 changes: 121 additions & 0 deletions cc/imgproc/imgprocBindings.h
Original file line number Diff line number Diff line change
Expand Up @@ -269,6 +269,127 @@ namespace ImgprocBindings {
};
};
};

typedef struct HistAxes {
float range[2];
int channel;
int bins;
} HistAxes;

class HistAxesConverterImpl : public FF::UnwrapperBase<HistAxesConverterImpl, HistAxes> {
public:

typedef HistAxes Type;

static std::string getTypeName() {
return std::string("HistAxes");
}

static bool assertType(v8::Local<v8::Value> jsVal) {
if (!jsVal->IsObject()) return false;

auto jsObj = Nan::To<v8::Object>(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<v8::Array>::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<v8::Value> jsVal) {
FF::TryCatch tryCatch("Imgproc::CalcHist");
HistAxes ret;
auto jsAxis = Nan::To<v8::Object>(jsVal).ToLocalChecked();

v8::Local<v8::Value> jsRangesVal = Nan::Get(jsAxis, Nan::New("ranges").ToLocalChecked()).ToLocalChecked();
v8::Local<v8::Array> jsRanges = v8::Local<v8::Array>::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<v8::Value> wrap(HistAxes val) {
v8::Local<v8::Object> ret = Nan::New<v8::Object>();
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<HistAxesConverterImpl> ArrayHistAxesConverter;

class CalcHist : public CvBinding {
private:
cv::MatND hist;
public:
v8::Local<v8::Value> getReturnValue(){
return Mat::Converter::wrap(hist);
}

void setup() {

auto src = req<Mat::Converter>();
auto jsHistAxes = req<ArrayHistAxesConverter>();
auto mask = opt<Mat::Converter>("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
1 change: 1 addition & 0 deletions lib/typings/cv.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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<Mat>;
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<Mat>;
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[] };
Expand Down
4 changes: 4 additions & 0 deletions test/tests/imgproc/imgprocTests.js
Original file line number Diff line number Diff line change
Expand Up @@ -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 = [
{
Expand Down

0 comments on commit 398400e

Please sign in to comment.