scanner: Check clarity of left mid of qr
This commit is contained in:
parent
d33836099c
commit
55fd00d35e
@ -260,6 +260,6 @@ install-scanner: qrtool.wx.wasm.br
|
||||
@cp -v qrtool.wx.js ../scanner/worker
|
||||
|
||||
install-web: qrtool.web.wasm
|
||||
@cp -v qrtool.web.js qrtool.web.wasm ../web/public/camera-4.0/js/
|
||||
@cp -v qrtool.web.js qrtool.web.wasm ../web/public/camera-5.0/js/
|
||||
|
||||
install: install-web install-scanner
|
||||
|
||||
267
alg/libqr.cpp
267
alg/libqr.cpp
@ -111,14 +111,12 @@ bool detect_qr(ProcessState &ps, float margin_ratio, bool warp, string &err)
|
||||
cvtColor(ps.straighten, g, COLOR_BGR2GRAY);
|
||||
equalizeHist(g, g);
|
||||
|
||||
Rect dot_rect;
|
||||
dot_rect.x = 0;
|
||||
dot_rect.y = 0;
|
||||
dot_rect.width = margin / 2;
|
||||
dot_rect.height = margin / 2;
|
||||
ps.dot_area = ps.straighten(dot_rect);
|
||||
Mat dot_area_gray = g(dot_rect);
|
||||
resize(dot_area_gray, ps.dot_area_gray, Size(64, 64));
|
||||
Rect left_mid_rect;
|
||||
left_mid_rect.x = 0;
|
||||
left_mid_rect.y = ps.straighten.rows / 6;
|
||||
left_mid_rect.width = ps.straighten.cols / 3;
|
||||
left_mid_rect.height = ps.straighten.rows / 3;
|
||||
ps.left_mid_area = ps.straighten(left_mid_rect);
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -138,107 +136,6 @@ bool preprocess(ProcessState &ps)
|
||||
return true;
|
||||
}
|
||||
|
||||
struct EnergyGradient {
|
||||
double x;
|
||||
double y;
|
||||
};
|
||||
|
||||
static
|
||||
EnergyGradient energy_gradient(Mat &gray_img)
|
||||
{
|
||||
|
||||
Mat smd_image_x, smd_image_y, G;
|
||||
|
||||
Mat kernel_x(3, 3, CV_32F, Scalar(0));
|
||||
kernel_x.at<float>(1, 2) = -1.0;
|
||||
kernel_x.at<float>(1, 1) = 1.0;
|
||||
Mat kernel_y(3, 3, CV_32F, Scalar(0));
|
||||
kernel_y.at<float>(1, 1) = 1.0;
|
||||
kernel_y.at<float>(2, 1) = -1.0;
|
||||
filter2D(gray_img, smd_image_x, gray_img.depth(), kernel_x);
|
||||
filter2D(gray_img, smd_image_y, gray_img.depth(), kernel_y);
|
||||
|
||||
multiply(smd_image_x, smd_image_x, smd_image_x);
|
||||
multiply(smd_image_y, smd_image_y, smd_image_y);
|
||||
|
||||
EnergyGradient ret = { mean(smd_image_x)[0], mean(smd_image_y)[0], };
|
||||
return ret;
|
||||
}
|
||||
|
||||
static
|
||||
bool check_blur_by_energy_gradient(Mat &gray, string &err)
|
||||
{
|
||||
const int thres = 85;
|
||||
auto a = energy_gradient(gray);
|
||||
|
||||
float angle = 45;
|
||||
auto m = getRotationMatrix2D(Point2f(gray.cols / 2, gray.rows / 2), angle, 1.0);
|
||||
Mat rotated;
|
||||
warpAffine(gray, rotated, m, gray.size());
|
||||
|
||||
auto b = energy_gradient(rotated);
|
||||
|
||||
auto diffa = fabs(a.x - a.y);
|
||||
auto diffb = fabs(b.x - b.y);
|
||||
auto diffa_percent = 100 * diffa / max(a.x, a.y);
|
||||
auto diffb_percent = 100 * diffb / max(b.x, b.y);
|
||||
bool ret =
|
||||
((a.x > thres && a.y > thres) || (b.x > thres && b.y > thres)) &&
|
||||
diffa_percent < 15 && diffb_percent < 15;
|
||||
|
||||
cout << "energy: "
|
||||
+ to_string(a.x) + " "
|
||||
+ to_string(a.y) + " "
|
||||
+ to_string(b.x) + " "
|
||||
+ to_string(b.y) << endl;
|
||||
if (!ret) {
|
||||
err = "energy: "
|
||||
+ to_string(a.x) + " "
|
||||
+ to_string(a.y) + " "
|
||||
+ to_string(b.x) + " "
|
||||
+ to_string(b.y);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
double laplacian(Mat &gray, string &err)
|
||||
{
|
||||
int ddepth = CV_16S;
|
||||
Mat check, lap;
|
||||
GaussianBlur(gray, check, Size(5, 5), 0, 0, BORDER_DEFAULT);
|
||||
Laplacian(check, lap, ddepth, 3);
|
||||
convertScaleAbs(lap, lap);
|
||||
|
||||
Mat mean, stddev;
|
||||
meanStdDev(lap, mean, stddev);
|
||||
if (stddev.cols * stddev.rows == 1) {
|
||||
double area = gray.rows * gray.cols;
|
||||
double sd = stddev.at<double>(0, 0);
|
||||
double var = sd * sd;
|
||||
return var / area;
|
||||
}
|
||||
err = "wrong shape of stddev result";
|
||||
return -1;
|
||||
}
|
||||
|
||||
static
|
||||
bool check_blur_by_laplacian(ProcessState &ps, Mat &gray, string &err)
|
||||
{
|
||||
auto var = laplacian(gray, err);
|
||||
if (var < 0) return false;
|
||||
|
||||
ps.clarity = var;
|
||||
if (var <= ps.laplacian_thres) {
|
||||
err = string_format("image (%d x %d) too blurry: %lf <= %lf",
|
||||
gray.cols, gray.rows,
|
||||
var, ps.laplacian_thres
|
||||
);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static
|
||||
bool check_blur_by_sobel(ProcessState &ps, Mat &gray, string &err)
|
||||
{
|
||||
@ -253,6 +150,7 @@ bool check_blur_by_sobel(ProcessState &ps, Mat &gray, string &err)
|
||||
|
||||
Scalar meanMagnitude = mean(magnitude);
|
||||
double sharpness = meanMagnitude[0] / (gray.rows * gray.cols) * 1000;
|
||||
ps.clarity = sharpness;
|
||||
if (sharpness < thres) {
|
||||
err = string_format("image too blurry: %lf < %lf", sharpness, thres);
|
||||
return false;
|
||||
@ -286,151 +184,6 @@ static bool is_valid_pattern(Mat &img)
|
||||
}
|
||||
#endif
|
||||
|
||||
static
|
||||
int find_score(Mat &img)
|
||||
{
|
||||
int ret = 0;
|
||||
for (int row = 0; row < img.rows; row++) {
|
||||
int row_sum = 0;
|
||||
for (int col = 0; col < img.cols; col++) {
|
||||
auto p = img.at<bool>(row, col);
|
||||
if (p) {
|
||||
row_sum += 1;
|
||||
}
|
||||
}
|
||||
if (row_sum) {
|
||||
ret += 1;
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void clear_connected(Mat &bin, Point p)
|
||||
{
|
||||
vector<Point> q;
|
||||
q.push_back(p);
|
||||
while (q.size()) {
|
||||
auto p = q[q.size() - 1];
|
||||
q.pop_back();
|
||||
bin.at<uint8_t>(p.y, p.x) = 0;
|
||||
for (int i = -1; i <= 1; i++) {
|
||||
for (int j = -1; j <= 1; j++) {
|
||||
int nx = p.x + i;
|
||||
int ny = p.y + j;
|
||||
if (nx < 0 || nx >= bin.cols || ny < 0 || ny >= bin.rows) {
|
||||
continue;
|
||||
}
|
||||
if (bin.at<bool>(ny, nx)) {
|
||||
q.push_back(Point(nx, ny));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static
|
||||
vector<Point> find_points(Mat bin)
|
||||
{
|
||||
vector<Point> ret;
|
||||
for (int x = 0; x < bin.cols; x++) {
|
||||
for (int y = 0; y < bin.rows; y++) {
|
||||
auto p = bin.at<uint8_t>(y, x);
|
||||
if (!p) continue;
|
||||
auto point = Point(x, y);
|
||||
ret.push_back(point);
|
||||
clear_connected(bin, point);
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static
|
||||
int adaptive_erode(Mat &bin, Mat &eroded, string &err)
|
||||
{
|
||||
auto kernel = getStructuringElement(MORPH_ELLIPSE, Size(3, 3));
|
||||
const int min_points = 25;
|
||||
int max_erodes = 5;
|
||||
|
||||
printf("adaptiveThreshold\n");
|
||||
eroded = bin.clone();
|
||||
while (max_erodes-- > 0) {
|
||||
// In this loop we erode a "full" image in order to get enough detached components
|
||||
auto points = find_points(bin.clone());
|
||||
printf("points: %zu\n", points.size());
|
||||
if (points.size() == 0) {
|
||||
err = "cannot find enough points";
|
||||
return -1;
|
||||
}
|
||||
if (points.size() > min_points) {
|
||||
break;
|
||||
}
|
||||
erode(eroded, eroded, kernel);
|
||||
}
|
||||
|
||||
while (max_erodes-- > 0) {
|
||||
// In this loop we further erode a "lean" image in order to get clarity until it's too much
|
||||
Mat next;
|
||||
erode(eroded, next, kernel);
|
||||
auto points = find_points(next.clone());
|
||||
if (points.size() < min_points) {
|
||||
break;
|
||||
}
|
||||
eroded = next;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static
|
||||
int emblem_detect_angle(Mat &gray, bool check_orthogonal, string &err)
|
||||
{
|
||||
Mat bin;
|
||||
int min_score = gray.cols;
|
||||
int max_score = 0;
|
||||
int lowest_score_angle = -1;
|
||||
|
||||
adaptiveThreshold(gray, bin, 255, ADAPTIVE_THRESH_GAUSSIAN_C, THRESH_BINARY_INV, 11, 2);
|
||||
|
||||
Mat inverted;
|
||||
bitwise_not(bin, inverted);
|
||||
const int MAX_ROT_ANGLE = 180;
|
||||
int scores[MAX_ROT_ANGLE] = { 0 };
|
||||
const int score_diff_thres = 5;
|
||||
|
||||
Mat eroded;
|
||||
adaptive_erode(bin, eroded, err);
|
||||
|
||||
for (int angle = 0; angle < MAX_ROT_ANGLE; angle += 1) {
|
||||
auto m = getRotationMatrix2D(Point2f(gray.cols / 2, gray.rows / 2), angle, 1.0);
|
||||
Mat rotated;
|
||||
warpAffine(eroded, rotated, m, gray.size());
|
||||
int score = find_score(rotated);
|
||||
scores[angle] = score;
|
||||
if (score < min_score) {
|
||||
lowest_score_angle = angle;
|
||||
}
|
||||
min_score = min(score, min_score);
|
||||
max_score = max(max_score, score);
|
||||
}
|
||||
if (max_score - min_score > score_diff_thres) {
|
||||
int orthogonal_angle = lowest_score_angle + 90;
|
||||
if (orthogonal_angle > 180) {
|
||||
orthogonal_angle -= 180;
|
||||
}
|
||||
int orthogonal_score = scores[orthogonal_angle];
|
||||
printf("lowest_score_angle %d, min score %d, max score %d, orthogonal_angle %d, orthogonal score: %d\n",
|
||||
lowest_score_angle, min_score, max_score, orthogonal_angle, orthogonal_score);
|
||||
lowest_score_angle = lowest_score_angle > 90 ? lowest_score_angle - 90 : lowest_score_angle;
|
||||
if (lowest_score_angle > 45)
|
||||
lowest_score_angle = 90 - lowest_score_angle;
|
||||
if (max_score - orthogonal_score > score_diff_thres || !check_orthogonal) {
|
||||
return lowest_score_angle;
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
bool emblem_dot_angle(ProcessState &ps, InputArray in, float &angle, string &qrcode, string &err)
|
||||
{
|
||||
try {
|
||||
@ -438,7 +191,7 @@ bool emblem_dot_angle(ProcessState &ps, InputArray in, float &angle, string &qrc
|
||||
ps.orig = (Mat *)in.getObj();
|
||||
preprocess(ps);
|
||||
|
||||
if (!detect_qr(ps, 0.20, true, err)) {
|
||||
if (!detect_qr(ps, 0, true, err)) {
|
||||
qrcode = ps.qrcode;
|
||||
err = "detect_qr: " + err;
|
||||
return false;
|
||||
@ -446,7 +199,9 @@ bool emblem_dot_angle(ProcessState &ps, InputArray in, float &angle, string &qrc
|
||||
|
||||
qrcode = ps.qrcode;
|
||||
|
||||
if (!check_sharpness(ps, ps.dot_area_gray, ps.sharpness_method, err)) {
|
||||
Mat lma_gray;
|
||||
cvtColor(ps.left_mid_area, lma_gray, COLOR_BGR2GRAY);
|
||||
if (!check_sharpness(ps, lma_gray, ps.sharpness_method, err)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@ -20,13 +20,11 @@ struct ProcessState {
|
||||
CvImg straighten;
|
||||
Rect qr_rect_in_straighten;
|
||||
CvImg qr_straighten;
|
||||
CvImg dot_area;
|
||||
CvImg dot_area_gray;
|
||||
CvImg left_mid_area;
|
||||
string qrcode = "";
|
||||
double clarity;
|
||||
float laplacian_thres = 0.1;
|
||||
float sobel_thres = 15;
|
||||
int sharpness_method = 2;
|
||||
float clarity = 0;
|
||||
};
|
||||
|
||||
bool preprocess(ProcessState &ps);
|
||||
@ -37,7 +35,6 @@ enum SimilarityAlg {
|
||||
FuzzyPixelCmp,
|
||||
};
|
||||
double emblem_roi_similarity(SimilarityAlg alg, InputArray a, InputArray b, string &err);
|
||||
double laplacian(Mat &gray, string &err);
|
||||
|
||||
static inline void showimg_(const char *title, Mat &img) {
|
||||
imshow(title, img);
|
||||
|
||||
541
alg/qrtool.cpp
541
alg/qrtool.cpp
@ -81,218 +81,10 @@ int dot_cmd(char **argv, int argc)
|
||||
printf("angle: %.1f\n", angle);
|
||||
printf("qrcode: %s\n", qrcode.c_str());
|
||||
printf("saving dot file: %s\n", outfile.c_str());
|
||||
imwrite(outfile, ps.dot_area);
|
||||
imwrite(outfile, ps.left_mid_area);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static
|
||||
int clarity_cmd(char **argv, int argc)
|
||||
{
|
||||
string err;
|
||||
|
||||
char *file = argv[0];
|
||||
printf("file: %s\n", file);
|
||||
Mat orig = imread(file);
|
||||
Mat gray;
|
||||
cvtColor(orig, gray, COLOR_BGR2GRAY);
|
||||
auto c = laplacian(gray, err);
|
||||
printf("clarity: %lf\n", c);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static
|
||||
int rectify_cmd(char **argv, int argc)
|
||||
{
|
||||
char *file = argv[0];
|
||||
string err;
|
||||
ProcessState ps;
|
||||
Mat orig = imread(file);
|
||||
|
||||
ps.orig = &orig;
|
||||
preprocess(ps);
|
||||
|
||||
if (!detect_qr(ps, 0.20, false, err)) {
|
||||
cerr << err << endl;
|
||||
return 1;
|
||||
}
|
||||
string outfile = string(file) + ".qr.jpg";
|
||||
imwrite(outfile, ps.straighten);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static
|
||||
int topleft_cmd(char **argv, int argc)
|
||||
{
|
||||
char *file = argv[0];
|
||||
string err;
|
||||
ProcessState ps;
|
||||
Mat orig = imread(file);
|
||||
|
||||
ps.orig = &orig;
|
||||
preprocess(ps);
|
||||
|
||||
if (!detect_qr(ps, 0.02, true, err)) {
|
||||
cerr << err << endl;
|
||||
return 1;
|
||||
}
|
||||
string outfile = string(file) + ".topleft.jpg";
|
||||
Mat &base = ps.straighten;
|
||||
auto crop = Rect(0, 0, base.cols / 2, base.rows / 2);
|
||||
Mat result = base(crop);
|
||||
imwrite(outfile, result);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static
|
||||
int find_roi_start_point(Mat &bin, Point &p)
|
||||
{
|
||||
int npoints = 4;
|
||||
|
||||
for (int i = 0; i < bin.cols / 3; i++) {
|
||||
uchar sum = 0;
|
||||
for (int j = 0; j < npoints; j++) {
|
||||
int v = i + j;
|
||||
sum += bin.at<uchar>(v, v);
|
||||
}
|
||||
if (sum == 0) {
|
||||
p = Point(i, i);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
cerr << "find_roi_start_point" << endl;
|
||||
return -1;
|
||||
}
|
||||
|
||||
static
|
||||
vector<int> count_black(Mat &bin, bool count_rows, int size)
|
||||
{
|
||||
vector<int> ret;
|
||||
for (int i = 0; i < size; i++) {
|
||||
int count = 0;
|
||||
for (int j = 0; j < size; j++) {
|
||||
int x = count_rows ? j : i;
|
||||
int y = count_rows ? i : j;
|
||||
if (bin.at<uchar>(y, x) == 0) count++;
|
||||
}
|
||||
ret.push_back(count);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int find_start_of_first_black_range(const vector<int> &data)
|
||||
{
|
||||
size_t i = 0;
|
||||
int m = *std::max_element(data.begin(), data.end());
|
||||
int thres = m * 50 / 100;
|
||||
while (i < data.size() - 3) {
|
||||
if (data[i] >= thres && data[i + 1] >= thres && data[i + 2] >= thres) {
|
||||
break;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
if (i >= data.size() - 3) return -1;
|
||||
return i;
|
||||
}
|
||||
|
||||
static int find_end_of_first_black_range(const vector<int> &data)
|
||||
{
|
||||
size_t i = 0;
|
||||
int m = *std::max_element(data.begin(), data.end());
|
||||
int thres = m * 50 / 100;
|
||||
while (i < data.size() - 3) {
|
||||
if (data[i] >= thres && data[i + 1] >= thres && data[i + 2] >= thres) {
|
||||
break;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
if (i >= data.size() - 3) return -1;
|
||||
while (i < data.size() - 3) {
|
||||
if (data[i] < thres || data[i + 1] < thres || data[i + 2] < thres) {
|
||||
break;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
if (i >= data.size() - 3) return -1;
|
||||
return i;
|
||||
}
|
||||
|
||||
static
|
||||
int find_roi_rect(Mat &bin, Point &start, Rect &rect, bool inner, string &err)
|
||||
{
|
||||
Mat visited;
|
||||
bin.copyTo(visited);
|
||||
vector<Point> q;
|
||||
q.push_back(start);
|
||||
int min_x = bin.rows, min_y = bin.rows, max_x = 0, max_y = 0;
|
||||
int orig_size = max(bin.rows, bin.cols);
|
||||
|
||||
while (q.size()) {
|
||||
Point p = q.back();
|
||||
q.pop_back();
|
||||
visited.at<uchar>(p.y, p.x) = 255;
|
||||
min_x = min(min_x, p.x);
|
||||
min_y = min(min_y, p.y);
|
||||
max_x = max(max_x, p.x);
|
||||
max_y = max(max_y, p.y);
|
||||
for (int xoff = -1; xoff <= 1; xoff++) {
|
||||
for (int yoff = -1; yoff <= 1; yoff++) {
|
||||
int x = p.x + xoff;
|
||||
int y = p.y + yoff;
|
||||
if (x >= 0 && x < visited.cols && y >= 0 && y < visited.rows) {
|
||||
auto v = visited.at<uchar>(y, x);
|
||||
if (v == 0) {
|
||||
if (q.size() >= 20000) {
|
||||
err = string("roi detected range too large: ") + to_string(q.size());
|
||||
return -1;
|
||||
}
|
||||
q.push_back(Point(x, y));
|
||||
visited.at<uchar>(y, x) = 255;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (max_x - min_x < 50 || max_y - min_y < 50) {
|
||||
err = "detected roi outer region too small";
|
||||
return -1;
|
||||
}
|
||||
|
||||
auto size = std::max(max_x, max_y);
|
||||
|
||||
auto row_sums = count_black(bin, true, size);
|
||||
auto col_sums = count_black(bin, false, size);
|
||||
|
||||
min_x = inner ? find_end_of_first_black_range(col_sums) : find_start_of_first_black_range(col_sums);
|
||||
min_y = inner ? find_end_of_first_black_range(row_sums) : find_start_of_first_black_range(row_sums);
|
||||
|
||||
if (min_x < 0 || min_y < 0) {
|
||||
err = "min_x or min_y is negative";
|
||||
return -1;
|
||||
}
|
||||
|
||||
// find the max values, similarly
|
||||
std::reverse(col_sums.begin(), col_sums.end());
|
||||
std::reverse(row_sums.begin(), row_sums.end());
|
||||
max_x = size - (inner ? find_end_of_first_black_range(col_sums) : find_start_of_first_black_range(col_sums));
|
||||
max_y = size - (inner ? find_end_of_first_black_range(row_sums) : find_start_of_first_black_range(row_sums));
|
||||
|
||||
if (max_x < 0 || max_y < 0) return -1;
|
||||
|
||||
if (max_x - min_x < 50 || max_y - min_y < 50) {
|
||||
err = "detected roi region too small";
|
||||
return -1;
|
||||
}
|
||||
size = (max_x - min_x + max_y - min_y) / 2;
|
||||
|
||||
if (size < orig_size / 5 || size > orig_size * 3 / 5) {
|
||||
err = "size of found region is out of valid range";
|
||||
return -1;
|
||||
}
|
||||
rect = Rect(min_x + 1, min_y + 1, size, size);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void get_bin(Mat &orig, Mat &out)
|
||||
{
|
||||
@ -307,297 +99,6 @@ static void get_bin(Mat &orig, Mat &out)
|
||||
threshold(gray, out, 128, 255, THRESH_BINARY);
|
||||
}
|
||||
|
||||
static
|
||||
int find_roi(Mat &qr_straighten, Mat &roi, bool inner, string &err)
|
||||
{
|
||||
Mat bin;
|
||||
Rect topleft_r(0, 0, qr_straighten.cols / 2, qr_straighten.rows / 2);
|
||||
Mat topleft = qr_straighten(topleft_r);
|
||||
get_bin(topleft, bin);
|
||||
|
||||
Point start;
|
||||
Rect roi_rect;
|
||||
auto r = find_roi_start_point(bin, start);
|
||||
if (r) {
|
||||
err = "failed to find roi start point";
|
||||
return r;
|
||||
}
|
||||
r = find_roi_rect(bin, start, roi_rect, inner, err);
|
||||
if (r) {
|
||||
return r;
|
||||
}
|
||||
roi = qr_straighten(roi_rect);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int roi_process_one(const char *file, bool inner, string &err, bool warp, Mat *roi_out)
|
||||
{
|
||||
Mat roi;
|
||||
Mat orig = imread(file, IMREAD_COLOR);
|
||||
Mat qr_with_margin;
|
||||
|
||||
if (warp) {
|
||||
ProcessState ps;
|
||||
ps.orig = &orig;
|
||||
preprocess(ps);
|
||||
if (!detect_qr(ps, 0.02, true, err)) {
|
||||
cerr << err << ":" << file << endl;
|
||||
return 1;
|
||||
}
|
||||
qr_with_margin = ps.straighten;
|
||||
} else {
|
||||
qr_with_margin = orig;
|
||||
}
|
||||
if (qr_with_margin.cols <= 0 || qr_with_margin.rows <= 0) return -1;
|
||||
auto r = find_roi(qr_with_margin, roi, inner, err);
|
||||
if (r) return r;
|
||||
if (roi_out) {
|
||||
*roi_out = roi;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static bool is_dir(char *path)
|
||||
{
|
||||
struct stat st;
|
||||
|
||||
if (lstat(path, &st) == 0) {
|
||||
if (S_ISREG(st.st_mode)) {
|
||||
return false;
|
||||
} else if (S_ISDIR(st.st_mode)) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
perror("Error in lstat");
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static void save_roi(string orig_file, Mat &roi)
|
||||
{
|
||||
string outfile = orig_file + ".roi.jpg";
|
||||
cout << "save: " << outfile << endl;
|
||||
imwrite(outfile, roi);
|
||||
}
|
||||
|
||||
static
|
||||
int frame_roi_cmd(char **argv, int argc)
|
||||
{
|
||||
char *file = argv[0];
|
||||
string err;
|
||||
int ret = 0;
|
||||
Mat roi;
|
||||
|
||||
cout << "frame roi processing: " << file << endl;
|
||||
ret = roi_process_one(file, false, err, true, &roi);
|
||||
if (ret) {
|
||||
cerr << "failed to process: " << file << ":" << err <<endl;
|
||||
return ret;
|
||||
}
|
||||
save_roi(file, roi);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static
|
||||
int roi_cmd(char **argv, int argc)
|
||||
{
|
||||
char *file = argv[0];
|
||||
string err;
|
||||
int ret = 0;
|
||||
|
||||
cout << "roi processing: " << file << endl;
|
||||
if (is_dir(file)) {
|
||||
for (auto const& dir_entry : filesystem::directory_iterator{file}) {
|
||||
auto path = dir_entry.path();
|
||||
Mat roi;
|
||||
if (roi_process_one(path.c_str(), false, err, false, &roi) != 0) {
|
||||
cerr << "failed: " << path << ":" << err << endl;
|
||||
ret = 1;
|
||||
}
|
||||
save_roi(file, roi);
|
||||
}
|
||||
} else {
|
||||
Mat roi;
|
||||
ret = roi_process_one(file, false, err, false, &roi);
|
||||
if (ret) {
|
||||
cerr << "failed to process: " << file << ":" << err <<endl;
|
||||
} else {
|
||||
save_roi(file, roi);
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static
|
||||
int roi_bench_cmd(char **argv, int argc)
|
||||
{
|
||||
char *file = argv[0];
|
||||
int n = 0;
|
||||
string err;
|
||||
auto begin = chrono::system_clock::now();
|
||||
for (auto const& dir_entry : filesystem::directory_iterator{file}) {
|
||||
auto path = dir_entry.path();
|
||||
if (roi_process_one(path.c_str(), false, err, false, NULL) == 0) {
|
||||
n += 1;
|
||||
}
|
||||
}
|
||||
auto end = chrono::system_clock::now();
|
||||
std::chrono::duration<float> duration = end - begin;
|
||||
float seconds = duration.count();
|
||||
printf("qps: %.1f\n", n / seconds);
|
||||
return 0;
|
||||
}
|
||||
|
||||
#if USE_PULSAR
|
||||
static vector<string> split_path(const string &path)
|
||||
{
|
||||
vector<string> ret;
|
||||
string cur = "";
|
||||
for (auto x: path) {
|
||||
if (x == '/') {
|
||||
if (cur.size()) {
|
||||
ret.push_back(cur);
|
||||
cur = "";
|
||||
}
|
||||
} else {
|
||||
cur += x;
|
||||
}
|
||||
}
|
||||
if (cur.size()) {
|
||||
ret.push_back(cur);
|
||||
cur = "";
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static string join_path(const vector<string> fs)
|
||||
{
|
||||
string ret;
|
||||
const string sep = "/";
|
||||
|
||||
for (auto x: fs) {
|
||||
ret += sep + x;
|
||||
}
|
||||
if (ret.size() == 0) ret = "/";
|
||||
return ret;
|
||||
}
|
||||
|
||||
static string get_output_path(const string &path)
|
||||
{
|
||||
auto ret = split_path(path);
|
||||
ret[3] = "roi";
|
||||
return join_path(ret);
|
||||
}
|
||||
|
||||
static
|
||||
int roi_worker_handle_image(const string &input_path,
|
||||
const vector<uint8_t> &input,
|
||||
string &output_path,
|
||||
vector<uint8_t> &output)
|
||||
{
|
||||
Mat roi;
|
||||
Mat orig = imdecode(input, IMREAD_COLOR);
|
||||
|
||||
auto r = find_roi(orig, roi, true, true);
|
||||
if (r) return r;
|
||||
imencode(".jpg", roi, output);
|
||||
output_path = get_output_path(input_path);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static
|
||||
int roi_worker_handle_image_nop(const string &input_path,
|
||||
const vector<uint8_t> &input,
|
||||
string &output_path,
|
||||
vector<uint8_t> &output)
|
||||
{
|
||||
output.push_back('f');
|
||||
output.push_back('o');
|
||||
output.push_back('o');
|
||||
output_path = get_output_path(input_path);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static
|
||||
int roi_worker_cmd(char *topic)
|
||||
{
|
||||
string worker_name = "roi-worker-";
|
||||
worker_name += to_string(rand());
|
||||
return mq_worker(topic, "roi-worker", roi_worker_handle_image);
|
||||
}
|
||||
|
||||
static
|
||||
int roi_worker_nop_cmd(char *topic)
|
||||
{
|
||||
string worker_name = "roi-worker-";
|
||||
worker_name += to_string(rand());
|
||||
return mq_worker(topic, "roi-worker", roi_worker_handle_image_nop);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#if ENABLE_GRPC
|
||||
static
|
||||
int grpc_server_cmd(char *addr)
|
||||
{
|
||||
return run_server(addr, roi_worker_handle_image);
|
||||
}
|
||||
#endif
|
||||
|
||||
static
|
||||
int http_server_handle_image(const vector<uint8_t> &input,
|
||||
vector<uint8_t> &output)
|
||||
{
|
||||
string err;
|
||||
Mat roi;
|
||||
Mat orig = imdecode(input, IMREAD_COLOR);
|
||||
|
||||
if (orig.empty()) {
|
||||
return -EINVAL;
|
||||
}
|
||||
auto r = find_roi(orig, roi, false, err);
|
||||
if (r) return r;
|
||||
imencode(".jpg", roi, output);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static
|
||||
int http_server_cmd(char **argv, int argc)
|
||||
{
|
||||
char *port = argv[0];
|
||||
return start_http_server(atoi(port), http_server_handle_image);
|
||||
}
|
||||
|
||||
static
|
||||
int verify_cmd(char **args, int nargs)
|
||||
{
|
||||
char *std_file = args[0];
|
||||
char *frame_file = args[1];
|
||||
Mat std = imread(std_file);
|
||||
Mat frame = imread(frame_file);
|
||||
Mat roi;
|
||||
int r;
|
||||
|
||||
string err;
|
||||
r = roi_process_one(frame_file, false, err, true, &roi);
|
||||
if (r) {
|
||||
printf("failed to find roi: %s\n", err.c_str());
|
||||
return r;
|
||||
}
|
||||
double s = emblem_roi_similarity(FuzzyPixelCmp, std, roi, err);
|
||||
|
||||
if (err.size()) {
|
||||
printf("err: %s\n", err.c_str());
|
||||
return 1;
|
||||
} else {
|
||||
printf("similarity: %f\n", s); return 0;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static
|
||||
void usage(const char *name, vector<string> &cmds)
|
||||
{
|
||||
@ -634,28 +135,6 @@ int roi_verify_cmd(char **argv, int argc)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static
|
||||
int side_by_side_cmd(char **argv, int argc)
|
||||
{
|
||||
Mat frame_roi;
|
||||
string err;
|
||||
cout << "side by side processing: " << argv[0] << " " << argv[1] << endl;
|
||||
int r = roi_process_one(argv[0], false, err, true, &frame_roi);
|
||||
if (r) {
|
||||
cerr << "failed to process: " << argv[0] << ":" << err << endl;
|
||||
return r;
|
||||
}
|
||||
Mat std_roi = imread(argv[1]);
|
||||
Mat side_by_side;
|
||||
auto roi_size = Size(128, 128);
|
||||
resize(frame_roi, frame_roi, roi_size);
|
||||
resize(std_roi, std_roi, roi_size);
|
||||
hconcat(frame_roi, std_roi, side_by_side);
|
||||
auto fn = string(argv[0]) + ".side_by_side.jpg";
|
||||
imwrite(fn, side_by_side);
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef QRTOOL_MAIN
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
@ -673,24 +152,6 @@ int main(int argc, char *argv[])
|
||||
|
||||
add_cmd(detect, 1);
|
||||
add_cmd(angle, 1);
|
||||
add_cmd(dot, 1);
|
||||
add_cmd(clarity, 1);
|
||||
add_cmd(rectify, 1);
|
||||
add_cmd(topleft, 1);
|
||||
add_cmd(frame_roi, 1);
|
||||
add_cmd(roi, 1);
|
||||
add_cmd(roi_bench, 1);
|
||||
#if USE_PULSAR
|
||||
add_cmd(roi_worker, 1);
|
||||
add_cmd(roi_worker_nop, 1);
|
||||
#endif
|
||||
#if ENABLE_GRPC
|
||||
add_cmd(grpc_server, 1);
|
||||
#endif
|
||||
add_cmd(http_server, 1);
|
||||
add_cmd(verify, 2);
|
||||
add_cmd(roi_verify, 2);
|
||||
add_cmd(side_by_side, 2);
|
||||
usage(argv[0], cmds);
|
||||
return 1;
|
||||
}
|
||||
|
||||
@ -8,13 +8,14 @@ using namespace cv;
|
||||
using namespace std;
|
||||
|
||||
static
|
||||
std::string make_resp(bool ok, string err, int angle = -1, string qrcode = "", double elapsed = 0)
|
||||
std::string make_resp(bool ok, string err, float clarity, int angle = -1, string qrcode = "", double elapsed = 0)
|
||||
{
|
||||
char buf[512];
|
||||
snprintf(buf, sizeof(buf), R"({ "ok": %s, "err": "%s", "qrcode": "%s", "angle": %d, "elapsed": %lf })",
|
||||
snprintf(buf, sizeof(buf), R"({ "ok": %s, "err": "%s", "qrcode": "%s", "clarity": %lf, "angle": %d, "elapsed": %lf })",
|
||||
ok ? "true" : "false",
|
||||
err.c_str(),
|
||||
qrcode.c_str(),
|
||||
clarity,
|
||||
angle,
|
||||
elapsed
|
||||
);
|
||||
@ -25,35 +26,28 @@ std::string make_resp(bool ok, string err, int angle = -1, string qrcode = "", d
|
||||
extern "C" {
|
||||
const char *qrtool_angle(uint8_t *data,
|
||||
int width, int height,
|
||||
uint8_t *dot_area,
|
||||
uint8_t *mid_left_area,
|
||||
float camera_sensitivity) {
|
||||
ProcessState ps;
|
||||
ps.laplacian_thres = camera_sensitivity / 10.0;
|
||||
ps.sobel_thres = camera_sensitivity * 15;
|
||||
ps.sobel_thres = camera_sensitivity * 3;
|
||||
auto start = std::chrono::system_clock::now();
|
||||
static char ret[512];
|
||||
printf("qrtool_angle, width: %d height %d\n", width, height);
|
||||
for (int i = 0; i < 16; i++) {
|
||||
printf("%d ", data[i]);
|
||||
}
|
||||
printf("\n");
|
||||
Mat orig(Size(width, height), CV_8UC4, data);
|
||||
printf("mat: %d %d\n", orig.cols, orig.rows);
|
||||
string qrcode, err;
|
||||
float angle;
|
||||
auto ok = emblem_dot_angle(ps, orig, angle, qrcode, err);
|
||||
auto end = std::chrono::system_clock::now();
|
||||
std::chrono::duration<double> elapsed = end-start;
|
||||
auto x = make_resp(ok, err, angle, qrcode, elapsed.count());
|
||||
if (dot_area) {
|
||||
if (!ps.dot_area.empty()) {
|
||||
Mat da;
|
||||
ps.dot_area.convertTo(da, CV_8UC4);
|
||||
resize(da, da, Size(32 ,32));
|
||||
memset(dot_area, 255, 32 * 32 * 4);
|
||||
memcpy(dot_area, da.ptr(), 32 * 32 * 4);
|
||||
auto x = make_resp(ok, err, ps.clarity, angle, qrcode, elapsed.count());
|
||||
if (mid_left_area) {
|
||||
if (!ps.left_mid_area.empty()) {
|
||||
Mat lma;
|
||||
ps.left_mid_area.convertTo(lma, CV_8UC4);
|
||||
resize(lma, lma, Size(32 ,32));
|
||||
memset(mid_left_area, 255, 32 * 32 * 4);
|
||||
memcpy(mid_left_area, lma.ptr(), 32 * 32 * 4);
|
||||
} else {
|
||||
memset(dot_area, 55, 32 * 32 * 4);
|
||||
memset(mid_left_area, 55, 32 * 32 * 4);
|
||||
}
|
||||
}
|
||||
snprintf(ret, 512, "%s", x.c_str());
|
||||
|
||||
@ -3893,7 +3893,7 @@ var asmLibraryArg = {
|
||||
"y": ___cxa_begin_catch,
|
||||
"wb": ___cxa_current_primary_exception,
|
||||
"Ja": ___cxa_decrement_exception_refcount,
|
||||
"F": ___cxa_end_catch,
|
||||
"G": ___cxa_end_catch,
|
||||
"b": ___cxa_find_matching_catch_2,
|
||||
"n": ___cxa_find_matching_catch_3,
|
||||
"C": ___cxa_find_matching_catch_4,
|
||||
@ -3970,7 +3970,7 @@ var asmLibraryArg = {
|
||||
"g": invoke_vii,
|
||||
"l": invoke_viid,
|
||||
"lb": invoke_viidf,
|
||||
"G": invoke_viididii,
|
||||
"F": invoke_viididii,
|
||||
"bb": invoke_viidiiid,
|
||||
"W": invoke_viidiiiii,
|
||||
"Pa": invoke_viifff,
|
||||
@ -3990,7 +3990,7 @@ var asmLibraryArg = {
|
||||
"pa": invoke_viiifii,
|
||||
"k": invoke_viiii,
|
||||
"M": invoke_viiiid,
|
||||
"db": invoke_viiiidd,
|
||||
"cb": invoke_viiiidd,
|
||||
"kb": invoke_viiiidi,
|
||||
"ya": invoke_viiiidid,
|
||||
"Ba": invoke_viiiidiidiiiiiii,
|
||||
@ -4025,7 +4025,7 @@ var asmLibraryArg = {
|
||||
"ga": invoke_viiiiiiiiiifii,
|
||||
"O": invoke_viiiiiiiiiii,
|
||||
"wa": invoke_viiiiiiiiiiiii,
|
||||
"cb": invoke_viiiiiiiiiiiiii,
|
||||
"db": invoke_viiiiiiiiiiiiii,
|
||||
"ba": invoke_viiiiiiiiiiiiiii,
|
||||
"Ta": invoke_viiiiiiiiiiiiiiii,
|
||||
"gb": invoke_viiiiiiiiiiiiiiiiiiidi,
|
||||
@ -4233,17 +4233,6 @@ function invoke_vii(index, a1, a2) {
|
||||
}
|
||||
}
|
||||
|
||||
function invoke_i(index) {
|
||||
var sp = stackSave();
|
||||
try {
|
||||
return getWasmTableEntry(index)();
|
||||
} catch (e) {
|
||||
stackRestore(sp);
|
||||
if (e !== e + 0 && e !== "longjmp") throw e;
|
||||
_setThrew(1, 0);
|
||||
}
|
||||
}
|
||||
|
||||
function invoke_iifii(index, a1, a2, a3, a4) {
|
||||
var sp = stackSave();
|
||||
try {
|
||||
@ -4277,6 +4266,17 @@ function invoke_viiid(index, a1, a2, a3, a4) {
|
||||
}
|
||||
}
|
||||
|
||||
function invoke_i(index) {
|
||||
var sp = stackSave();
|
||||
try {
|
||||
return getWasmTableEntry(index)();
|
||||
} catch (e) {
|
||||
stackRestore(sp);
|
||||
if (e !== e + 0 && e !== "longjmp") throw e;
|
||||
_setThrew(1, 0);
|
||||
}
|
||||
}
|
||||
|
||||
function invoke_viidf(index, a1, a2, a3, a4) {
|
||||
var sp = stackSave();
|
||||
try {
|
||||
@ -4772,10 +4772,10 @@ function invoke_viiiiiiiiiddi(index, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a1
|
||||
}
|
||||
}
|
||||
|
||||
function invoke_viiiidd(index, a1, a2, a3, a4, a5, a6) {
|
||||
function invoke_viiiiiiiiiiiiii(index, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14) {
|
||||
var sp = stackSave();
|
||||
try {
|
||||
getWasmTableEntry(index)(a1, a2, a3, a4, a5, a6);
|
||||
getWasmTableEntry(index)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14);
|
||||
} catch (e) {
|
||||
stackRestore(sp);
|
||||
if (e !== e + 0 && e !== "longjmp") throw e;
|
||||
@ -4783,10 +4783,10 @@ function invoke_viiiidd(index, a1, a2, a3, a4, a5, a6) {
|
||||
}
|
||||
}
|
||||
|
||||
function invoke_viiiiiiiiiiiiii(index, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14) {
|
||||
function invoke_viiiidd(index, a1, a2, a3, a4, a5, a6) {
|
||||
var sp = stackSave();
|
||||
try {
|
||||
getWasmTableEntry(index)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14);
|
||||
getWasmTableEntry(index)(a1, a2, a3, a4, a5, a6);
|
||||
} catch (e) {
|
||||
stackRestore(sp);
|
||||
if (e !== e + 0 && e !== "longjmp") throw e;
|
||||
|
||||
Binary file not shown.
@ -3893,7 +3893,7 @@ var asmLibraryArg = {
|
||||
"y": ___cxa_begin_catch,
|
||||
"wb": ___cxa_current_primary_exception,
|
||||
"Ja": ___cxa_decrement_exception_refcount,
|
||||
"F": ___cxa_end_catch,
|
||||
"G": ___cxa_end_catch,
|
||||
"b": ___cxa_find_matching_catch_2,
|
||||
"n": ___cxa_find_matching_catch_3,
|
||||
"C": ___cxa_find_matching_catch_4,
|
||||
@ -3970,7 +3970,7 @@ var asmLibraryArg = {
|
||||
"g": invoke_vii,
|
||||
"l": invoke_viid,
|
||||
"lb": invoke_viidf,
|
||||
"G": invoke_viididii,
|
||||
"F": invoke_viididii,
|
||||
"bb": invoke_viidiiid,
|
||||
"W": invoke_viidiiiii,
|
||||
"Pa": invoke_viifff,
|
||||
@ -3990,7 +3990,7 @@ var asmLibraryArg = {
|
||||
"pa": invoke_viiifii,
|
||||
"k": invoke_viiii,
|
||||
"M": invoke_viiiid,
|
||||
"db": invoke_viiiidd,
|
||||
"cb": invoke_viiiidd,
|
||||
"kb": invoke_viiiidi,
|
||||
"ya": invoke_viiiidid,
|
||||
"Ba": invoke_viiiidiidiiiiiii,
|
||||
@ -4025,7 +4025,7 @@ var asmLibraryArg = {
|
||||
"ga": invoke_viiiiiiiiiifii,
|
||||
"O": invoke_viiiiiiiiiii,
|
||||
"wa": invoke_viiiiiiiiiiiii,
|
||||
"cb": invoke_viiiiiiiiiiiiii,
|
||||
"db": invoke_viiiiiiiiiiiiii,
|
||||
"ba": invoke_viiiiiiiiiiiiiii,
|
||||
"Ta": invoke_viiiiiiiiiiiiiiii,
|
||||
"gb": invoke_viiiiiiiiiiiiiiiiiiidi,
|
||||
@ -4233,17 +4233,6 @@ function invoke_vii(index, a1, a2) {
|
||||
}
|
||||
}
|
||||
|
||||
function invoke_i(index) {
|
||||
var sp = stackSave();
|
||||
try {
|
||||
return getWasmTableEntry(index)();
|
||||
} catch (e) {
|
||||
stackRestore(sp);
|
||||
if (e !== e + 0 && e !== "longjmp") throw e;
|
||||
_setThrew(1, 0);
|
||||
}
|
||||
}
|
||||
|
||||
function invoke_iifii(index, a1, a2, a3, a4) {
|
||||
var sp = stackSave();
|
||||
try {
|
||||
@ -4277,6 +4266,17 @@ function invoke_viiid(index, a1, a2, a3, a4) {
|
||||
}
|
||||
}
|
||||
|
||||
function invoke_i(index) {
|
||||
var sp = stackSave();
|
||||
try {
|
||||
return getWasmTableEntry(index)();
|
||||
} catch (e) {
|
||||
stackRestore(sp);
|
||||
if (e !== e + 0 && e !== "longjmp") throw e;
|
||||
_setThrew(1, 0);
|
||||
}
|
||||
}
|
||||
|
||||
function invoke_viidf(index, a1, a2, a3, a4) {
|
||||
var sp = stackSave();
|
||||
try {
|
||||
@ -4772,10 +4772,10 @@ function invoke_viiiiiiiiiddi(index, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a1
|
||||
}
|
||||
}
|
||||
|
||||
function invoke_viiiidd(index, a1, a2, a3, a4, a5, a6) {
|
||||
function invoke_viiiiiiiiiiiiii(index, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14) {
|
||||
var sp = stackSave();
|
||||
try {
|
||||
getWasmTableEntry(index)(a1, a2, a3, a4, a5, a6);
|
||||
getWasmTableEntry(index)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14);
|
||||
} catch (e) {
|
||||
stackRestore(sp);
|
||||
if (e !== e + 0 && e !== "longjmp") throw e;
|
||||
@ -4783,10 +4783,10 @@ function invoke_viiiidd(index, a1, a2, a3, a4, a5, a6) {
|
||||
}
|
||||
}
|
||||
|
||||
function invoke_viiiiiiiiiiiiii(index, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14) {
|
||||
function invoke_viiiidd(index, a1, a2, a3, a4, a5, a6) {
|
||||
var sp = stackSave();
|
||||
try {
|
||||
getWasmTableEntry(index)(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14);
|
||||
getWasmTableEntry(index)(a1, a2, a3, a4, a5, a6);
|
||||
} catch (e) {
|
||||
stackRestore(sp);
|
||||
if (e !== e + 0 && e !== "longjmp") throw e;
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user