11#include <unordered_map>
36 int rowstride =
buf->get_rowstride();
37 int nchannels =
buf->get_n_channels();
41 for (
int y = 0; y <
height; y++) {
42 auto p =
data + rowstride * y;
43 for (
int x = 0; x <
width; x++) {
47 uint32_t a = nchannels == 3 ? 255 : p[3];
59 auto buf = Gdk::Pixbuf::create(Gdk::Colorspace::RGB,
true, 8,
width,
height);
61 int rowstride =
buf->get_rowstride();
62 int nchannels =
buf->get_n_channels();
65 for (
int y = 0; y <
height; y++) {
66 auto p =
data + rowstride * y;
67 for (
int x = 0; x <
width; x++) {
69 p[0] = (
rgb >> 16) & 0xff;
70 p[1] = (
rgb >> 8) & 0xff;
72 p[3] = (
rgb >> 24) & 0xff;
82 auto f = std::fopen(filename,
"wb");
89 for (
int y = 0; y <
height; y++) {
90 for (
int x = 0; x <
width; x++) {
92 uint8_t r = (
rgb >> 16) & 0xff;
93 uint8_t g = (
rgb >> 8) & 0xff;
94 uint8_t b = (
rgb ) & 0xff;
127void apply_adjacent(
float *cm,
int xres,
int yres, F f)
129 for (
int y = 0; y < yres; y++) {
130 for (
int x = 0; x < xres - 1; x++) {
131 int idx = y * xres + x;
132 f(cm[idx], cm[idx + 1]);
135 for (
int y = 0; y < yres; y++) {
136 for (
int x = xres - 1; x >= 1; x--) {
137 int idx = y * xres + x;
138 f(cm[idx], cm[idx - 1]);
141 for (
int y = 0; y < yres - 1; y++) {
142 for (
int x = 0; x < xres; x++) {
143 int idx = y * xres + x;
144 f(cm[idx], cm[idx + xres]);
147 for (
int y = yres - 1; y >= 1; y--) {
148 for (
int x = 0; x < xres; x++) {
149 int idx = y * xres + x;
150 f(cm[idx], cm[idx - xres]);
160void dilate(
float *cm,
int xres,
int yres)
162 apply_adjacent(cm, xres, yres, [] (
float &a,
float b) {
172void erode(
float *cm,
int xres,
int yres)
174 apply_adjacent(cm, xres, yres, [] (
float &a,
float b) {
184void premultiplyMatrix(
float alpha,
float *cm,
int cmSize)
186 for (
int i = 0; i < cmSize; i++) {
194void normalizeMatrix(
float *cm,
int cmSize)
197 for (
int i = 0; i < cmSize; i++) {
203 if (max <= 0.0f || max == 1.0f) {
207 float alpha = 1.0f /
max;
208 premultiplyMatrix(alpha, cm, cmSize);
217void smooth(
float *cm,
int xres,
int yres,
float f1,
float f2,
float f3)
219 for (
int y = 0; y < yres; y++) {
220 for (
int x = 0; x < xres - 2; x++) {
221 int idx = y * xres + x;
222 cm[idx] = f1 * cm[idx] + f2 * cm[idx + 1] + f3 * cm[idx + 2];
225 for (
int y = 0; y < yres; y++) {
226 for (
int x = xres - 1; x >= 2; x--) {
227 int idx = y * xres + x;
228 cm[idx] = f3 * cm[idx - 2] + f2 * cm[idx - 1] + f1 * cm[idx];
231 for (
int y = 0; y < yres - 2; y++) {
232 for (
int x = 0; x < xres; x++) {
233 int idx = y * xres + x;
234 cm[idx] = f1 * cm[idx] + f2 * cm[((y + 1) * xres) + x] + f3 * cm[((y + 2) * xres) + x];
237 for (
int y = yres - 1; y >= 2; y--) {
238 for (
int x = 0; x < xres; x++) {
239 int idx = y * xres + x;
240 cm[idx] = f3 * cm[((y - 2) * xres) + x] + f2 * cm[((y - 1) * xres) + x] + f1 * cm[idx];
248float sqrEuclideanDist(
float *p,
int pSize,
float *q)
251 for (
int i = 0; i < pSize; i++) {
252 float v = p[i] - q[i];
261 : progress(&progress)
270 g_warning(
"Siox error: %s\n",
msg.c_str());
275 g_message(
"Siox: %s\n",
msg.c_str());
294 auto labelField_storage = std::make_unique<int[]>(
pixelCount);
297 trace(
"### Creating signatures");
300 std::vector<CieLab> knownBg, knownFg;
301 auto imageClab = std::make_unique<CieLab[]>(
pixelCount);
304 uint32_t pix =
image[i];
308 knownBg.emplace_back(lab);
310 knownFg.emplace_back(lab);
316 trace(
"knownBg:" + std::to_string(knownBg.size()) +
" knownFg:" + std::to_string(knownFg.size()));
318 std::vector<CieLab> bgSignature;
323 std::vector<CieLab> fgSignature;
328 if (bgSignature.empty()) {
330 error(
"Signature size is < 1. Segmentation is impossible");
338 trace(
"### Analyzing image");
340 std::unordered_map<uint32_t, bool> hs;
345 if (i % progressResolution == 0) {
354 auto [it, inserted] = hs.emplace(
image[i],
false);
356 auto const &lab = imageClab[i];
358 float minBg = std::numeric_limits<float>::max();
359 for (
auto const &s : bgSignature) {
364 if (fgSignature.empty()) {
367 minFg = std::numeric_limits<float>::max();
368 for (
auto const &s : fgSignature) {
373 it->second = minBg < minFg;
376 bool isBackground = it->second;
384 trace(
"### postProcessing");
412 image[i] = backgroundFillColor;
427 negLimits[0] = -
limits[0];
428 negLimits[1] = -
limits[1];
429 negLimits[2] = -
limits[2];
437 unsigned recursionDepth,
438 unsigned *clusterCount,
441 unsigned currentDim = recursionDepth % dims;
442 CieLab point = points[leftBase];
443 float min = point(currentDim);
446 for (
unsigned i = leftBase + 1; i < rightBase; i++) {
448 float curval = point(currentDim);
449 if (curval < min) min = curval;
450 if (curval > max) max = curval;
454 if (max - min >
limits[currentDim]) {
455 float pivotPoint = (min + max) / 2.0;
456 unsigned left = leftBase;
457 unsigned right = rightBase - 1;
462 point = points[left];
463 if (point(currentDim) > pivotPoint) {
469 point = points[right];
470 if (point(currentDim) <= pivotPoint) {
480 point = points[left];
481 points[left] = points[right];
482 points[right] = point;
496 newpoint.
C = rightBase - leftBase;
498 for (; leftBase < rightBase; leftBase++) {
499 newpoint.
add(points[leftBase]);
504 if (newpoint.
C != 0) {
505 newpoint.
mul(1.0f / newpoint.
C);
507 points[*clusterCount] = newpoint;
515 unsigned recursionDepth,
516 unsigned *clusterCount,
520 unsigned currentDim = recursionDepth % dims;
521 CieLab point = points[leftBase];
522 float min = point(currentDim);
525 for (
unsigned i = leftBase+ 1; i < rightBase; i++) {
527 float curval = point(currentDim);
528 if (curval < min) min = curval;
529 if (curval > max) max = curval;
533 if (max - min >
limits[currentDim]) {
534 float pivotPoint = (min + max) / 2.0;
535 unsigned left = leftBase;
536 unsigned right = rightBase - 1;
541 point = points[left];
542 if (point(currentDim) > pivotPoint) {
548 point = points[right];
549 if (point(currentDim) <= pivotPoint) {
559 point = points[left];
560 points[left] = points[right];
561 points[right] = point;
568 colorSignatureStage2(points, leftBase, left, recursionDepth + 1, clusterCount, threshold, dims);
569 colorSignatureStage2(points, left, rightBase, recursionDepth + 1, clusterCount, threshold, dims);
574 for (
unsigned i = leftBase; i < rightBase; i++) {
578 if (
sum >= threshold) {
579 float scale = rightBase - leftBase;
582 for (; leftBase < rightBase; leftBase++) {
583 newpoint.
add(points[leftBase]);
589 points[*clusterCount] = newpoint;
596 std::vector<CieLab> &
result,
599 if (inputVec.empty()) {
603 unsigned length = inputVec.size();
606 unsigned stage1length = 0;
609 unsigned stage2length = 0;
612 result.resize(stage2length);
626 std::vector<int> labelSizes;
631 labelSizes.emplace_back(regionCount);
634 if (regionCount > maxregion) {
635 maxregion = regionCount;
636 maxblob = curlabel-1;
643 if (labelSizes[
labelField[i]] * sizeFactorToKeep < maxregion) {
663 std::vector<int> pixelsToVisit;
664 int componentSize = 0;
666 if (
labelField[startPos] == -1 &&
cm[startPos] >= threshold) {
669 pixelsToVisit.emplace_back(startPos);
672 while (!pixelsToVisit.empty()) {
673 int pos = pixelsToVisit[pixelsToVisit.size() - 1];
674 pixelsToVisit.erase(pixelsToVisit.end() - 1);
680 if (x - 1 >= 0 &&
labelField[left] == -1 &&
cm[left] >= threshold) {
683 pixelsToVisit.emplace_back(left);
690 pixelsToVisit.emplace_back(right);
693 int top = pos -
width;
694 if (y - 1 >= 0 &&
labelField[top] == -1 &&
cm[top] >= threshold) {
697 pixelsToVisit.emplace_back(top);
700 int bottom = pos +
width;
704 pixelsToVisit.emplace_back(bottom);
708 return componentSize;
717 std::vector<int> pixelsToVisit;
723 uint32_t origColor =
image[i];
729 pixelsToVisit.emplace_back(i);
731 while (!pixelsToVisit.empty()) {
732 int pos = pixelsToVisit[pixelsToVisit.size() - 1];
733 pixelsToVisit.erase(pixelsToVisit.end() - 1);
742 pixelsToVisit.emplace_back(left);
749 pixelsToVisit.emplace_back(right);
751 int top = pos -
width;
756 pixelsToVisit.emplace_back(top);
758 int bottom = pos +
width;
763 pixelsToVisit.emplace_back(bottom);
An interface for tasks to report progress and check for cancellation.
void report_or_throw(T const &... progress)
Report a progress value, throwing CancelledException if cancelled.
void add(CieLab const &other)
static float diff(CieLab const &c1, CieLab const &c2)
Computes euclidean distance in CieLab space between two colors.
static float diffSq(CieLab const &c1, CieLab const &c2)
Squared Euclidean distance between two colors in CieLab space.
SioxImage is the input/output format of Siox.
SioxImage(Glib::RefPtr< Gdk::Pixbuf > const &buf)
Create an image from a Gdk::Pixbuf.
std::vector< float > cmdata
Confidence matrix data.
uint32_t * getImageData()
Return the image data buffer.
std::vector< uint32_t > pixdata
Pixel data.
float * getConfidenceData()
Return the confidence data buffer.
int getWidth() const
Return the width of this image.
bool writePPM(char const *filename) const
Save this image as a simple color PPM.
int getHeight() const
Return the height of this image.
int width
Width of the image.
int height
Height of the image.
Glib::RefPtr< Gdk::Pixbuf > getGdkPixbuf() const
Create a Gdk::Pixbuf from this image.
unsigned hash() const
Return an extremely naive but fast hash of the image/confidence map contents.
float * cm
Working image confidence matrix.
void trace(std::string const &str)
Trace logging.
Async::Progress< double > * progress
int * labelField
Markup for image editing.
int height
Height of the image.
static constexpr float UNKNOWN_REGION_CONFIDENCE
Confidence for foreground or background type being equally likely.
void error(std::string const &str)
Error logging.
SioxImage extractForeground(SioxImage const &originalImage, uint32_t backgroundFillColor)
Extract the foreground of the original image, according to the values in the confidence matrix.
int pixelCount
Number of pixels in the image.
float limits[3]
Our signature limits.
Siox(Async::Progress< double > &progress)
void colorSignatureStage2(CieLab *points, unsigned leftBase, unsigned rightBase, unsigned recursionDepth, unsigned *clusters, float threshold, unsigned dims)
Stage 2 of the color signature work.
float clusterSize
Maximum distance of two lab values.
static constexpr float FOREGROUND_CONFIDENCE
Confidence for a region likely being foreground.
static constexpr float BACKGROUND_CONFIDENCE
Confidence for a region likely being background.
int depthFirstSearch(int startPos, float threshold, int curLabel)
void colorSignatureStage1(CieLab *points, unsigned leftBase, unsigned rightBase, unsigned recursionDepth, unsigned *clusters, unsigned dims)
Stage 1 of the color signature work.
void keepOnlyLargeComponents(float threshold, double sizeFactorToKeep)
void init()
Initialize the Siox engine to its 'pristine' state.
static constexpr float CERTAIN_FOREGROUND_CONFIDENCE
Confidence corresponding to a certain foreground region (equals one).
static constexpr float CERTAIN_BACKGROUND_CONFIDENCE
Confidence corresponding to a certain background reagion (equals zero).
int width
Width of the image.
void colorSignature(std::vector< CieLab > const &inputVec, std::vector< CieLab > &result, unsigned dims)
Main color signature method.
uint32_t * image
Working image data.
std::unique_ptr< Magick::Image > image
MultiDegree< n > max(MultiDegree< n > const &p, MultiDegree< n > const &q)
Returns the maximal degree appearing in the two arguments for each variables.
Helper class to stream background task notifications as a series of messages.
double sum(const double alpha[16], const double &x, const double &y)
Glib::RefPtr< Gtk::Adjustment > smooth