scanner: Check clarity of left mid of qr

This commit is contained in:
Fam Zheng 2025-04-24 22:24:28 +01:00
parent d33836099c
commit 55fd00d35e
8 changed files with 67 additions and 860 deletions

View File

@ -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

View File

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

View File

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

View File

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

View File

@ -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());

View File

@ -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.

View File

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