diff options
Diffstat (limited to 'svg-flatten')
-rw-r--r-- | svg-flatten/src/nopencv.cpp | 53 | ||||
-rw-r--r-- | svg-flatten/src/nopencv.hpp | 24 | ||||
-rw-r--r-- | svg-flatten/src/nopencv_test.cpp | 74 |
3 files changed, 95 insertions, 56 deletions
diff --git a/svg-flatten/src/nopencv.cpp b/svg-flatten/src/nopencv.cpp index 2384fcc..18d3f20 100644 --- a/svg-flatten/src/nopencv.cpp +++ b/svg-flatten/src/nopencv.cpp @@ -2,6 +2,9 @@ #include <iostream> #include <iomanip> +#define STB_IMAGE_IMPLEMENTATION +#include "stb_image.h" + #include "nopencv.hpp" using namespace gerbolyze; @@ -374,3 +377,53 @@ ContourCallback gerbolyze::nopencv::simplify_contours_teh_chin(ContourCallback c }; } + +gerbolyze::nopencv::Image32::Image32(int size_x, int size_y, const int32_t *data) { + assert(size_x > 0 && size_x < 100000); + assert(size_y > 0 && size_y < 100000); + m_data = new int32_t[size_x * size_y] { 0 }; + m_rows = size_y; + m_cols = size_x; + if (data != nullptr) { + memcpy(m_data, data, sizeof(int32_t) * size_x * size_y); + } +} + +bool gerbolyze::nopencv::Image32::load(const char *filename) { + return stb_to_internal(stbi_load(filename, &m_cols, &m_rows, nullptr, 1)); +} + +bool gerbolyze::nopencv::Image32::load_memory(uint8_t *buf, size_t len) { + return stb_to_internal(stbi_load_from_memory(buf, len, &m_cols, &m_rows, nullptr, 1)); +} + +void gerbolyze::nopencv::Image32::binarize() { + assert(m_data != nullptr); + assert(m_rows > 0 && m_cols > 0); + + for (int y=0; y<m_rows; y++) { + for (int x=0; x<m_cols; x++) { + m_data[y*m_cols + x] = m_data[y*m_cols + x] > 0; + } + } +} + +bool gerbolyze::nopencv::Image32::stb_to_internal(uint8_t *data) { + if (data == nullptr) + return false; + + if (m_rows < 0 || m_rows > 100000) + return false; + if (m_cols < 0 || m_cols > 100000) + return false; + + m_data = new int32_t[size()] { 0 }; + for (int y=0; y<m_rows; y++) { + for (int x=0; x<m_cols; x++) { + m_data[y*m_cols + x] = data[y*m_cols + x]; + } + } + + stbi_image_free(data); + return true; +} diff --git a/svg-flatten/src/nopencv.hpp b/svg-flatten/src/nopencv.hpp index 9be2434..51a068a 100644 --- a/svg-flatten/src/nopencv.hpp +++ b/svg-flatten/src/nopencv.hpp @@ -41,23 +41,20 @@ namespace gerbolyze { class Image32 { public: - Image32(int size_x, int size_y, const int32_t *data=nullptr) { - assert(size_x > 0 && size_x < 100000); - assert(size_y > 0 && size_y < 100000); - m_data = new int32_t[size_x * size_y] { 0 }; - m_rows = size_y; - m_cols = size_x; - if (data != nullptr) { - memcpy(m_data, data, sizeof(int32_t) * size_x * size_y); - } - } - + Image32() {} + Image32(int size_x, int size_y, const int32_t *data=nullptr); Image32(const Image32 &other) : Image32(other.cols(), other.rows(), other.ptr()) {} ~Image32() { - delete m_data; + if (m_data) { + delete m_data; + } } + bool load(const char *filename); + bool load_memory(uint8_t *buf, size_t len); + void binarize(); + int32_t &at(int x, int y) { assert(x >= 0 && y >= 0 && x < m_cols && y < m_rows); assert(m_data != nullptr); @@ -93,9 +90,12 @@ namespace gerbolyze { int rows() const { return m_rows; } int cols() const { return m_cols; } + int size() const { return m_cols*m_rows; } const int32_t *ptr() const { return m_data; } private: + bool stb_to_internal(uint8_t *data); + int32_t *m_data = nullptr; int m_rows=0, m_cols=0; }; diff --git a/svg-flatten/src/nopencv_test.cpp b/svg-flatten/src/nopencv_test.cpp index 389de60..902aa18 100644 --- a/svg-flatten/src/nopencv_test.cpp +++ b/svg-flatten/src/nopencv_test.cpp @@ -10,7 +10,6 @@ #include <subprocess.h> #include <minunit.h> -#define STB_IMAGE_IMPLEMENTATION #include "stb_image.h" using namespace gerbolyze; @@ -180,42 +179,35 @@ int render_svg(const char *in_svg, const char *out_png) { } static void testdata_roundtrip(const char *fn) { - int x, y; - uint8_t *data = stbi_load(fn, &x, &y, nullptr, 1); - Image32 ref_img(x, y); - for (int cy=0; cy<y; cy++) { - for (int cx=0; cx<x; cx++) { - ref_img.at(cx, cy) = data[cy*x + cx] / 255; - } - } - stbi_image_free(data); + Image32 ref_img; + mu_assert(ref_img.load(fn), "Input image failed to load"); + ref_img.binarize(); Image32 ref_img_copy(ref_img); TempfileHack tmp_svg(".svg"); TempfileHack tmp_png(".png"); - SVGPolyRenderer ctx(tmp_svg.c_str(), x, y); + SVGPolyRenderer ctx(tmp_svg.c_str(), ref_img.cols(), ref_img.rows()); gerbolyze::nopencv::find_blobs(ref_img, ctx.callback()); ctx.close(); mu_assert_int_eq(0, render_svg(tmp_svg.c_str(), tmp_png.c_str())); - int out_x, out_y; - uint8_t *out_data = stbi_load(tmp_png.c_str(), &out_x, &out_y, nullptr, 1); - mu_assert_int_eq(x, out_x); - mu_assert_int_eq(y, out_y); - - for (int cy=0; cy<y; cy++) { - for (int cx=0; cx<x; cx++) { - int actual = out_data[cy*x + cx]; - int expected = ref_img_copy.at(cx, cy)*255; - if (actual != expected) { - snprintf(msg, sizeof(msg), "%s: Result does not match input @(%d, %d): %d != %d\n", fn, cx, cy, actual, expected); + Image32 out_img; + mu_assert(out_img.load(tmp_png.c_str()), "Output image failed to load"); + out_img.binarize(); + + mu_assert_int_eq(ref_img.cols(), out_img.cols()); + mu_assert_int_eq(ref_img.rows(), out_img.rows()); + + for (int y=0; y<out_img.rows(); y++) { + for (int x=0; x<out_img.cols(); x++) { + if (out_img.at(x, y) != ref_img_copy.at(x, y)) { + snprintf(msg, sizeof(msg), "%s: Result does not match input @(%d, %d): %d != %d\n", fn, x, y, out_img.at(x, y), ref_img_copy.at(x, y)); mu_fail(msg); } } } - stbi_image_free(out_data); } MU_TEST(test_round_trip_blank) { testdata_roundtrip("testdata/blank.png"); } @@ -237,45 +229,39 @@ MU_TEST(test_round_trip_two_px_inv) { testdata_roundtrip("testdata/two-p static void chain_approx_test(const char *fn) { //cout << endl << "Testing \"" << fn << "\"" << endl; - int w, h; - uint8_t *data = stbi_load(fn, &w, &h, nullptr, 1); - Image32 ref_img(w, h); - for (int y=0; y<h; y++) { - for (int x=0; x<w; x++) { - ref_img.at(x, y) = data[y*w + x] / 255; - } - } + Image32 ref_img; + mu_assert(ref_img.load(fn), "Input image failed to load"); + ref_img.binarize(); + Image32 ref_img_copy(ref_img); TempfileHack tmp_svg(".svg"); TempfileHack tmp_png(".png"); - SVGPolyRenderer ctx(tmp_svg.c_str(), w, h); + SVGPolyRenderer ctx(tmp_svg.c_str(), ref_img.cols(), ref_img.rows()); gerbolyze::nopencv::find_blobs(ref_img, simplify_contours_teh_chin(ctx.callback())); ctx.close(); - mu_assert_int_eq(0, render_svg(tmp_svg.c_str(), "/tmp/out.png")); + mu_assert_int_eq(0, render_svg(tmp_svg.c_str(), tmp_png.c_str())); - int out_w, out_h; - uint8_t *out_data = stbi_load("/tmp/out.png", &out_w, &out_h, nullptr, 1); - mu_assert_int_eq(w, out_w); - mu_assert_int_eq(h, out_h); + Image32 out_img; + mu_assert(out_img.load(tmp_png.c_str()), "Output image failed to load"); + mu_assert_int_eq(ref_img.rows(), out_img.rows()); + mu_assert_int_eq(ref_img.cols(), out_img.cols()); double max_abs_deviation = 0; double rms_sum = 0; double mean_sum = 0; - for (int y=0; y<h; y++) { - for (int x=0; x<w; x++) { - double delta = fabs((double)out_data[y*w + x] - (double)data[y*w + x]) / 255.0; + for (int y=0; y<out_img.rows(); y++) { + for (int x=0; x<out_img.cols(); x++) { + double delta = fabs((double)out_img.at(x, y)/255.0 - (double)ref_img_copy.at(x, y)); max_abs_deviation = fmax(max_abs_deviation, delta); rms_sum += delta*delta; mean_sum += delta; } } - stbi_image_free(data); - stbi_image_free(out_data); - rms_sum = sqrt(rms_sum / (w*h)); - mean_sum /= w*h; + rms_sum = sqrt(rms_sum / out_img.size()); + mean_sum /= out_img.size(); if (rms_sum > 0.5) { snprintf(msg, sizeof(msg), "%s: Chain approximation RMS error is above threshold: %.3f > 0.5\n", fn, rms_sum); mu_fail(msg); |