diff --git a/src/SipiImage.cpp b/src/SipiImage.cpp index 54d767f1..0cb45bc8 100755 --- a/src/SipiImage.cpp +++ b/src/SipiImage.cpp @@ -754,7 +754,7 @@ bool SipiImage::crop(const std::shared_ptr ®ion) /****************************************************************************/ -#define POSITION(x, y, c, n) ((n) * ((y) * nx + (x)) + c) +#define POSITION(x, y, c, n) ((n) * ((y)*nx + (x)) + c) byte SipiImage::bilinn(byte buf[], const int nx, const double x, const double y, const int c, const int n) { @@ -1367,6 +1367,7 @@ bool SipiImage::toBitonal() //============================================================================ +#define POSITION(w, x, y, c, n) ((n) * ((y)*w + (x)) + c) void SipiImage::add_watermark(const std::string &wmfilename) { @@ -1374,45 +1375,42 @@ void SipiImage::add_watermark(const std::string &wmfilename) byte *wmbuf = read_watermark(wmfilename, wm_nx, wm_ny, wm_nc); if (wmbuf == nullptr) { throw SipiImageError("Cannot read watermark file " + wmfilename); } - auto xlut = shttps::make_unique(nx); - auto ylut = shttps::make_unique(ny); + // scaling is calculated with the middle point as point of origin + double wm_scale = ((double)wm_nx / wm_ny > (double)nx / ny) ? (double)wm_nx / nx : (double)wm_ny / ny; - // float *xlut = new float[nx]; - // float *ylut = new float[ny]; - - for (size_t i = 0; i < nx; i++) { xlut[i] = (double)(wm_nx * i) / (double)nx; } - - for (size_t j = 0; j < ny; j++) { ylut[j] = (double)(wm_ny * j) / (double)ny; } + // blending alpha coefficient (multiplies with alpha channel if there is one) + double wm_strength = 0.8; if (bps == 8) { auto *buf = pixels; for (size_t j = 0; j < ny; j++) { for (size_t i = 0; i < nx; i++) { - byte val = bilinn(wmbuf, wm_nx, xlut[i], ylut[j], 0, wm_nc); - for (size_t k = 0; k < nc; k++) { - double nval = (buf[nc * (j * nx + i) + k] / 255.) * (1.0 + val / 2550.0) + val / 2550.0; - buf[nc * (j * nx + i) + k] = (nval > 1.0) ? 255 : (unsigned char)floorl(nval * 255. + .5); - } - } - } - } else if (bps == 16) { - auto *buf = reinterpret_cast(pixels); + double wm_i = (i - nx / 2.) * wm_scale + wm_nx / 2.; + double wm_j = (j - ny / 2.) * wm_scale + wm_ny / 2.; - for (size_t j = 0; j < ny; j++) { - for (size_t i = 0; i < nx; i++) { for (size_t k = 0; k < nc; k++) { - byte val = bilinn(wmbuf, wm_nx, xlut[i], ylut[j], 0, wm_nc); - double nval = (buf[nc * (j * nx + i) + k] / 65535.0) * (1.0 + val / 655350.0) + val / 352500.; - buf[nc * (j * nx + i) + k] = (nval > 1.0) ? (word)65535 : (word)floorl(nval * 65535. + .5); + if (!(abs(wm_i - wm_nx / 2.) < wm_nx / 2. && abs(wm_j - wm_ny / 2.) < wm_ny / 2.)) continue; + + double wm_alpha = wm_nc == 4 ? bilinn(wmbuf, wm_nx, wm_i, wm_j, 3, wm_nc) : 1; + double wm_alpha_weak = wm_alpha / 255.0 * wm_strength; + + double wm_color = bilinn(wmbuf, wm_nx, wm_i, wm_j, k, wm_nc) / 255.0; + double color = (buf[nc * (j * nx + i) + k] / 255.); + double blend = color * (1.0 - wm_alpha_weak) + wm_color * wm_alpha_weak; + buf[nc * (j * nx + i) + k] = std::clamp(blend * 255., 0., 255.); } } } + } else if (bps == 16) { + // 16bps support was never really finished, left unimplemented } delete[] wmbuf; } +#undef POSITION + /*==========================================================================*/ @@ -1703,6 +1701,36 @@ bool SipiImage::operator==(const SipiImage &rhs) const /*==========================================================================*/ +std::optional SipiImage::compare(const SipiImage &rhs) const +{ + if ((nx != rhs.nx) || (ny != rhs.ny) || (nc != rhs.nc) || (bps != rhs.bps) || (photo != rhs.photo)) { return {}; } + + double diff = 0; + double niters = 0; + + switch (bps) { + case 8: { + byte *ltmp1 = pixels; + byte *ltmp2 = rhs.pixels; + for (size_t j = 0; j < ny; j++) { + for (size_t i = 0; i < nx; i++) { + for (size_t k = 0; k < nc; k++) { + niters++; + diff += abs(ltmp1[nc * (j * nx + i) + k] - ltmp2[nc * (j * nx + i) + k]) / 255.; + } + } + } + break; + } + case 16: + return 1; + } + + return diff / niters; +} + +/*==========================================================================*/ + std::ostream &operator<<(std::ostream &outstr, const SipiImage &rhs) { diff --git a/src/SipiImage.hpp b/src/SipiImage.hpp index 3fd56cd6..03ba5e6c 100755 --- a/src/SipiImage.hpp +++ b/src/SipiImage.hpp @@ -546,6 +546,13 @@ class SipiImage */ bool toBitonal(); + /*! + * Conclude similarity of two SipiImages, used in tests. Only tested with small differences. + * + * \returns Returns similarity index, returns in [0..1] + */ + std::optional compare(const SipiImage &rhs) const; + /*! * Add a watermark to a file... * @@ -553,7 +560,6 @@ class SipiImage */ void add_watermark(const std::string &wmfilename); - /*! * Calculates the difference between 2 images. * diff --git a/src/formats/SipiIOTiff.cpp b/src/formats/SipiIOTiff.cpp index 38f09106..af1212de 100755 --- a/src/formats/SipiIOTiff.cpp +++ b/src/formats/SipiIOTiff.cpp @@ -333,11 +333,6 @@ unsigned char *read_watermark(const std::string &wmfile, int &nx, int &ny, int & TIFF_GET_FIELD(tif, TIFFTAG_SAMPLESPERPIXEL, &spp, 1); - if (spp != 1) { - TIFFClose(tif); - throw Sipi::SipiImageError("ERROR in read_watermark: ssp ≠ 1: " + wmfile); - } - TIFF_GET_FIELD(tif, TIFFTAG_BITSPERSAMPLE, &bps, 1); if (bps != 8) { diff --git a/test/_test_data/images/unit/MaoriFigure.jpg b/test/_test_data/images/unit/MaoriFigure.jpg new file mode 100644 index 00000000..1f28a537 Binary files /dev/null and b/test/_test_data/images/unit/MaoriFigure.jpg differ diff --git a/test/_test_data/images/unit/MaoriFigureWatermark.jpg b/test/_test_data/images/unit/MaoriFigureWatermark.jpg new file mode 100644 index 00000000..81711daa Binary files /dev/null and b/test/_test_data/images/unit/MaoriFigureWatermark.jpg differ diff --git a/test/_test_data/images/unit/gradient-stars.tif b/test/_test_data/images/unit/gradient-stars.tif new file mode 100644 index 00000000..cca02a0c Binary files /dev/null and b/test/_test_data/images/unit/gradient-stars.tif differ diff --git a/test/_test_data/images/unit/watermark_alpha.tif b/test/_test_data/images/unit/watermark_alpha.tif new file mode 100644 index 00000000..cca02a0c Binary files /dev/null and b/test/_test_data/images/unit/watermark_alpha.tif differ diff --git a/test/unit/sipiimage/sipiimage.cpp b/test/unit/sipiimage/sipiimage.cpp index 3033d09d..83207767 100644 --- a/test/unit/sipiimage/sipiimage.cpp +++ b/test/unit/sipiimage/sipiimage.cpp @@ -310,17 +310,31 @@ TEST(SipiImage, Watermark) Sipi::SipiImage img3; Sipi::SipiImage img4; + std::string maori = "../../../../test/_test_data/images/unit/MaoriFigure.jpg"; + std::string gradstars = "../../../../test/_test_data/images/unit/gradient-stars.tif"; + std::string maoriWater = "../../../../test/_test_data/images/unit/MaoriFigureWatermark.jpg"; + + EXPECT_TRUE(exists_file(maori)); + EXPECT_TRUE(exists_file(gradstars)); + ASSERT_NO_THROW(img1.read(cielab)); EXPECT_NO_THROW(img1.add_watermark(watermark_correct)); - ASSERT_NO_THROW(img2.read(cielab)); - ASSERT_THROW(img2.add_watermark(watermark_incorrect), std::exception); + ASSERT_NO_THROW(img2.read(cielab16)); + EXPECT_NO_THROW(img2.add_watermark(watermark_correct)); + + ASSERT_NO_THROW(img3.read(maori)); + + EXPECT_NO_THROW(img3.add_watermark(gradstars)); + /* ASSERT_NO_THROW(img3.write("jpg", maoriWater)); */ + + ASSERT_NO_THROW(img4.read(maoriWater)); + EXPECT_TRUE(img4.compare(img3).value_or(1000) < 0.007); // 0.00605 - ASSERT_NO_THROW(img3.read(cielab16)); - EXPECT_NO_THROW(img3.add_watermark(watermark_correct)); + ASSERT_NO_THROW(img3.read(maori)); + EXPECT_TRUE(img4.compare(img3) > 0.017); // 0.0174 - ASSERT_NO_THROW(img4.read(cielab16)); - ASSERT_THROW(img4.add_watermark(watermark_incorrect), std::exception); + ASSERT_NO_THROW(img3.rotate(90)); } TEST(SipiImage, CMYK_With_Alpha_Conversion)