-
Notifications
You must be signed in to change notification settings - Fork 4
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
Clean background #107
Clean background #107
Conversation
Hi @zdenop . Wrong: #define BW_THRESHOLD_DEF 192 See: #define BW_THRESHOLD_DEF 128 if bwthresdelta = (bgnmode) ? 0 : 64;
bw_threshold = ((bw_threshold + bwthresdelta) < 256) ? (bw_threshold + bwthresdelta) : 255; if git diff >my_changes.patch diff --git a/src/jbig2.cc b/src/jbig2.cc
index 79816d4..343ef24 100644
--- a/src/jbig2.cc
+++ b/src/jbig2.cc
@@ -62,6 +62,7 @@ usage(const char *argv0) {
fprintf(stderr, " -t <threshold>: set classification threshold for symbol coder (def: %0.2f)\n", JBIG2_THRESHOLD_DEF);
fprintf(stderr, " -w <weight>: set classification weight for symbol coder (def: %0.2f)\n", JBIG2_WEIGHT_DEF);
fprintf(stderr, " -T <bw threshold>: set 1 bpp threshold (def: %d)\n", BW_THRESHOLD_DEF);
+ fprintf(stderr, " -N --normalize: backgrond clean/normalize\n");
fprintf(stderr, " -r --refine: use refinement (requires -s: lossless)\n");
fprintf(stderr, " -O <outfile>: dump thresholded image as PNG\n");
fprintf(stderr, " -2: upsample 2x before thresholding\n");
@@ -213,9 +214,11 @@ int
main(int argc, char **argv) {
bool duplicate_line_removal = false;
bool pdfmode = false;
+ bool bgnmode = false;
float threshold = JBIG2_THRESHOLD_DEF;
float weight = JBIG2_WEIGHT_DEF;
int bw_threshold = BW_THRESHOLD_DEF;
+ int bwthresdelta = 0;
bool symbol_mode = false;
bool refine = false;
bool up2 = false, up4 = false;
@@ -363,6 +366,12 @@ main(int argc, char **argv) {
continue;
}
+ if (strcmp(argv[i], "-N") == 0 ||
+ strcmp(argv[i], "--normalize") == 0) {
+ bgnmode = true;
+ continue;
+ }
+
// engage auto thresholding
if (strcmp(argv[i], "--auto-thresh") == 0 ||
strcmp(argv[i], "-a") == 0 ) {
@@ -421,6 +430,9 @@ main(int argc, char **argv) {
struct jbig2ctx *ctx = jbig2_init(threshold, weight, 0, 0, !pdfmode, refine ? 10 : -1);
int pageno = -1;
+ bwthresdelta = (bgnmode) ? 0 : 64;
+ bw_threshold = ((bw_threshold + bwthresdelta) < 256) ? (bw_threshold + bwthresdelta) : 255;
+
int numsubimages=0, subimage=0, num_pages = 0;
while (i < argc) {
if (subimage==numsubimages) {
@@ -456,7 +468,7 @@ main(int argc, char **argv) {
if (verbose)
pixInfo(source, "source image:");
- PIX *pixl, *gray, *pixt;
+ PIX *pixl, *gray, *adapt, *pixt;
if ((pixl = pixRemoveColormap(source, REMOVE_CMAP_BASED_ON_SRC)) == NULL) {
fprintf(stderr, "Failed to remove colormap from %s\n", argv[i]);
return 1;
@@ -474,7 +486,11 @@ main(int argc, char **argv) {
fprintf(stderr, "Unsupported input image depth: %d\n", pixl->d);
return 1;
}
- Pix *adapt = pixBackgroundNormSimple(gray, NULL, NULL);
+ if (bgnmode) {
+ adapt = pixBackgroundNormSimple(gray, NULL, NULL);
+ } else {
+ adapt = pixCleanBackgroundToWhite(gray, NULL, NULL, 1.0, 90, 190);
+ }
pixDestroy(&gray);
if (up2) {
pixt = pixScaleGray2xLIThresh(adapt, bw_threshold); PS: I will not move to the main branch until it has a release with |
I always do PR from other than the main branch. With |
Hi @zdenop . Hmm,,, I can do a non-linear scaling for
if (!bgnmode) {
float bwt = bw_threshold;
bwt = 2.0f * bwt - 1.0f / 255.0f * bwt * bwt;
bw_threshold = (int) (bwt + 0.5f); /* 0.5f for round */
} No |
Correct me if I'm wrong. That's all fine with me, except see below about choosing which type of thresholding. Assuming that's what you want, this doesn't do it. Suggest:
|
@DanBloomberg , the picture is actually like this: Having worked with global, local, hybrid and pre-filtered thresholds, I'm used to expressing the threshold as:
, where The type of threshold where the value is set directly is “slightly” strange for different images without analyzing them. That’s why I initially asked you about the tiled Otsu, which is implemented in your library. Because the threshold in Such are the things. 😄 |
I understand your general approach. We are trying to get something simple and satisfactory for jbig2. Simple both in implementation and in what it does, so the user understands the options and can easily follow the code if they want to. I was fine with your initial reply to allow a threshold value with the adaptive method We should make this simple. Here again is my proposal:
That's it. No deltas required. |
@DanBloomberg , this topic requires further discussion. And testing! In the meantime, sufficient testing has not been carried out, this PR is finished (IMHO), but the discussion is not finished. And for discussion, testing is needed, and it is desirable that this testing be tied to a specific release that you can point your finger at. And then there will be other releases... PS: And all my reasoning has long gone beyond the scope of this PR. Such are the things. 😄 |
You are right. This PR is not the final, optimal solution, but as I outlined, it is a very big advance over the current implementation. So let's do it the way I suggested, adding one more command line flag to distinguish between global and adaptive thresholding. We can refine later based on more testing. And consider this: if someone wants the absolute best results, both in the stored image and the compression, they'll need to do the kind of preprocessing that you do with your programs, including using the djvu classifier. Then the images presented to jbig2 will be optimal and won't need anything more than global thresholding. |
@DanBloomberg . Both true and false at the same time. If everything were like this, then jbig2enc would immediately set a condition on monochrome images at the input, just like Good luck. 😄 |
Having a single global threshold is unsatisfactory in general, but as I said, it is fine for sufficiently clean grayscale images. Goal here: we're trying to improve the current situation (release 0.29 + a few changes), not going for an optimal solution, which as you said would tailor the processing based on an analysis of the images. So let's do that. It's true that this puts more responsibility on the user to know what they are doing. But by making adaptive thresholding the default, it lessens the risk of getting release 0.29's bad behavior on difficult images We can fix jbig2 to automatically do a much better job, using adaptive thresholding with those iimages. I'll resend a jbig2.cc file that seems to do it. |
@DanBloomberg .say:
No. It makes sense only (only!) to speed up the encoding of BW images. No shades of gray. In this case, there is truth. If you encode prepared images, acceleration makes sense. I'll try to make a patch. 😄 git diff >my_changes.patch diff --git a/src/jbig2.cc b/src/jbig2.cc
index 79816d4..5ee600c 100644
--- a/src/jbig2.cc
+++ b/src/jbig2.cc
@@ -62,6 +62,8 @@ usage(const char *argv0) {
fprintf(stderr, " -t <threshold>: set classification threshold for symbol coder (def: %0.2f)\n", JBIG2_THRESHOLD_DEF);
fprintf(stderr, " -w <weight>: set classification weight for symbol coder (def: %0.2f)\n", JBIG2_WEIGHT_DEF);
fprintf(stderr, " -T <bw threshold>: set 1 bpp threshold (def: %d)\n", BW_THRESHOLD_DEF);
+ fprintf(stderr, " -G --global: simply threshold for BW images\n");
+ fprintf(stderr, " -N --normalize: background clean/normalize\n");
fprintf(stderr, " -r --refine: use refinement (requires -s: lossless)\n");
fprintf(stderr, " -O <outfile>: dump thresholded image as PNG\n");
fprintf(stderr, " -2: upsample 2x before thresholding\n");
@@ -213,9 +215,12 @@ int
main(int argc, char **argv) {
bool duplicate_line_removal = false;
bool pdfmode = false;
+ bool bgnmode = false;
+ bool globalmode = false;
float threshold = JBIG2_THRESHOLD_DEF;
float weight = JBIG2_WEIGHT_DEF;
int bw_threshold = BW_THRESHOLD_DEF;
+ int bwthresdelta = 0;
bool symbol_mode = false;
bool refine = false;
bool up2 = false, up4 = false;
@@ -363,6 +368,18 @@ main(int argc, char **argv) {
continue;
}
+ if (strcmp(argv[i], "-G") == 0 ||
+ strcmp(argv[i], "--global") == 0) {
+ globalmode = true;
+ continue;
+ }
+
+ if (strcmp(argv[i], "-N") == 0 ||
+ strcmp(argv[i], "--normalize") == 0) {
+ bgnmode = true;
+ continue;
+ }
+
// engage auto thresholding
if (strcmp(argv[i], "--auto-thresh") == 0 ||
strcmp(argv[i], "-a") == 0 ) {
@@ -392,7 +409,7 @@ main(int argc, char **argv) {
if (t_dpi <= 0 || t_dpi > 9600) {
fprintf(stderr, "Invalid dpi: (1..9600)\n");
return 12;
- }
+ }
dpi = (int)t_dpi;
i++;
continue;
@@ -421,6 +438,11 @@ main(int argc, char **argv) {
struct jbig2ctx *ctx = jbig2_init(threshold, weight, 0, 0, !pdfmode, refine ? 10 : -1);
int pageno = -1;
+ if (!globalmode) {
+ bwthresdelta = (bgnmode) ? 0 : 64;
+ bw_threshold = ((bw_threshold + bwthresdelta) < 256) ? (bw_threshold + bwthresdelta) : 255;
+ }
+
int numsubimages=0, subimage=0, num_pages = 0;
while (i < argc) {
if (subimage==numsubimages) {
@@ -456,7 +478,7 @@ main(int argc, char **argv) {
if (verbose)
pixInfo(source, "source image:");
- PIX *pixl, *gray, *pixt;
+ PIX *pixl, *gray, *adapt, *pixt;
if ((pixl = pixRemoveColormap(source, REMOVE_CMAP_BASED_ON_SRC)) == NULL) {
fprintf(stderr, "Failed to remove colormap from %s\n", argv[i]);
return 1;
@@ -474,7 +496,13 @@ main(int argc, char **argv) {
fprintf(stderr, "Unsupported input image depth: %d\n", pixl->d);
return 1;
}
- Pix *adapt = pixBackgroundNormSimple(gray, NULL, NULL);
+ if (globalmode) {
+ adapt = pixClone(gray);
+ } else if (bgnmode) {
+ adapt = pixBackgroundNormSimple(gray, NULL, NULL);
+ } else {
+ adapt = pixCleanBackgroundToWhite(gray, NULL, NULL, 1.0, 90, 190);
+ }
pixDestroy(&gray);
if (up2) {
pixt = pixScaleGray2xLIThresh(adapt, bw_threshold);
@@ -583,4 +611,3 @@ main(int argc, char **argv) {
return 0;
}
- |
Not just BW. Skipping local thresholding and using a global threshold also makes sense for clean 8 bpp images, however they have been produced. |
So what is the staus? Can this be merged into the main and can we proceed to the new release? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for jumping in!
This needs to be simplified.
- remove the -N flag; we don't need it
- remove the bgnmode (we have one mode variable, globalmode, that determines if we're using global or adaptive thresholding
- remove bwthresdelta (not needed)
- set bw_threshold to be default (global or adaptive) if no '-T' is used. Otherwise, use the '-T'. Range of allowable input thresholds is given by the MAX & MIN constants (we can refine later).
I'll try to send you a working example jbig2.cc later today.
Dan
Here's the simplified version of jbig2.cc, as described above (etc). |
Hi all. First, delete the image threshold parameter, then say that it is “not important” and then, without regaining consciousness, add a new parameter (second) for the same threshold adjustment. This is all very bad. Users don't understand you. But the main thing is that you understand yourself. You know better.... PS: Compromise is when the interests of all parties are taken into account. 😄 |
Hi all. No matter how you wiggle, but testing is a necessary part of the process. And you won’t get away from this anywhere. OriginCurrent state of PR.
Patch by @DanBloomberg
Good luck. 😄 |
A whole bunch of comments. (1) Your representation of my "global" is wrong. It appears to be the same as your "global". With my patch:
yields the global that I showed, which isn't terrible. I do not know the reason for the difference.
(2) You claim that my users don't understand me. (3) We may have been talking past each other over the last few days, but I have tried to be as clear as possible about my suggestions. (4) As I have said several times, one should not use the pixBackgroundNorm*() functions before thresholding, without first doing a reasonable TRC. (5) The result from my patch on your image is far better than what we currently have in release 0.29. (6) Keeping global thresholding was actually your suggestion (I was initially ready to drop it), and I kept it for the reason I gave: that it gives the user a more efficient path they can use with clean images. Such as your cleaned up input images. (7) We use adaptive thresholding by default because it is more robust, users generally value quality over processing time, and we want to give them quality first out of the box. (8) Really, this is not a big deal. We're all adults here. Zdenko and I are just trying to make agl's project more useful for users, as you are. And as a user, you're not relying on our binarization for difficult images because you do all the cleaning in preprocesor stages using djvumini and your new programs. |
Hi all. Another option for discussion: simple and easy adjustment of scaling of the image threshold scale {0..255} for various modes (without any delta, only scaling). The default image threshold setting is 128 for all modes.
git diff >my_changes.patch diff --git a/src/jbig2.cc b/src/jbig2.cc
index 79816d4..becaba1 100644
--- a/src/jbig2.cc
+++ b/src/jbig2.cc
@@ -50,6 +50,8 @@
#define BW_THRESHOLD_MIN 0
#define BW_THRESHOLD_MAX 255
#define BW_THRESHOLD_DEF 128
+#define BW_THRESHOLD_RESCALE_CLEAN 0.5f
+#define BW_THRESHOLD_RESCALE_NORM 0.125f
static void
usage(const char *argv0) {
@@ -62,6 +64,8 @@ usage(const char *argv0) {
fprintf(stderr, " -t <threshold>: set classification threshold for symbol coder (def: %0.2f)\n", JBIG2_THRESHOLD_DEF);
fprintf(stderr, " -w <weight>: set classification weight for symbol coder (def: %0.2f)\n", JBIG2_WEIGHT_DEF);
fprintf(stderr, " -T <bw threshold>: set 1 bpp threshold (def: %d)\n", BW_THRESHOLD_DEF);
+ fprintf(stderr, " -G --global: simply threshold for BW images\n");
+ fprintf(stderr, " -N --normalize: background clean/normalize\n");
fprintf(stderr, " -r --refine: use refinement (requires -s: lossless)\n");
fprintf(stderr, " -O <outfile>: dump thresholded image as PNG\n");
fprintf(stderr, " -2: upsample 2x before thresholding\n");
@@ -213,6 +217,8 @@ int
main(int argc, char **argv) {
bool duplicate_line_removal = false;
bool pdfmode = false;
+ bool bgnmode = false;
+ bool globalmode = false;
float threshold = JBIG2_THRESHOLD_DEF;
float weight = JBIG2_WEIGHT_DEF;
int bw_threshold = BW_THRESHOLD_DEF;
@@ -363,6 +369,18 @@ main(int argc, char **argv) {
continue;
}
+ if (strcmp(argv[i], "-G") == 0 ||
+ strcmp(argv[i], "--global") == 0) {
+ globalmode = true;
+ continue;
+ }
+
+ if (strcmp(argv[i], "-N") == 0 ||
+ strcmp(argv[i], "--normalize") == 0) {
+ bgnmode = true;
+ continue;
+ }
+
// engage auto thresholding
if (strcmp(argv[i], "--auto-thresh") == 0 ||
strcmp(argv[i], "-a") == 0 ) {
@@ -392,7 +410,7 @@ main(int argc, char **argv) {
if (t_dpi <= 0 || t_dpi > 9600) {
fprintf(stderr, "Invalid dpi: (1..9600)\n");
return 12;
- }
+ }
dpi = (int)t_dpi;
i++;
continue;
@@ -421,6 +439,24 @@ main(int argc, char **argv) {
struct jbig2ctx *ctx = jbig2_init(threshold, weight, 0, 0, !pdfmode, refine ? 10 : -1);
int pageno = -1;
+ /* Rescale BW threshold scale [0..128..255] to: *
+ * clean mode (+0.5 == 1/2): [0..192..255] *
+ * normalize mode (+0.125 == 1/8): [0..144..255] */
+ if (!globalmode) {
+ float bwr = 0.0f;
+ if (bgnmode) {
+ bwr = BW_THRESHOLD_RESCALE_NORM; /* 0.125 -> 128 ~ 144 */
+ } else {
+ bwr = BW_THRESHOLD_RESCALE_CLEAN; /* 0.5 -> 128 ~ 192 */
+ }
+ float bwt = bw_threshold;
+ float bwa1 = 1.0f + 2.0f * bwr;
+ float bwa2 = (2.0f * bwr) / 255.0f;
+ bwt = bwa1 * bwt - bwa2 * bwt * bwt;
+ bwt = (bwt < 0.0f) ? 0.0f : ((bwt < 255.0f) ? bwt : 255.0f); /* clipping*/
+ bw_threshold = (int) (bwt + 0.5f); /* 0.5f for round */
+ }
+
int numsubimages=0, subimage=0, num_pages = 0;
while (i < argc) {
if (subimage==numsubimages) {
@@ -456,7 +492,7 @@ main(int argc, char **argv) {
if (verbose)
pixInfo(source, "source image:");
- PIX *pixl, *gray, *pixt;
+ PIX *pixl, *gray, *adapt, *pixt;
if ((pixl = pixRemoveColormap(source, REMOVE_CMAP_BASED_ON_SRC)) == NULL) {
fprintf(stderr, "Failed to remove colormap from %s\n", argv[i]);
return 1;
@@ -474,7 +510,13 @@ main(int argc, char **argv) {
fprintf(stderr, "Unsupported input image depth: %d\n", pixl->d);
return 1;
}
- Pix *adapt = pixBackgroundNormSimple(gray, NULL, NULL);
+ if (globalmode) {
+ adapt = pixClone(gray);
+ } else if (bgnmode) {
+ adapt = pixBackgroundNormSimple(gray, NULL, NULL);
+ } else {
+ adapt = pixCleanBackgroundToWhite(gray, NULL, NULL, 1.0, 90, 190);
+ }
pixDestroy(&gray);
if (up2) {
pixt = pixScaleGray2xLIThresh(adapt, bw_threshold);
@@ -583,4 +625,3 @@ main(int argc, char **argv) {
return 0;
}
- Rescale:
Good luck. 😄 |
I don't have much more to add. To recapitulate: (1) your approach is overly complicated For now, let's proceed with my jbig2.cc, for all the reasons that I have previously given. |
@zdenop , use @DanBloomberg patch. I wash my hands of the fact that the usual brightness curve, which is in every graphics editor, is difficult. And make a release. Good luck. 😄 |
Closing in favor of 528a5f4. Thank you for your cooperation! |
Improve background handling