victorvde / jpeg2png

silky smooth JPEG decoding
GNU General Public License v3.0
556 stars 31 forks source link

About overblurring #10

Open ilyakurdyukov opened 4 years ago

ilyakurdyukov commented 4 years ago

As the author of a similar project, I also encountered this effect, but found a method to prevent overblurring. I tried to apply same method to jpeg2png.

In compute.c, made changes to clamp_dct_c function:

// clamp the DCT values to interval that quantizes to our jpg
POSSIBLY_UNUSED static void clamp_dct_c(struct coef *coef, float *boxed, unsigned blocks) {
        for(unsigned i = 0; i < blocks; i++) {
                float mul = 1;
#if 1 // here's the fix
                float m0 = 0, m1 = 0;
                for (unsigned j = 1; j < 64; j++) {
                        float a0 = coef->data[i*64+j] * (int)coef->quant_table[j];
                        m0 += boxed[i*64+j] * a0;
                        m1 += a0 * a0;
                }
                if (m1 > m0) mul = MIN(m1 / m0, 1.1f);
#endif
                for(unsigned j = 0; j < 64; j++) {
                        float min = (coef->data[i*64+j] - 0.5f) * coef->quant_table[j];
                        float max = (coef->data[i*64+j] + 0.5f) * coef->quant_table[j];
                        float val = boxed[i*64+j] * mul;
                        boxed[i*64+j] = CLAMP(val, min, max);
                }
        }
}

And replaced the line
POSSIBLY_SIMD(clamp_dct)(coef, boxed, blocks);
with
clamp_dct_c(coef, boxed, blocks);

The resulting images become sharper, but in some places appeared side effects like sharp dots, and some gradients split into borders. I tried to limit this with MIN(m1 / m0, 1.1f) (I don't have this limit in my project, works without it), side effects become less visible but remain.

So my attempt to fix this problem added a few new ones.
Maybe someone will find a way to improve this fix, or another method to aid with overblurring.

ilyakurdyukov commented 4 years ago

Second attempt:

--- compute-orig.c  2016-01-31 06:42:13.000000000 +0600
+++ compute.c   2020-07-12 17:52:23.634530193 +0700
@@ -331,7 +331,7 @@
 }

 // compute projection of data onto the feasible set defined by our jpg
-static void compute_projection(unsigned w, unsigned h, struct aux *aux, struct coef *coef) {
+static void compute_projection(unsigned w, unsigned h, struct aux *aux, struct coef *coef, float mul_limit) {
         unsigned blocks = (coef->h / 8) * (coef->w / 8);
         float *subsampled;
         float *boxed = aux->temp[0];
@@ -378,6 +378,25 @@

         POSSIBLY_SIMD(clamp_dct)(coef, boxed, blocks);

+        // second attempt to prevent overblurring
+        if(mul_limit > 1.0f)
+        for(unsigned i = 0; i < blocks; i++) {
+                float m0 = 0, m1 = 0, mul = 1;
+                for(unsigned j = 1; j < 64; j++) {
+                        float a0 = coef->data[i*64+j] * (int)coef->quant_table[j];
+                        m0 += boxed[i*64+j] * a0;
+                        m1 += a0 * a0;
+                }
+                if(m1 <= m0) continue;
+                mul = MIN(m1 / m0, mul_limit);
+                for(unsigned j = 1; j < 64; j++) {
+                        float min = (coef->data[i*64+j] - 0.5f) * coef->quant_table[j];
+                        float max = (coef->data[i*64+j] + 0.5f) * coef->quant_table[j];
+                        float val = boxed[i*64+j] * mul;
+                        boxed[i*64+j] = CLAMP(val, min, max);
+                }
+        }
+
         memcpy(aux->cos, boxed, coef->w * coef->h * sizeof(float)); // save a copy of the DCT values for step_prob

         for(unsigned i = 0; i < blocks; i++) {
@@ -444,7 +463,7 @@
                 // project back onto feasible set
                 OPENMP(parallel for schedule(dynamic))
                 for(unsigned c = 0; c < nchannel; c++) {
-                        compute_projection(w, h, &auxs[c], &coefs[c]);
+                        compute_projection(w, h, &auxs[c], &coefs[c], (float)(i + 1) / iterations * 0.2f + 0.9f);
                 }
                 if(pb) {
                         OPENMP(critical(progressbar))

Reduced side effects, the quality now seems acceptable. You can adjust the sharpness by experimenting with the constants * 0.2f + 0.9f in the code. Results looks a little over sharpened with the current values.

zvezdochiot commented 4 years ago

@ilyakurdyukov say:

You can adjust the sharpness by experimenting with the constants * 0.2f + 0.9f in the code.

It is less bad to make constants as ext. command line options.

PS: :question: Maybe add 'Issues' of you fork?