summaryrefslogtreecommitdiffstats
path: root/third_party/jpeg-xl/lib/jxl/enc_detect_dots.cc
diff options
context:
space:
mode:
Diffstat (limited to 'third_party/jpeg-xl/lib/jxl/enc_detect_dots.cc')
-rw-r--r--third_party/jpeg-xl/lib/jxl/enc_detect_dots.cc154
1 files changed, 84 insertions, 70 deletions
diff --git a/third_party/jpeg-xl/lib/jxl/enc_detect_dots.cc b/third_party/jpeg-xl/lib/jxl/enc_detect_dots.cc
index 4ee8808766..94e6fefb61 100644
--- a/third_party/jpeg-xl/lib/jxl/enc_detect_dots.cc
+++ b/third_party/jpeg-xl/lib/jxl/enc_detect_dots.cc
@@ -26,7 +26,6 @@
#include "lib/jxl/base/status.h"
#include "lib/jxl/convolve.h"
#include "lib/jxl/enc_linalg.h"
-#include "lib/jxl/enc_optimize.h"
#include "lib/jxl/image.h"
#include "lib/jxl/image_ops.h"
@@ -44,14 +43,16 @@ using hwy::HWY_NAMESPACE::Add;
using hwy::HWY_NAMESPACE::Mul;
using hwy::HWY_NAMESPACE::Sub;
-ImageF SumOfSquareDifferences(const Image3F& forig, const Image3F& smooth,
- ThreadPool* pool) {
+StatusOr<ImageF> SumOfSquareDifferences(const Image3F& forig,
+ const Image3F& smooth,
+ ThreadPool* pool) {
const HWY_FULL(float) d;
const auto color_coef0 = Set(d, 0.0f);
const auto color_coef1 = Set(d, 10.0f);
const auto color_coef2 = Set(d, 0.0f);
- ImageF sum_of_squares(forig.xsize(), forig.ysize());
+ JXL_ASSIGN_OR_RETURN(ImageF sum_of_squares,
+ ImageF::Create(forig.xsize(), forig.ysize()));
JXL_CHECK(RunOnPool(
pool, 0, forig.ysize(), ThreadPool::NoInit,
[&](const uint32_t task, size_t thread) {
@@ -142,11 +143,12 @@ const WeightsSeparable5& WeightsSeparable5Gaussian3() {
return weights;
}
-ImageF ComputeEnergyImage(const Image3F& orig, Image3F* smooth,
- ThreadPool* pool) {
+StatusOr<ImageF> ComputeEnergyImage(const Image3F& orig, Image3F* smooth,
+ ThreadPool* pool) {
// Prepare guidance images for dot selection.
- Image3F forig(orig.xsize(), orig.ysize());
- *smooth = Image3F(orig.xsize(), orig.ysize());
+ JXL_ASSIGN_OR_RETURN(Image3F forig,
+ Image3F::Create(orig.xsize(), orig.ysize()));
+ JXL_ASSIGN_OR_RETURN(*smooth, Image3F::Create(orig.xsize(), orig.ysize()));
Rect rect(orig);
const auto& weights1 = WeightsSeparable5Gaussian0_65();
@@ -176,7 +178,7 @@ const size_t kMaxCCSize = 1000;
// Extracts a connected component from a Binary image where seed is part
// of the component
-bool ExtractComponent(ImageF* img, std::vector<Pixel>* pixels,
+bool ExtractComponent(const Rect& rect, ImageF* img, std::vector<Pixel>* pixels,
const Pixel& seed, double threshold) {
static const std::vector<Pixel> neighbors{{1, -1}, {1, 0}, {1, 1}, {0, -1},
{0, 1}, {-1, -1}, {-1, 1}, {1, 0}};
@@ -188,9 +190,9 @@ bool ExtractComponent(ImageF* img, std::vector<Pixel>* pixels,
if (pixels->size() > kMaxCCSize) return false;
for (const Pixel& delta : neighbors) {
Pixel child = current + delta;
- if (child.x >= 0 && static_cast<size_t>(child.x) < img->xsize() &&
- child.y >= 0 && static_cast<size_t>(child.y) < img->ysize()) {
- float* value = &img->Row(child.y)[child.x];
+ if (child.x >= 0 && static_cast<size_t>(child.x) < rect.xsize() &&
+ child.y >= 0 && static_cast<size_t>(child.y) < rect.ysize()) {
+ float* value = &rect.Row(img, child.y)[child.x];
if (*value > threshold) {
*value = 0.0;
q.push_back(child);
@@ -221,7 +223,7 @@ struct ConnectedComponent {
float score;
Pixel mode;
- void CompStats(const ImageF& energy, int extra) {
+ void CompStats(const ImageF& energy, const Rect& rect, int extra) {
maxEnergy = 0.0;
meanEnergy = 0.0;
varEnergy = 0.0;
@@ -234,12 +236,12 @@ struct ConnectedComponent {
for (int sy = -extra; sy < (static_cast<int>(bounds.ysize()) + extra);
sy++) {
int y = sy + static_cast<int>(bounds.y0());
- if (y < 0 || static_cast<size_t>(y) >= energy.ysize()) continue;
- const float* JXL_RESTRICT erow = energy.ConstRow(y);
+ if (y < 0 || static_cast<size_t>(y) >= rect.ysize()) continue;
+ const float* JXL_RESTRICT erow = rect.ConstRow(energy, y);
for (int sx = -extra; sx < (static_cast<int>(bounds.xsize()) + extra);
sx++) {
int x = sx + static_cast<int>(bounds.x0());
- if (x < 0 || static_cast<size_t>(x) >= energy.xsize()) continue;
+ if (x < 0 || static_cast<size_t>(x) >= rect.xsize()) continue;
if (erow[x] > maxEnergy) {
maxEnergy = erow[x];
mode.x = x;
@@ -266,7 +268,10 @@ struct ConnectedComponent {
Rect BoundingRectangle(const std::vector<Pixel>& pixels) {
JXL_ASSERT(!pixels.empty());
- int low_x, high_x, low_y, high_y;
+ int low_x;
+ int high_x;
+ int low_y;
+ int high_y;
low_x = high_x = pixels[0].x;
low_y = high_y = pixels[0].y;
for (const Pixel& p : pixels) {
@@ -278,22 +283,25 @@ Rect BoundingRectangle(const std::vector<Pixel>& pixels) {
return Rect(low_x, low_y, high_x - low_x + 1, high_y - low_y + 1);
}
-std::vector<ConnectedComponent> FindCC(const ImageF& energy, double t_low,
- double t_high, uint32_t maxWindow,
- double minScore) {
+StatusOr<std::vector<ConnectedComponent>> FindCC(const ImageF& energy,
+ const Rect& rect, double t_low,
+ double t_high,
+ uint32_t maxWindow,
+ double minScore) {
const int kExtraRect = 4;
- ImageF img(energy.xsize(), energy.ysize());
+ JXL_ASSIGN_OR_RETURN(ImageF img,
+ ImageF::Create(energy.xsize(), energy.ysize()));
CopyImageTo(energy, &img);
std::vector<ConnectedComponent> ans;
- for (size_t y = 0; y < img.ysize(); y++) {
- float* JXL_RESTRICT row = img.Row(y);
- for (size_t x = 0; x < img.xsize(); x++) {
+ for (size_t y = 0; y < rect.ysize(); y++) {
+ float* JXL_RESTRICT row = rect.Row(&img, y);
+ for (size_t x = 0; x < rect.xsize(); x++) {
if (row[x] > t_high) {
std::vector<Pixel> pixels;
row[x] = 0.0;
bool success = ExtractComponent(
- &img, &pixels, Pixel{static_cast<int>(x), static_cast<int>(y)},
- t_low);
+ rect, &img, &pixels,
+ Pixel{static_cast<int>(x), static_cast<int>(y)}, t_low);
if (!success) continue;
#if JXL_DEBUG_DOT_DETECT
for (size_t i = 0; i < pixels.size(); i++) {
@@ -304,7 +312,7 @@ std::vector<ConnectedComponent> FindCC(const ImageF& energy, double t_low,
Rect bounds = BoundingRectangle(pixels);
if (bounds.xsize() < maxWindow && bounds.ysize() < maxWindow) {
ConnectedComponent cc{bounds, std::move(pixels)};
- cc.CompStats(energy, kExtraRect);
+ cc.CompStats(energy, rect, kExtraRect);
if (cc.score < minScore) continue;
JXL_DEBUG(JXL_DEBUG_DOT_DETECT,
"cc mode: (%d,%d), max: %f, bgMean: %f bgVar: "
@@ -323,12 +331,14 @@ std::vector<ConnectedComponent> FindCC(const ImageF& energy, double t_low,
// TODO(sggonzalez): Adapt this function for the different color spaces or
// remove it if the color space with the best performance does not need it
void ComputeDotLosses(GaussianEllipse* ellipse, const ConnectedComponent& cc,
- const Image3F& img, const Image3F& background) {
+ const Rect& rect, const Image3F& img,
+ const Image3F& background) {
const int rectBounds = 2;
const double kIntensityR = 0.0; // 0.015;
const double kSigmaR = 0.0; // 0.01;
const double kZeroEpsilon = 0.1; // Tolerance to consider a value negative
- double ct = cos(ellipse->angle), st = sin(ellipse->angle);
+ double ct = cos(ellipse->angle);
+ double st = sin(ellipse->angle);
const std::array<double, 3> channelGains{{1.0, 1.0, 1.0}};
int N = 0;
ellipse->l1_loss = 0.0;
@@ -342,15 +352,15 @@ void ComputeDotLosses(GaussianEllipse* ellipse, const ConnectedComponent& cc,
for (int sy = -rectBounds;
sy < (static_cast<int>(cc.bounds.ysize()) + rectBounds); sy++) {
int y = sy + cc.bounds.y0();
- if (y < 0 || static_cast<size_t>(y) >= img.ysize()) continue;
- const float* JXL_RESTRICT row = img.ConstPlaneRow(c, y);
+ if (y < 0 || static_cast<size_t>(y) >= rect.ysize()) continue;
+ const float* JXL_RESTRICT row = rect.ConstPlaneRow(img, c, y);
// bgrow is only used if kOptimizeBackground is false.
// NOLINTNEXTLINE(clang-analyzer-deadcode.DeadStores)
- const float* JXL_RESTRICT bgrow = background.ConstPlaneRow(c, y);
+ const float* JXL_RESTRICT bgrow = rect.ConstPlaneRow(background, c, y);
for (int sx = -rectBounds;
sx < (static_cast<int>(cc.bounds.xsize()) + rectBounds); sx++) {
int x = sx + cc.bounds.x0();
- if (x < 0 || static_cast<size_t>(x) >= img.xsize()) continue;
+ if (x < 0 || static_cast<size_t>(x) >= rect.xsize()) continue;
double target = row[x];
double dotDelta = DotGaussianModel(
x - ellipse->x, y - ellipse->y, ct, st, ellipse->sigma_x,
@@ -385,9 +395,8 @@ void ComputeDotLosses(GaussianEllipse* ellipse, const ConnectedComponent& cc,
ellipse->ridge_loss = ellipse->l2_loss + ridgeTerm;
}
-GaussianEllipse FitGaussianFast(const ConnectedComponent& cc,
- const ImageF& energy, const Image3F& img,
- const Image3F& background) {
+GaussianEllipse FitGaussianFast(const ConnectedComponent& cc, const Rect& rect,
+ const Image3F& img, const Image3F& background) {
constexpr bool leastSqIntensity = true;
constexpr double kEpsilon = 1e-6;
GaussianEllipse ans;
@@ -405,18 +414,18 @@ GaussianEllipse FitGaussianFast(const ConnectedComponent& cc,
"%" PRIuS " %" PRIuS " %" PRIuS " %" PRIuS "\n", cc.bounds.x0(),
cc.bounds.y0(), cc.bounds.xsize(), cc.bounds.ysize());
for (int c = 0; c < 3; c++) {
- color[c] = img.ConstPlaneRow(c, cc.mode.y)[cc.mode.x] -
- background.ConstPlaneRow(c, cc.mode.y)[cc.mode.x];
+ color[c] = rect.ConstPlaneRow(img, c, cc.mode.y)[cc.mode.x] -
+ rect.ConstPlaneRow(background, c, cc.mode.y)[cc.mode.x];
}
double sign = (color[1] > 0) ? 1 : -1;
for (int sy = -kRectBounds; sy <= kRectBounds; sy++) {
int y = sy + cc.mode.y;
- if (y < 0 || static_cast<size_t>(y) >= energy.ysize()) continue;
- const float* JXL_RESTRICT row = img.ConstPlaneRow(1, y);
- const float* JXL_RESTRICT bgrow = background.ConstPlaneRow(1, y);
+ if (y < 0 || static_cast<size_t>(y) >= rect.ysize()) continue;
+ const float* JXL_RESTRICT row = rect.ConstPlaneRow(img, 1, y);
+ const float* JXL_RESTRICT bgrow = rect.ConstPlaneRow(background, 1, y);
for (int sx = -kRectBounds; sx <= kRectBounds; sx++) {
int x = sx + cc.mode.x;
- if (x < 0 || static_cast<size_t>(x) >= energy.xsize()) continue;
+ if (x < 0 || static_cast<size_t>(x) >= rect.xsize()) continue;
double w = std::max(kEpsilon, sign * (row[x] - bgrow[x]));
sum += w;
@@ -426,7 +435,7 @@ GaussianEllipse FitGaussianFast(const ConnectedComponent& cc,
m2[1] += w * x * y;
m2[2] += w * y * y;
for (int c = 0; c < 3; c++) {
- bgColor[c] += background.ConstPlaneRow(c, y)[x];
+ bgColor[c] += rect.ConstPlaneRow(background, c, y)[x];
}
N++;
}
@@ -450,14 +459,16 @@ GaussianEllipse FitGaussianFast(const ConnectedComponent& cc,
ans.intensity[j] = kScaleMult[j] * color[j];
}
- ImageD Sigma(2, 2), D(1, 2), U(2, 2);
- Sigma.Row(0)[0] = m2[0] - m1[0] * m1[0];
- Sigma.Row(1)[1] = m2[2] - m1[1] * m1[1];
- Sigma.Row(0)[1] = Sigma.Row(1)[0] = m2[1] - m1[0] * m1[1];
- ConvertToDiagonal(Sigma, &D, &U);
- const double* JXL_RESTRICT d = D.ConstRow(0);
- const double* JXL_RESTRICT u = U.ConstRow(1);
- int p1 = 0, p2 = 1;
+ Matrix2x2 Sigma;
+ Vector2 d;
+ Matrix2x2 U;
+ Sigma[0][0] = m2[0] - m1[0] * m1[0];
+ Sigma[1][1] = m2[2] - m1[1] * m1[1];
+ Sigma[0][1] = Sigma[1][0] = m2[1] - m1[0] * m1[1];
+ ConvertToDiagonal(Sigma, d, U);
+ Vector2& u = U[1];
+ int p1 = 0;
+ int p2 = 1;
if (d[0] < d[1]) std::swap(p1, p2);
ans.sigma_x = kSigmaMult * d[p1];
ans.sigma_y = kSigmaMult * d[p2];
@@ -466,7 +477,8 @@ GaussianEllipse FitGaussianFast(const ConnectedComponent& cc,
ans.bgColor = bgColor;
if (leastSqIntensity) {
GaussianEllipse* ellipse = &ans;
- double ct = cos(ans.angle), st = sin(ans.angle);
+ double ct = cos(ans.angle);
+ double st = sin(ans.angle);
// Estimate intensity with least squares (fixed background)
for (int c = 0; c < 3; c++) {
double gg = 0.0;
@@ -474,11 +486,11 @@ GaussianEllipse FitGaussianFast(const ConnectedComponent& cc,
int yc = static_cast<int>(cc.mode.y);
int xc = static_cast<int>(cc.mode.x);
for (int y = yc - kRectBounds; y <= yc + kRectBounds; y++) {
- if (y < 0 || static_cast<size_t>(y) >= img.ysize()) continue;
- const float* JXL_RESTRICT row = img.ConstPlaneRow(c, y);
- const float* JXL_RESTRICT bgrow = background.ConstPlaneRow(c, y);
+ if (y < 0 || static_cast<size_t>(y) >= rect.ysize()) continue;
+ const float* JXL_RESTRICT row = rect.ConstPlaneRow(img, c, y);
+ const float* JXL_RESTRICT bgrow = rect.ConstPlaneRow(background, c, y);
for (int x = xc - kRectBounds; x <= xc + kRectBounds; x++) {
- if (x < 0 || static_cast<size_t>(x) >= img.xsize()) continue;
+ if (x < 0 || static_cast<size_t>(x) >= rect.xsize()) continue;
double target = row[x] - bgrow[x];
double gaussian =
DotGaussianModel(x - ellipse->x, y - ellipse->y, ct, st,
@@ -490,13 +502,13 @@ GaussianEllipse FitGaussianFast(const ConnectedComponent& cc,
ans.intensity[c] = gd / (gg + 1e-6); // Regularized least squares
}
}
- ComputeDotLosses(&ans, cc, img, background);
+ ComputeDotLosses(&ans, cc, rect, img, background);
return ans;
}
-GaussianEllipse FitGaussian(const ConnectedComponent& cc, const ImageF& energy,
+GaussianEllipse FitGaussian(const ConnectedComponent& cc, const Rect& rect,
const Image3F& img, const Image3F& background) {
- auto ellipse = FitGaussianFast(cc, energy, img, background);
+ auto ellipse = FitGaussianFast(cc, rect, img, background);
if (ellipse.sigma_x < ellipse.sigma_y) {
std::swap(ellipse.sigma_x, ellipse.sigma_y);
ellipse.angle += kPi / 2.0;
@@ -522,14 +534,16 @@ GaussianEllipse FitGaussian(const ConnectedComponent& cc, const ImageF& energy,
} // namespace
-std::vector<PatchInfo> DetectGaussianEllipses(
- const Image3F& opsin, const GaussianDetectParams& params,
+StatusOr<std::vector<PatchInfo>> DetectGaussianEllipses(
+ const Image3F& opsin, const Rect& rect, const GaussianDetectParams& params,
const EllipseQuantParams& qParams, ThreadPool* pool) {
std::vector<PatchInfo> dots;
- Image3F smooth(opsin.xsize(), opsin.ysize());
- ImageF energy = ComputeEnergyImage(opsin, &smooth, pool);
- std::vector<ConnectedComponent> components = FindCC(
- energy, params.t_low, params.t_high, params.maxWinSize, params.minScore);
+ JXL_ASSIGN_OR_RETURN(Image3F smooth,
+ Image3F::Create(opsin.xsize(), opsin.ysize()));
+ JXL_ASSIGN_OR_RETURN(ImageF energy, ComputeEnergyImage(opsin, &smooth, pool));
+ JXL_ASSIGN_OR_RETURN(std::vector<ConnectedComponent> components,
+ FindCC(energy, rect, params.t_low, params.t_high,
+ params.maxWinSize, params.minScore));
size_t numCC =
std::min(params.maxCC, (components.size() * params.percCC) / 100);
if (components.size() > numCC) {
@@ -541,11 +555,11 @@ std::vector<PatchInfo> DetectGaussianEllipses(
components.erase(components.begin() + numCC, components.end());
}
for (const auto& cc : components) {
- GaussianEllipse ellipse = FitGaussian(cc, energy, opsin, smooth);
+ GaussianEllipse ellipse = FitGaussian(cc, rect, opsin, smooth);
if (ellipse.x < 0.0 ||
- std::ceil(ellipse.x) >= static_cast<double>(opsin.xsize()) ||
+ std::ceil(ellipse.x) >= static_cast<double>(rect.xsize()) ||
ellipse.y < 0.0 ||
- std::ceil(ellipse.y) >= static_cast<double>(opsin.ysize())) {
+ std::ceil(ellipse.y) >= static_cast<double>(rect.ysize())) {
continue;
}
if (ellipse.neg_pixels > params.maxNegPixels) continue;
@@ -573,8 +587,8 @@ std::vector<PatchInfo> DetectGaussianEllipses(
for (size_t x = 0; x < patch.xsize; x++) {
for (size_t c = 0; c < 3; c++) {
patch.fpixels[c][y * patch.xsize + x] =
- opsin.ConstPlaneRow(c, y0 + y)[x0 + x] -
- smooth.ConstPlaneRow(c, y0 + y)[x0 + x];
+ rect.ConstPlaneRow(opsin, c, y0 + y)[x0 + x] -
+ rect.ConstPlaneRow(smooth, c, y0 + y)[x0 + x];
}
}
}