Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Watermarks with alpha support and respect to ratio (DEV-4072) #458

Merged
merged 4 commits into from
Oct 8, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
74 changes: 51 additions & 23 deletions src/SipiImage.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -754,7 +754,7 @@


/****************************************************************************/
#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)
{
Expand Down Expand Up @@ -1367,52 +1367,50 @@

//============================================================================

#define POSITION(w, x, y, c, n) ((n) * ((y)*w + (x)) + c)

void SipiImage::add_watermark(const std::string &wmfilename)
{
int wm_nx, wm_ny, wm_nc;
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<double[]>(nx);
auto ylut = shttps::make_unique<double[]>(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<word *>(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

/*==========================================================================*/


Expand Down Expand Up @@ -1703,6 +1701,36 @@

/*==========================================================================*/

std::optional<double> 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;

Check warning on line 1726 in src/SipiImage.cpp

View check run for this annotation

Codecov / codecov/patch

src/SipiImage.cpp#L1725-L1726

Added lines #L1725 - L1726 were not covered by tests
}

return diff / niters;
}

/*==========================================================================*/


std::ostream &operator<<(std::ostream &outstr, const SipiImage &rhs)
{
Expand Down
8 changes: 7 additions & 1 deletion src/SipiImage.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -546,14 +546,20 @@ 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<double> compare(const SipiImage &rhs) const;

/*!
* Add a watermark to a file...
*
* \param[in] wmfilename Path to watermarkfile (which must be a TIFF file at the moment)
*/
void add_watermark(const std::string &wmfilename);


/*!
* Calculates the difference between 2 images.
*
Expand Down
5 changes: 0 additions & 5 deletions src/formats/SipiIOTiff.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down
Binary file added test/_test_data/images/unit/MaoriFigure.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added test/_test_data/images/unit/gradient-stars.tif
Binary file not shown.
Binary file added test/_test_data/images/unit/watermark_alpha.tif
Binary file not shown.
26 changes: 20 additions & 6 deletions test/unit/sipiimage/sipiimage.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down