Viewing is open to everyone. Downloading and starring need a GitHub sign-in.
Sign in with GitHub
image_decode_tests.cpp
v0.1.0-build.85
Latest
Commit ede38f5
Sign in with GitHub to download. Each download is recorded with your GitHub username.
| 1 | "ckey/ckey.h" |
| 2 | |
| 3 | <array> |
| 4 | "check.h" |
| 5 | <cstdint> |
| 6 | <fstream> |
| 7 | <iostream> |
| 8 | <iterator> |
| 9 | <string> |
| 10 | <vector> |
| 11 | |
| 12 | struct Pgm { size_t width, height; std::vector<uint8_t> pixels; }; |
| 13 | |
| 14 | static Pgm load_pgm(const std::string &path, size_t expected_side) { |
| 15 | std::ifstream file(path, std::ios::binary); |
| 16 | CKEY_CHECK(file && "optical image fixture is required"); |
| 17 | std::string magic; size_t width=0,height=0; unsigned maxval=0; |
| 18 | file >> magic >> width >> height >> maxval; |
| 19 | file.get(); |
| 20 | CKEY_CHECK(magic=="P5" && width==expected_side && height==expected_side && maxval==255); |
| 21 | std::vector<uint8_t> pixels((std::istreambuf_iterator<char>(file)),{}); |
| 22 | CKEY_CHECK(pixels.size()==width*height); |
| 23 | return {width,height,std::move(pixels)}; |
| 24 | } |
| 25 | |
| 26 | int main() { |
| 27 | const std::string base=std::string(CKEY_TEST_SOURCE_DIR)+"/tests/vectors/"; |
| 28 | auto fixture=load_pgm(base+"locator-v8-preview.pgm",670); |
| 29 | auto &image=fixture.pixels; const size_t width=fixture.width, height=fixture.height; |
| 30 | |
| 31 | // The PGM is a crop of a browser-rendered v0.8 locator, not a raw generated |
| 32 | // bit matrix. The symbol's 31-unit SVG canvas maps canonical [0,29]^2 to |
| 33 | // [1,30]^2 within this 670 px crop. |
| 34 | const double unit=static_cast<double>(width)/31.0; |
| 35 | const double x0=unit, x1=30.0*unit; |
| 36 | const double quad[8]={x0,x0,x1,x0,x1,x1,x0,x1}; |
| 37 | constexpr std::array<uint8_t,16> expected={0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15}; |
| 38 | std::array<uint8_t,16> decoded{}; ckey_diagnostics diag{}; |
| 39 | CKEY_CHECK(ckey_calibration_score_gray8(image.data(),width,height,width,quad) > 40.0); |
| 40 | CKEY_CHECK(ckey_decode_gray8(image.data(),width,height,width,quad,decoded.data(),decoded.size(),&diag)==CKEY_OK); |
| 41 | CKEY_CHECK(decoded==expected && diag.status==CKEY_OK); |
| 42 | |
| 43 | // This crop is taken from the user's exact reader target image and resized |
| 44 | // to 20 px/module. It verifies this image's marks and framing reach the core. |
| 45 | auto target=load_pgm(base+"locator-v8-reader-target.pgm",580); |
| 46 | const double target_edge=580.0/934.0; |
| 47 | const double target_far=933.0*target_edge; |
| 48 | const double target_quad[8]={target_edge,target_edge,target_far,target_edge, |
| 49 | target_far,target_far,target_edge,target_far}; |
| 50 | decoded.fill(0); |
| 51 | CKEY_CHECK(ckey_decode_gray8(target.pixels.data(),target.width,target.height,target.width, |
| 52 | target_quad,decoded.data(),decoded.size(),&diag)==CKEY_OK); |
| 53 | CKEY_CHECK(decoded==expected); |
| 54 | |
| 55 | // Regression fixture cropped from the user's iPhone camera preview while |
| 56 | // viewing the CKey on a laptop display. It includes screen rasterization, |
| 57 | // camera blur, and slight projective skew instead of a generated bitmap. |
| 58 | auto camera=load_pgm(base+"locator-v8-phone-camera.pgm",500); |
| 59 | const double camera_quad[8]={24,24,462,24,462,450,24,450}; |
| 60 | decoded.fill(0); |
| 61 | CKEY_CHECK(ckey_decode_gray8(camera.pixels.data(),camera.width,camera.height,camera.width, |
| 62 | camera_quad,decoded.data(),decoded.size(),&diag)==CKEY_OK); |
| 63 | CKEY_CHECK(decoded==expected); |
| 64 | |
| 65 | // Far-distance phone preview: the target is only about 135 px across |
| 66 | // (~4.7 px/module), but still decodes when localization supplies its field. |
| 67 | auto far_camera=load_pgm(base+"locator-v8-far-camera.pgm",180); |
| 68 | const double far_quad[8]={14,14,149,14,149,149,14,149}; |
| 69 | decoded.fill(0); |
| 70 | CKEY_CHECK(ckey_decode_gray8(far_camera.pixels.data(),far_camera.width,far_camera.height, |
| 71 | far_camera.width,far_quad,decoded.data(),decoded.size(),&diag)==CKEY_OK); |
| 72 | CKEY_CHECK(decoded==expected); |
| 73 | |
| 74 | // A rotated corner order and inverted monochrome polarity are both valid. |
| 75 | const double rotated[8]={x1,x0,x1,x1,x0,x1,x0,x0}; |
| 76 | for (uint8_t &pixel : image) pixel=static_cast<uint8_t>(255-pixel); |
| 77 | decoded.fill(0); |
| 78 | CKEY_CHECK(ckey_decode_gray8(image.data(),width,height,width,rotated,decoded.data(),decoded.size(),&diag)==CKEY_OK); |
| 79 | CKEY_CHECK(decoded==expected); |
| 80 | |
| 81 | const double invalid[8]={1,1,2,2,3,3,4,4}; |
| 82 | CKEY_CHECK(ckey_decode_gray8(image.data(),width,height,width,invalid,decoded.data(),decoded.size(),&diag)==CKEY_INVALID_ARGUMENT); |
| 83 | CKEY_CHECK(ckey_calibration_score_extended_gray8(nullptr,width,height,width,target_quad) < 0.0); |
| 84 | std::cout << "CKey optical image decode tests passed\n"; |
| 85 | } |