Skip to content

Commit

Permalink
Merge pull request justadudewhohacks#651 from legraphista/feature/cal…
Browse files Browse the repository at this point in the history
…cHistAsync

refactor calcHist and add calcHistAsync
  • Loading branch information
justadudewhohacks authored Nov 15, 2019
2 parents 92cf0b2 + ed1cace commit c602ff8
Show file tree
Hide file tree
Showing 12 changed files with 278 additions and 105 deletions.
1 change: 1 addition & 0 deletions binding.gyp
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
64 changes: 64 additions & 0 deletions cc/core/HistAxes.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
//
// Created by stefan on 11/4/19.
//

#include "HistAxes.h"

Nan::Persistent<v8::FunctionTemplate> HistAxes::constructor;

NAN_MODULE_INIT(HistAxes::Init) {
v8::Local<v8::FunctionTemplate> ctor = Nan::New<v8::FunctionTemplate>(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<v8::Object>(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<v8::Value> jsRangesVal = Nan::Get(jsAxis, Nan::New("ranges").ToLocalChecked()).ToLocalChecked();
v8::Local<v8::Array> jsRanges = v8::Local<v8::Array>::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());
}
36 changes: 36 additions & 0 deletions cc/core/HistAxes.h
Original file line number Diff line number Diff line change
@@ -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<double> range = {};
int channel = 0;
int bins = 0;
};
}

class HistAxes: public FF::ObjectWrap<HistAxes, internal::HistAxes> {
public:
static Nan::Persistent<v8::FunctionTemplate> 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__
1 change: 1 addition & 0 deletions cc/core/core.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
1 change: 1 addition & 0 deletions cc/core/core.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
#include "Rect.h"
#include "RotatedRect.h"
#include "TermCriteria.h"
#include "HistAxes.h"
#include "macros.h"
#include <opencv2/core.hpp>

Expand Down
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
4 changes: 3 additions & 1 deletion cc/imgproc/imgproc.h
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
#include "Mat.h"
#include "Contour.h"
#include "Moments.h"
#include "HistAxes.h"

#ifndef __FF_IMGPROC_H__
#define __FF_IMGPROC_H__
Expand All @@ -18,7 +19,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 +49,8 @@ class Imgproc {
static NAN_METHOD(AccumulateSquareAsync);
static NAN_METHOD(AccumulateWeighted);
static NAN_METHOD(AccumulateWeightedAsync);
static NAN_METHOD(CalcHist);
static NAN_METHOD(CalcHistAsync);
};

#endif
61 changes: 60 additions & 1 deletion cc/imgproc/imgprocBindings.h
Original file line number Diff line number Diff line change
Expand Up @@ -206,7 +206,7 @@ namespace ImgprocBindings {
};

class Accumulate : public CvBinding {
public:
public:
void setup() {
auto src = req<Mat::Converter>();
auto dst = req<Mat::Converter>();
Expand Down Expand Up @@ -269,6 +269,65 @@ namespace ImgprocBindings {
};
};
};

class CalcHist : public CvBinding {
public:
void setup() {

auto src = req<Mat::Converter>();
auto jsHistAxes = req<HistAxes::ArrayConverter>();
auto mask = opt<Mat::Converter>("mask", cv::noArray().getMat());
auto retHist = ret<Mat::Converter>("hist");

executeBinding = [=]() {
auto histAxes = jsHistAxes->ref();
auto img = src->ref();
cv::MatND hist;

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;
}

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);
}

retHist->ref() = hist;
};
}
};
}

#endif
Loading

0 comments on commit c602ff8

Please sign in to comment.