0

I have 1000 Image of book page, Its text is dimmed as this piece Dimmed Text
Now I tried to repair it to be more clearly to read, I use this code

 private Bitmap repairImage(Bitmap bmp)
    {
        Color cc=Color.FromArgb(255, 251, 251, 251);
        for (int x = 0; x < bmp.Width; x++)
        {
            for (int y = 0; y < bmp.Height; y++)
            {
                if (bmp.GetPixel(x, y).R>238)
                {
                    bmp.SetPixel(x, y, Color.White);
                }
                else
                {
                    bmp.SetPixel(x, y, Color.Black);
                }
            }
        }
       return bmp;
    }

Due to the image dimensions is 1168 x 1807 it took a lot of time to finish repair, it exactly loops 2110576 cycles.
Is there any another way to solve this problem? Thanks.

Community
  • 1
  • 1
JustMe
  • 6,065
  • 3
  • 19
  • 17
  • 2
    look here: http://stackoverflow.com/questions/6020406/travel-through-pixels-in-bmp (avoid using GetPixel() and SetPixel() for every pixel) – Rosa Gronchi Dec 06 '15 at 14:20
  • Look up gamma and ColorMatrix! See [here](http://stackoverflow.com/questions/23865511/contrast-with-color-matrix/23866677?s=1|6.2068#23866677), especially the link to the guide! – TaW Dec 06 '15 at 17:12

2 Answers2

2

The best way I can think of it to use the built-in ColorMatrix class to change the Gamma and the Contrast of the image.

Here is a result for a Gamma = 6.27 and a Contrast = +1.04:

enter image description here

Here is the code I used:

using System.Drawing.Imaging;
..

public static Bitmap ApplyGamma(Bitmap bmp0, float gamma, float contrast)
{

    Bitmap bmp1 = new Bitmap(bmp0.Width, bmp0.Height);
    using (Graphics g = Graphics.FromImage(bmp1))
    {
        ColorMatrix colorMatrix = new ColorMatrix(new float[][] 
                {
                    new float[] {contrast, 0, 0, 0, 0},
                    new float[] {0,contrast, 0, 0, 0},
                    new float[] {0, 0, contrast, 0, 0},
                    new float[] {0, 0, 0, 1, 0},
                    new float[] {0, 0, 0, 0, 1}
                });


        ImageAttributes attributes = new ImageAttributes();
        attributes.SetColorMatrix(colorMatrix, ColorMatrixFlag.Default,
                                               ColorAdjustType.Bitmap);
        attributes.SetGamma(gamma, ColorAdjustType.Bitmap);
        g.DrawImage(bmp0, new Rectangle(0, 0, bmp0.Width, bmp0.Height),
                    0, 0, bmp0.Width, bmp0.Height, GraphicsUnit.Pixel, attributes);
    }
    return bmp1;
}

The function uses two variables and two TrackBars along with two Labels:

float gamma = 1f ;
float contrast = 1f;

private void trackBar1_Scroll(object sender, EventArgs e)
{
    gamma = 1f * trackBar1.Value / 100f;
    label1.Text = gamma.ToString("#0.00");
    pictureBox1.Image = ApplyGamma(originalImage, gamma, contrast);
}


private void trackBar2_Scroll(object sender, EventArgs e)
{
    contrast = 1f * trackBar2.Value / 1000f;
    label2.Text = contrast.ToString("#0.00");
    pictureBox1.Image = ApplyGamma(originalImage, gamma, contrast);
}

Note that I am leaking the Bitmaps; it is just for testing ;-)

TaW
  • 53,122
  • 8
  • 69
  • 111
1

I use this (C++) code:

void picture::enhance_range()
    {
    int i,x,y,a0[4],min[4],max,n,c0,c1,q,c;
    if (xs<1) return;
    if (ys<1) return;

    n=0;    // dimensions to interpolate
    if (pf==_pf_s   ) { n=1; c0=0; c1=127*3; }
    if (pf==_pf_u   ) { n=1; c0=0; c1=255*3; }
    if (pf==_pf_ss  ) { n=2; c0=0; c1=32767; }
    if (pf==_pf_uu  ) { n=2; c0=0; c1=65535; }
    if (pf==_pf_rgba) { n=4; c0=0; c1=  255; } // this is your image pixel format so ignore the other pf statements

    // find min,max intensities
    dec_color(a0,p[0][0],pf);
    for (i=0;i<n;i++) min[i]=a0[i]; max=0;
    for (y=0;y<ys;y++)
     for (x=0;x<xs;x++)
        {
        dec_color(a0,p[y][x],pf); // this just unpack pixel color p[][] to a0[4]={r,g,b,a}
        for (q=0,i=0;i<n;i++)
            {
            c=a0[i]; if (c<0) c=-c;
            if (min[i]>c) min[i]=c;
            if (max<c) max=c;
            }
        }
    // change dynamic range to max
    for (y=0;y<ys;y++)
     for (x=0;x<xs;x++)
        {
        dec_color(a0,p[y][x],pf);
        for (i=0;i<n;i++) a0[i]=c0+(((a0[i]-min[i])*(c1-c0))/(max-min[i]+1));
//      for (i=0;i<n;i++) if (a0[i]<c0) a0[i]=c0; // clamp if needed
//      for (i=0;i<n;i++) if (a0[i]>c1) a0[i]=c1; // clamp if needed
        enc_color(a0,p[y][x],pf); // this just pack a0[4]={r,g,b,a} to pixel color p[][]
        }
    }

where:

  • pf is current pixel format in your case pf=_pf_rgba which is just enum constant
  • xs,ys is resolution of image
  • p[y][x] is direct pixel access to image
  • enc_color,dec_color just pack/unpack color components for desired pixel format

This is the result:

example

The main idea is to find minimal and maximal color value, Then enhance this dynamic range to maximum. For example (on grayscale colors) your image has:

min=181;
max=254;

So if you take each pixel and rescale to max <0,255> you need to do something like:

color=(color-min)*255/(max-min);

for each pixel of the image and that is all.

[Notes]

As @RosaGronchi mentioned Your current approach is slow due to use of getpixel,setpixel use scanlines instead (that should be few thousand times faster).

Also Another disadvantage of your approach is that you just binarise the image loosing all rendered anti-aliasing of text ...

Community
  • 1
  • 1
Spektre
  • 49,595
  • 11
  • 110
  • 380
  • _that should be few thousand times faster_ That sounds rather optimistic to me. – TaW Dec 06 '15 at 17:37
  • @TaW not at all if coded right ... each `setpixel` or `getpixel` invokes **many** GDI calls checking for bouns,pixelformat,coverions and much more... The `ScanLine` property does it too but only once per line and if remembered then only once per line per image/resize and can be accessed repeatedly after ... on heavy pixel access the usual speed boost is around `10000x` times (on for example SW 3D rendering or fill algorithms) ... but have to be coded right ... – Spektre Dec 06 '15 at 18:19
  • Well the last time I compres Lockbits and get/setPixel is was aound 20 times faster. A lot but by no means thousands.. But that is not the same as your code, I guess.. – TaW Dec 06 '15 at 18:24
  • @Taw It depends on how much you actually use the `getpiel`,`setpixel` instead of direct pixel access ... opposed to the amount of alternative GDI calls. If the `getpixel`,`setpixel` you are referring to are not GDI then it is whole another topic. – Spektre Dec 06 '15 at 18:34
  • I compared using GDI+ Get/SetPixel from C# angainst LockBits. – TaW Dec 06 '15 at 18:35
  • @TaW the LockBits does copying the memory or not? If yes that would explain small speed boost.... – Spektre Jun 14 '16 at 08:32
  • In the center of a LockBits routine there are two [Marshall.Copy](https://msdn.microsoft.com/en-us/library/system.runtime.interopservices.marshal.copy%28v=vs.110%29.aspx) calls, so yes memory gets copied. But do not know if nothing is copied with a ColorMatrix, though. – TaW Jun 14 '16 at 09:05