jfjoch_viewer: Auto foreground comes from integer histogram - it is much faster compared to previous implementation
This commit is contained in:
+25
-41
@@ -50,82 +50,66 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
class FloatHistogram {
|
||||
class Histogram {
|
||||
std::vector<uint64_t> count;
|
||||
int64_t total_count = 0;
|
||||
mutable std::mutex m;
|
||||
float min;
|
||||
float max;
|
||||
float spacing;
|
||||
int32_t total_count = 0;
|
||||
int32_t max_bin = 0;
|
||||
|
||||
public:
|
||||
explicit FloatHistogram(size_t bins, float in_min, float in_max) : count(bins), min(in_min), max(in_max) {
|
||||
if (bins == 0)
|
||||
throw JFJochException(JFJochExceptionCategory::InputParameterBelowMin,
|
||||
"FloatHistogram neds to have non-zero bins");
|
||||
spacing = (max - min) / bins;
|
||||
}
|
||||
explicit Histogram(size_t bins) : count(bins) {}
|
||||
|
||||
void Add(float val) {
|
||||
std::unique_lock ul(m);
|
||||
|
||||
size_t bin = (val - min) / spacing;
|
||||
|
||||
if (bin < count.size())
|
||||
count[bin] += 1;
|
||||
++total_count;
|
||||
void Add(int32_t val) {
|
||||
if (val > 0 && val < count.size()) {
|
||||
count[val] += 1;
|
||||
if (val > max_bin)
|
||||
max_bin = val;
|
||||
++total_count;
|
||||
}
|
||||
}
|
||||
|
||||
void clear() {
|
||||
std::unique_lock ul(m);
|
||||
for (auto &c: count) c = 0;
|
||||
total_count = 0;
|
||||
max_bin = 0;
|
||||
}
|
||||
|
||||
MultiLinePlot GetPlot() const {
|
||||
std::unique_lock ul(m);
|
||||
|
||||
MultiLinePlotStruct plot;
|
||||
plot.x.resize(count.size());
|
||||
plot.y.resize(count.size());
|
||||
for (int i = 0; i < count.size(); i++) {
|
||||
plot.x[i] = min + (i + 0.5f) * spacing;
|
||||
plot.y[i] = static_cast<float>(count[i]);
|
||||
}
|
||||
MultiLinePlot ret;
|
||||
ret.AddPlot(plot);
|
||||
[[nodiscard]] std::vector<float> GetCount() const {
|
||||
std::vector<float> ret;
|
||||
ret.reserve(max_bin + 1);
|
||||
for (size_t i = 0; i < max_bin + 1; i++)
|
||||
ret.emplace_back(count[i]);
|
||||
return ret;
|
||||
}
|
||||
|
||||
uint64_t TotalCount() const {
|
||||
std::unique_lock ul(m);
|
||||
[[nodiscard]] uint64_t GetTotalCount() const {
|
||||
return total_count;
|
||||
}
|
||||
|
||||
// Returns the value x such that approximately `percent`% of samples are <= x.
|
||||
// - percent must be in [0, 100]
|
||||
// - returns std::nullopt if histogram is empty
|
||||
std::optional<float> Percentile(float percent) const {
|
||||
[[nodiscard]] std::optional<int32_t> Percentile(float percent) const {
|
||||
if (!std::isfinite(percent) || percent < 0.0f || percent > 100.0f) {
|
||||
throw JFJochException(JFJochExceptionCategory::InputParameterBelowMin,
|
||||
"FloatHistogram Percentile expects percent in [0, 100]");
|
||||
}
|
||||
|
||||
std::unique_lock ul(m);
|
||||
if (total_count == 0)
|
||||
return std::nullopt;
|
||||
|
||||
// Target rank in [0, total-1]
|
||||
const double q = static_cast<double>(percent) / 100.0;
|
||||
const auto target = static_cast<uint64_t>(std::floor(q * static_cast<double>(total_count - 1)));
|
||||
const auto target = static_cast<int64_t>(std::floor(q * static_cast<double>(total_count - 1)));
|
||||
|
||||
uint64_t cumulative = 0;
|
||||
for (size_t i = 0; i < count.size(); i++) {
|
||||
for (int64_t i = 0; i < max_bin + 1; i++) {
|
||||
cumulative += count[i];
|
||||
if (target < cumulative && count[i] > 0)
|
||||
return min + static_cast<float>(i) * spacing + 0.5f * spacing;
|
||||
return i;
|
||||
}
|
||||
|
||||
// If due to rounding we didn't return inside the loop, clamp to the last bin's upper edge.
|
||||
return max;
|
||||
return count.size() - 1;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -8,12 +8,13 @@
|
||||
|
||||
#include <queue>
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
|
||||
JFJochReaderImage::JFJochReaderImage(const DataMessage &in_message,
|
||||
const std::shared_ptr<const JFJochReaderDataset> &in_dataset)
|
||||
: message(in_message),
|
||||
dataset(in_dataset),
|
||||
image(in_dataset->experiment.GetPixelsNum(), 0){
|
||||
image(in_dataset->experiment.GetPixelsNum(), 0) {
|
||||
ProcessInputImage(in_message.image);
|
||||
|
||||
message.image = CompressedImage(image, in_dataset->experiment.GetXPixelsNum(),
|
||||
@@ -124,13 +125,12 @@ void JFJochReaderImage::ProcessInputImage(const void *data, size_t npixel, int64
|
||||
valid_max = std::max(valid_max, val);
|
||||
}
|
||||
valid_count++;
|
||||
|
||||
count_histogram.Add(val);
|
||||
top_pixels_acc.Add(val, static_cast<int32_t>(i));
|
||||
}
|
||||
}
|
||||
|
||||
// For now: auto-foreground based purely on max valid element
|
||||
auto_foreground = has_valid ? std::max(1, valid_max) : 10;
|
||||
auto_foreground = count_histogram.Percentile(99.9).value_or(10);
|
||||
|
||||
// Export top pixels (already sorted descending) into the existing vector interface
|
||||
for (int i = 0; i < top_pixels_acc.Size(); i++) {
|
||||
@@ -139,12 +139,6 @@ void JFJochReaderImage::ProcessInputImage(const void *data, size_t npixel, int64
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void JFJochReaderImage::CalcAutoContrast() {
|
||||
// Now simplified: based on max element (valid_max)
|
||||
auto_foreground = has_valid ? std::max(1, valid_max) : 10;
|
||||
}
|
||||
|
||||
std::optional<std::pair<int32_t, int32_t>> JFJochReaderImage::ValidMinMax() const {
|
||||
if (!has_valid)
|
||||
return {};
|
||||
@@ -200,6 +194,7 @@ void JFJochReaderImage::AddImage(const JFJochReaderImage &other) {
|
||||
top_pixels_acc.Clear();
|
||||
top_pixels.clear();
|
||||
top_pixels.reserve(top_pixels_acc.Capacity());
|
||||
count_histogram.clear();
|
||||
|
||||
for (size_t i = 0; i < image.size(); i++) {
|
||||
if (image[i] == GAP_PXL_VALUE || other.image[i] == GAP_PXL_VALUE) {
|
||||
@@ -231,14 +226,13 @@ void JFJochReaderImage::AddImage(const JFJochReaderImage &other) {
|
||||
valid_max = std::max(valid_max, val);
|
||||
}
|
||||
valid_count++;
|
||||
|
||||
count_histogram.Add(val);
|
||||
top_pixels_acc.Add(val, static_cast<int32_t>(i));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// For now: auto-foreground based purely on max valid element
|
||||
auto_foreground = has_valid ? std::max(1, valid_max) : 10;
|
||||
auto_foreground = count_histogram.Percentile(99.9).value_or(10);
|
||||
|
||||
for (int i = 0; i < top_pixels_acc.Size(); i++) {
|
||||
const auto &e = top_pixels_acc[i];
|
||||
@@ -281,3 +275,7 @@ std::shared_ptr<JFJochReaderDataset> JFJochReaderImage::CreateMutableDataset() {
|
||||
int32_t JFJochReaderImage::GetAutoContrastValue() const {
|
||||
return auto_foreground;
|
||||
}
|
||||
|
||||
std::vector<float> JFJochReaderImage::GetHistogram() const {
|
||||
return count_histogram.GetCount();
|
||||
}
|
||||
|
||||
@@ -40,7 +40,7 @@ class JFJochReaderImage {
|
||||
std::vector<std::pair<int32_t, int32_t>> top_pixels;
|
||||
|
||||
// This histogram operates in square root of count from 0 to 10^20
|
||||
FloatHistogram count_histogram{2048, 0.0f, 1000.0};
|
||||
Histogram count_histogram{100000};
|
||||
|
||||
constexpr static float auto_foreground_range = 0.001f;
|
||||
int32_t auto_foreground;
|
||||
@@ -71,7 +71,7 @@ public:
|
||||
|
||||
int32_t GetAutoContrastValue() const;
|
||||
|
||||
MultiLinePlot GetHistogram() const;
|
||||
std::vector<float> GetHistogram() const;
|
||||
};
|
||||
|
||||
#endif //JFJOCH_JFJOCHREADERIMAGE_H
|
||||
|
||||
+36
-39
@@ -29,61 +29,58 @@ TEST_CASE("SetAverage") {
|
||||
CHECK(p.GetPlots()[0].y[99] == 0.0);
|
||||
}
|
||||
|
||||
TEST_CASE("FloatHistogram") {
|
||||
FloatHistogram h(100, 100.0, 200.0);
|
||||
h.Add(150.5);
|
||||
h.Add(100.2);
|
||||
h.Add(100.9999);
|
||||
h.Add(100.0);
|
||||
h.Add(100.00001);
|
||||
h.Add(101.0);
|
||||
h.Add(200.0);
|
||||
TEST_CASE("Histogram") {
|
||||
Histogram h(200);
|
||||
h.Add(150);
|
||||
h.Add(100);
|
||||
h.Add(100);
|
||||
h.Add(100);
|
||||
h.Add(100);
|
||||
h.Add(101);
|
||||
h.Add(199);
|
||||
h.Add(200); // Outside or range
|
||||
h.Add(-1); // Outside of range
|
||||
|
||||
auto p = h.GetPlot();
|
||||
REQUIRE(p.GetPlots().size() == 1);
|
||||
REQUIRE(p.GetPlots()[0].x.size() == 100);
|
||||
REQUIRE(p.GetPlots()[0].y.size() == 100);
|
||||
|
||||
CHECK(p.GetPlots()[0].x[0] == 100.5);
|
||||
CHECK(p.GetPlots()[0].x[34] == 134.5);
|
||||
CHECK(p.GetPlots()[0].x[99] == 199.5);
|
||||
|
||||
CHECK(p.GetPlots()[0].y[0] == 4.0);
|
||||
CHECK(p.GetPlots()[0].y[1] == 1.0);
|
||||
CHECK(p.GetPlots()[0].y[50] == 1.0);
|
||||
CHECK(p.GetPlots()[0].y[99] == 0.0);
|
||||
CHECK(h.GetTotalCount() == 7);
|
||||
auto p = h.GetCount();
|
||||
REQUIRE(p.size() == 200);
|
||||
CHECK(p[100] == 4);
|
||||
CHECK(p[150] == 1);
|
||||
CHECK(p[199] == 1);
|
||||
CHECK(p[101] == 1);
|
||||
CHECK(p[55] == 0);
|
||||
}
|
||||
|
||||
TEST_CASE("FloatHistogram_Percentile") {
|
||||
FloatHistogram h(111, 89.5, 200.5);
|
||||
TEST_CASE("Histogram_Percentile") {
|
||||
Histogram h(500);
|
||||
for (int i = 0; i < 8; i++)
|
||||
h.Add(100.0f);
|
||||
h.Add(150.0f);
|
||||
h.Add(200.0f);
|
||||
|
||||
CHECK(h.Percentile(0.0f).value_or(-1) == Catch::Approx(100.0f));
|
||||
CHECK(h.Percentile(30.0f).value_or(-1) == Catch::Approx(100.0f));
|
||||
CHECK(h.Percentile(50.0f).value_or(-1) == Catch::Approx(100.0f));
|
||||
CHECK(h.Percentile(80.0f).value_or(-1) == Catch::Approx(100.0f));
|
||||
CHECK(h.Percentile(90.0f).value_or(-1) == Catch::Approx(150.0f));
|
||||
CHECK(h.Percentile(100.0f).value_or(-1) == Catch::Approx(200.0f));
|
||||
CHECK(h.Percentile(0.0f).value_or(-1) == 100);
|
||||
CHECK(h.Percentile(30.0f).value_or(-1) == 100);
|
||||
CHECK(h.Percentile(50.0f).value_or(-1) == 100);
|
||||
CHECK(h.Percentile(80.0f).value_or(-1) == 100);
|
||||
CHECK(h.Percentile(90.0f).value_or(-1) == 150);
|
||||
CHECK(h.Percentile(100.0f).value_or(-1) == 200);
|
||||
|
||||
CHECK(h.TotalCount() == 10);
|
||||
CHECK(h.GetTotalCount() == 10);
|
||||
}
|
||||
|
||||
TEST_CASE("FloatHistogram_Clear") {
|
||||
FloatHistogram h(111, 89.5, 200.5);
|
||||
TEST_CASE("Histogram_Clear") {
|
||||
Histogram h(300);
|
||||
for (int i = 0; i < 8; i++)
|
||||
h.Add(100.0f);
|
||||
h.Add(150.0f);
|
||||
h.Add(200.0f);
|
||||
h.Add(100);
|
||||
h.Add(150);
|
||||
h.Add(200);
|
||||
|
||||
CHECK(h.TotalCount() == 10);
|
||||
CHECK(h.GetTotalCount() == 10);
|
||||
|
||||
h.clear();
|
||||
|
||||
CHECK(h.TotalCount() == 0);
|
||||
CHECK(h.GetTotalCount() == 0);
|
||||
|
||||
h.Add(200.0f);
|
||||
CHECK(h.TotalCount() == 1);
|
||||
CHECK(h.GetTotalCount() == 1);
|
||||
}
|
||||
Reference in New Issue
Block a user