<p>Here are both images &quot;cleaned&quot; using simple luminance thresholding like I suggested in the comments. There was nothing special done to these images, a simple &quot;is the luminance below threshold for this texel&quot; is all that was done. The image was first converted to gray scale.</p>
<p>If the scan was better quality with a higher resolution the results would better.</p>
<p>The real challenge with this technique is finding the threshold, again a scan with higher resolution would help. But one of the images looked best at a cutoff of 0.55 and the other at 0.68.</p>
<p>I did this in a small program that I threw together, but there must be an image tool that can do this easily. (I am no expert at tools that would be a question for the photoshop/gimp forums)</p>
<p>The program was just the stbi image header (a c++ header file that can load and save various image types like jpg and png) and a loop that spun over the values computed the gray scale, tested the threshold, and output the result.</p>
<p>Here are the results:</p>
<ul>
<li><a href="https://i.sstatic.net/VJkXk.jpg" rel="nofollow noreferrer">https://i.sstatic.net/VJkXk.jpg</a><br />
<a href="https://i.sstatic.net/lcOEh.jpg" rel="nofollow noreferrer">https://i.sstatic.net/lcOEh.jpg</a></li>
</ul>
<hr />
<pre><code>#include &lt;stdio.h&gt;
#include &lt;stdlib.h&gt;

#define STB_IMAGE_IMPLEMENTATION
#include &quot;stb_image/stb_image.h&quot;
#define STB_IMAGE_WRITE_IMPLEMENTATION
#include &quot;stb_image/stb_image_write.h&quot;

int main(int argc, char **argv) {
   int width, height, channels;
   if( argc &lt; 1 ) {
      printf(&quot;Full path to image must be the first argument passed on the command line.\n&quot;); 
      return -1; 
   }
   unsigned char *img = stbi_load(argv[0], &amp;width, &amp;height, &amp;channels, 0);
   if(img == NULL || channels &lt; 3) {
       printf(&quot;Error in loading the image, expected color image\n&quot;);
       return -1;
   }
   printf(&quot;Loaded image with a width of %dpx, a height of %dpx and %d channels\n&quot;, width, height, channels);


   // convert to gray scale, ignore alpha channel if it exists
   for( int n=0; n &lt; width*height*channels; n+=channels)
   {
      int grayscale = img[n];
      grayscale += img[n+1];
      grayscale += image[n+2];
      img[n] = img[n+1] = img[n+2] = (unsigned char)(grayscale/3);
   }

   // filter 0.56f is the threshold here, it is converted into the range of an 8 bit image (0-256).
   // the threshold must be a value between zero and 1
   int threshold = (int)(0.56f * 256);
   for( int n=0; n&lt;width*height*channels; n+=channels)
   {
      // if the pixel is below the threshold then make it white
      if( image[n] &lt; threshold ){
          img[n] = image[n+1] = image[n+2] = 255;
      }
   }

  // save the image to a bmp ( don't overwrite original file)
  stbi_write_bmp(&quot;output.bmp&quot;, width, height, channels, img);

  return 0;
}
</code></pre>

CC BY-SA 4.0
2024-03-03T06:23:19.747Z
Can you share the program here?
skeetastax
CC BY-SA 4.0
2024-03-03T16:11:54.297Z
I deleted the program I wrote for this, but I wrote a new one from memory here.  The conversion to gray scale is just an average in this one but (hopefully) it will be enough to get started. It is written in the c language.
pmw1234
CC BY-SA 4.0
2024-03-04T00:11:44.533Z
Obrigado! Danke! Merci!
skeetastax
CC BY-SA 4.0
2023-11-02T10:35:58.983Z
2024-03-03T19:13:18.010Z
2024-03-03T19:13:18.010Z
pmw1234
<p><em><strong>Originally asked in Graphic Design site <a href="https://graphicdesign.stackexchange.com/questions/162596/how-do-i-normalize-all-pixel-values-such-that-all-pixel-values-are-pulled-towar">here</a></strong></em> <em>(but I don't know how to 'move' a question to another site)</em></p>
<ul>
<li><em>Credit to this guy: <a href="https://www.youtube.com/watch?v=ADV-AjAXHdc&amp;list=PL2VXyKi-KpYuTAZz__9KVl1jQz74bDG7i&amp;index=6&amp;ab_channel=PythonTutorialsforDigitalHumanities" rel="nofollow noreferrer">Python Tutorials for Digital Humanities</a> - I followed a lot of his ideas, but I haven't got it down yet</em></li>
</ul>
<p><strong>I have a bunch of scanned document images</strong> and in each image there is a variance in background colour (sometimes a wide variance).</p>
<p>I want to adjust all image pixels such that they are 'normalized' towards the pixel colour mean. To put it another way, I want to adjust all pixel colour values (or grayscale) so that the background colour (e.g. the 'white page') loses the colour gradients and variation and instead becomes as close as possible to white (or white).</p>
<p>I have been looking into colour gradients, vectorization and various filters, thresh-holding and blurring techniques, but I can't quite work out how to do what I see in my head.</p>
<p><strong>Take the following example image:</strong> <em>(ignore my red annotation)</em></p>
<p><a href="https://i.sstatic.net/JOPkG.jpg" rel="nofollow noreferrer"><img src="https://i.sstatic.net/JOPkG.jpg" alt="enter image description here" /></a></p>
<p>I want to find out what the overall mean colour is for all pixels, then for each pixel reduce or increase it towards the mean value to create a more 'flat' image. The reason I want to do that is because I believe it will improve the next step, which is to detect contours and edges for the purpose of text detection (and ultimately better OCR results).</p>
<p>So in the above example, the goal is to effectively remove the gray diagonal lines, but leave the text. I think there might be a way to automatically determine a threshold value (or have a dynamic threshold) but I am not sure exactly how to do that.</p>
<p><strong>Here is another example image:</strong></p>
<p><a href="https://i.sstatic.net/DGQKM.png" rel="nofollow noreferrer"><img src="https://i.sstatic.net/DGQKM.png" alt="enter image description here" /></a></p>
<p>The goal for this second image would be to effectively remove the target logo and most of the other background 'document colour mess' picked up by the scanner, as per the annotation.</p>
<p>The overall objective is to improve text block detection to then improve OCR accuracy.</p>
<p>I know I can play around with filters and image enhancements using slides in GIMP or some other graphics GUI, but one of the key points is that <em><strong>this process has to be automated</strong></em> because doing it manually would essentially defeat the purpose and I may as well go back to manual data entry by eyeballing every document (ugh!).</p>
<p>This is why I have been trying to use OpenCV in Python.</p>
<p>The second point is that since every image is different I need to be able to determine the threshold (for final binarization prior to OCR) automatically, which is kind of what I mean by 'normalization' above.</p>

CC BY-SA 4.0
2023-11-01T11:21:40.130Z
might I ask towards what end removing security information from receipts might be?
pmw1234
CC BY-SA 4.0
2023-11-01T11:36:47.680Z
I'm not sure what you mean by 'removing security information', sorry. These aren't receipts, they're just scanned price tags as examples. There's nothing private about them to me (you'll find the same kind of tags in your local store ;), they're just examples of scanned text that needs cleaning prior to effective OCR. The goal is to automate image preprocessing so as to (vastly) improve OCR (I have other OCR tools). Doesn't have to be a price tag or receipt (though they may benefit from this). It could be street signs or any other images with text that needs converting to searchable text.
skeetastax
CC BY-SA 4.0
2023-11-01T12:43:21.480Z
I worked my way through college partially in retail and receipts/tags/etc that look like they came out of a parking lot set off red flags for me...my spidy senses are still tingling on this one... at any rate In these images all you want to keep is the very darkest pixels so all that's really needed is to take only those pixels that are below a certain luminance threshold.
pmw1234
CC BY-SA 4.0
2023-11-01T20:09:16.737Z
Let me put my your mind at rest - I am just trying to scan my own receipts because  I want to automate transaction entry. I hate manually doing this stuff....but after reading your comment I think I might understand what you're getting at regarding price tags now....I don't plan on creating fake price tags to save money. I hadn't even thought of that until you raised it, but I guess some people do, and now I understand why you said 'security' in relation to a price tag. I didn't post actual receipt images because...well, that does contain private info ;)
skeetastax
CC BY-SA 4.0
2023-11-01T20:11:21.323Z
I wonder, is there a particular forum that is dedicated to OCR discussions that you know of?
skeetastax
CC BY-SA 4.0
2023-11-01T20:12:33.580Z
There is one other really useful reason - blind people. i have a blind friend (great guitarist) who would benefit from high accuracy OCR tools.
skeetastax
<image-processing><vector-graphics><pixel-graphics><filtering><opencv>
How do I normalize all pixel values such that all pixel values are 'pulled towards' the mean?
skeetastax
# How do I normalize all pixel values such that all pixel values are 'pulled towards' the mean?

(Asked by: username_0 on 2023-11-01)

***Originally asked in Graphic Design site [here](https://graphicdesign.stackexchange.com/questions/162596/how-do-i-normalize-all-pixel-values-such-that-all-pixel-values-are-pulled-towar)*** *(but I don't know how to 'move' a question to another site)*

* *Credit to this guy: [Python Tutorials for Digital Humanities](https://www.youtube.com/watch?v=ADV-AjAXHdc&list=PL2VXyKi-KpYuTAZz__9KVl1jQz74bDG7i&index=6&ab_channel=PythonTutorialsforDigitalHumanities) - I followed a lot of his ideas, but I haven't got it down yet*

**I have a bunch of scanned document images** and in each image there is a variance in background colour (sometimes a wide variance).

I want to adjust all image pixels such that they are 'normalized' towards the pixel colour mean. To put it another way, I want to adjust all pixel colour values (or grayscale) so that the background colour (e.g. the 'white page') loses the colour gradients and variation and instead becomes as close as possible to white (or white).

I have been looking into colour gradients, vectorization and various filters, thresh-holding and blurring techniques, but I can't quite work out how to do what I see in my head.

**Take the following example image:** *(ignore my red annotation)*

[![enter image description here](https://i.sstatic.net/JOPkG.jpg)](https://i.sstatic.net/JOPkG.jpg)

I want to find out what the overall mean colour is for all pixels, then for each pixel reduce or increase it towards the mean value to create a more 'flat' image. The reason I want to do that is because I believe it will improve the next step, which is to detect contours and edges for the purpose of text detection (and ultimately better OCR results).

So in the above example, the goal is to effectively remove the gray diagonal lines, but leave the text. I think there might be a way to automatically determine a threshold value (or have a dynamic threshold) but I am not sure exactly how to do that.

**Here is another example image:**

[![enter image description here](https://i.sstatic.net/DGQKM.png)](https://i.sstatic.net/DGQKM.png)

The goal for this second image would be to effectively remove the target logo and most of the other background 'document colour mess' picked up by the scanner, as per the annotation.

The overall objective is to improve text block detection to then improve OCR accuracy.

I know I can play around with filters and image enhancements using slides in GIMP or some other graphics GUI, but one of the key points is that ***this process has to be automated*** because doing it manually would essentially defeat the purpose and I may as well go back to manual data entry by eyeballing every document (ugh!).

This is why I have been trying to use OpenCV in Python.

The second point is that since every image is different I need to be able to determine the threshold (for final binarization prior to OCR) automatically, which is kind of what I mean by 'normalization' above.

**Tags:** image-processing, vector-graphics, pixel-graphics, filtering, opencv

> **Comments:**
> **username_1**: might I ask towards what end removing security information from receipts might be?  
> **username_0**: I'm not sure what you mean by 'removing security information', sorry. These aren't receipts, they're just scanned price tags as examples. There's nothing private about them to me (you'll find the same kind of tags in your local store ;), they're just examples of scanned text that needs cleaning prior to effective OCR. The goal is to automate image preprocessing so as to (vastly) improve OCR (I have other OCR tools). Doesn't have to be a price tag or receipt (though they may benefit from this). It could be street signs or any other images with text that needs converting to searchable text.  
> **username_1**: I worked my way through college partially in retail and receipts/tags/etc that look like they came out of a parking lot set off red flags for me...my spidy senses are still tingling on this one... at any rate In these images all you want to keep is the very darkest pixels so all that's really needed is to take only those pixels that are below a certain luminance threshold.  
> **username_0**: Let me put my your mind at rest - I am just trying to scan my own receipts because  I want to automate transaction entry. I hate manually doing this stuff....but after reading your comment I think I might understand what you're getting at regarding price tags now....I don't plan on creating fake price tags to save money. I hadn't even thought of that until you raised it, but I guess some people do, and now I understand why you said 'security' in relation to a price tag. I didn't post actual receipt images because...well, that does contain private info ;)  
> **username_0**: I wonder, is there a particular forum that is dedicated to OCR discussions that you know of?  
> **username_0**: There is one other really useful reason - blind people. i have a blind friend (great guitarist) who would benefit from high accuracy OCR tools.  

---

## 1 answers

### Answer by username_1 on 2023-11-02. Score: 2 | Accepted answer

Here are both images "cleaned" using simple luminance thresholding like I suggested in the comments. There was nothing special done to these images, a simple "is the luminance below threshold for this texel" is all that was done. The image was first converted to gray scale.

If the scan was better quality with a higher resolution the results would better.

The real challenge with this technique is finding the threshold, again a scan with higher resolution would help. But one of the images looked best at a cutoff of 0.55 and the other at 0.68.

I did this in a small program that I threw together, but there must be an image tool that can do this easily. (I am no expert at tools that would be a question for the photoshop/gimp forums)

The program was just the stbi image header (a c++ header file that can load and save various image types like jpg and png) and a loop that spun over the values computed the gray scale, tested the threshold, and output the result.

Here are the results:

* <https://i.sstatic.net/VJkXk.jpg>  
  <https://i.sstatic.net/lcOEh.jpg>

---

```
#include <stdio.h>
#include <stdlib.h>

#define STB_IMAGE_IMPLEMENTATION
#include "stb_image/stb_image.h"
#define STB_IMAGE_WRITE_IMPLEMENTATION
#include "stb_image/stb_image_write.h"

int main(int argc, char **argv) {
   int width, height, channels;
   if( argc < 1 ) {
      printf("Full path to image must be the first argument passed on the command line.\n"); 
      return -1; 
   }
   unsigned char *img = stbi_load(argv[0], &width, &height, &channels, 0);
   if(img == NULL || channels < 3) {
       printf("Error in loading the image, expected color image\n");
       return -1;
   }
   printf("Loaded image with a width of %dpx, a height of %dpx and %d channels\n", width, height, channels);

   // convert to gray scale, ignore alpha channel if it exists
   for( int n=0; n < width*height*channels; n+=channels)
   {
      int grayscale = img[n];
      grayscale += img[n+1];
      grayscale += image[n+2];
      img[n] = img[n+1] = img[n+2] = (unsigned char)(grayscale/3);
   }

   // filter 0.56f is the threshold here, it is converted into the range of an 8 bit image (0-256).
   // the threshold must be a value between zero and 1
   int threshold = (int)(0.56f * 256);
   for( int n=0; n<width*height*channels; n+=channels)
   {
      // if the pixel is below the threshold then make it white
      if( image[n] < threshold ){
          img[n] = image[n+1] = image[n+2] = 255;
      }
   }

  // save the image to a bmp ( don't overwrite original file)
  stbi_write_bmp("output.bmp", width, height, channels, img);

  return 0;
}

```

> **Comments:**
> **username_0**: Can you share the program here?  
> **username_1**: I deleted the program I wrote for this, but I wrote a new one from memory here.  The conversion to gray scale is just an average in this one but (hopefully) it will be enough to get started. It is written in the c language.  
> **username_0**: Obrigado! Danke! Merci!  

---

