I have image with white uneven background (due to lighting). I'm trying to estimate background color and transform image into image with true white background. For this I estimated white color for each 15x15 pixels block based on its luminosity. So I've got the following map (on the right):
Now I want to interpolate color so it will be more smooth transition from 15x15 block to neighboring block, plus I want it to eliminate outliers (pink dots on left hand side). Could anyone suggest good technique/algorithm for this? (Ideally within OpenCV library, but not necessary)
Asked
Active
Viewed 565 times
1

Pavel Podlipensky
- 8,201
- 5
- 42
- 53
-
Can you post original image? Do you also need to segment the text or not? – Miki Feb 10 '16 at 17:24
-
@Miki Original image is on the left, and my pre-processing is on the right of the image I uploaded. – Pavel Podlipensky Feb 10 '16 at 17:37
-
That is your original image? Or the original image shown by your program? Also, do you have a better quality image (e.g. png)? – Miki Feb 10 '16 at 17:39
-
@Miki Yes image in window "Board Image" (on a left) is original one. I try to convert shades of gray into solid white background. Sorry, I don't have better original image since I got it somewhere from internet. – Pavel Podlipensky Feb 10 '16 at 18:09
-
If you want to measure the illumination of the *"white"* background, maybe use the red channel since the writing shows up least in there so the writing will have least impact on your measurement that way. Your calculated image is very blocky, if you set each pixel to the mean of its surrounding 50x50 pixel block, it should come out pretty smooth. Your image quality is very poor too :-( – Mark Setchell Feb 10 '16 at 22:43
-
@MarkSetchell I want to develop robust technique for white background estimation - stroke color could be any other color (including black). – Pavel Podlipensky Feb 11 '16 at 00:05
1 Answers
3
Starting from this image:
You could find the text on the whiteboard as the parts of your images that have a high gradient, and apply a little dilation to deal with thick parts of the text. You'll get a mask that separates background from foreground pretty well:
Background:
Foreground:
You can then apply inpainting using the computed mask on the original image (you need OpenCV contrib module photo
):
Just to show that this works independently of the text color, I tried on a different image:
Resulting in:
Code:
#include <opencv2/opencv.hpp>
#include <opencv2/photo.hpp>
using namespace cv;
void findText(const Mat3b& src, Mat1b& mask)
{
// Convert to grayscale
Mat1b gray;
cvtColor(src, gray, COLOR_BGR2GRAY);
// Compute gradient magnitude
Mat1f dx, dy, mag;
Sobel(gray, dx, CV_32F, 1, 0);
Sobel(gray, dy, CV_32F, 0, 1);
magnitude(dx, dy, mag);
// Remove low magnitude, keep only text
mask = mag > 10;
// Apply a dilation to deal with thick text
Mat1b K = getStructuringElement(MORPH_ELLIPSE, Size(3, 3));
dilate(mask, mask, K);
}
int main(int argc, const char * argv[])
{
Mat3b img = imread("path_to_image");
// Segment white
Mat1b mask;
findText(img, mask);
// Show intermediate images
Mat3b background = img.clone();
background.setTo(0, mask);
Mat3b foreground = img.clone();
foreground.setTo(0, ~mask);
// Apply inpainting
Mat3b inpainted;
inpaint(img, mask, inpainted, 21, CV_INPAINT_TELEA);
imshow("Original", img);
imshow("Foreground", foreground);
imshow("Background", background);
imshow("Inpainted", inpainted);
waitKey();
return 0;
}

Miki
- 40,887
- 13
- 123
- 202
-
thanks for sharing your approach, but unfortunately such background estimation is not ideal. Once I calculate it, I replace pixels in original image with white pixels if I believe that that pixel belongs to background (L2 norm distance < threshold). And it doesn't give me result I want - http://imgur.com/j0m2E5k. Also this approach is too slow for my application (it takes ~10sec to inpaint on my laptop and I plan to run this code on mobile device). But thank you anyway. – Pavel Podlipensky Feb 11 '16 at 19:39
-
Most (99%) of the time is for inpainting. If you have another method to rebuild background, you'll earn a lot of time. If you share your code in the question we can come up with something better, probably. – Miki Feb 11 '16 at 19:43
-
I'm working on another approach now - once I have approximate background (by using 15x15 blocks explained above), I want to fit plane into RGB space, so those pink dots will be interpolated and replaced with colors from neighbors. Then my plan is to subtract this background from image (don't know how exactly yet) – Pavel Podlipensky Feb 11 '16 at 20:34
-
Ok. But I still don't understand the need to find the _cleaned_ whiteboard, though. I'm just curious... what kind of app are you doing? – Miki Feb 11 '16 at 20:39
-
as I mentioned above I want clean whiteboard in order to subtract it from original one and receive just text. It also should fix any illumination in text if any. Eventually my app will do OCR on whiteboard content – Pavel Podlipensky Feb 12 '16 at 18:11
-
But if you want to segment text, can't you [just segment text](http://stackoverflow.com/a/19971599/5008845)? I think the extra effort to estimate the background is not useful at all in this context. – Miki Feb 17 '16 at 13:14
-
my goal is also to show enhanced (with uniform white background) photo of the whiteboard. Stroke Width Transform approach looks promising, but I don't think it will be robust and work in cases like this: http://parentteachplay.com/inlieuofpreschool.com/wp-content/uploads/2012/04/DSC_0002-00110.jpg – Pavel Podlipensky Feb 17 '16 at 22:06
-
Ok. I get what you mean. I still think that it's easier to directly segment the foregound (since you need to do it anyway to get the background and reconstruct it). I'll see if in the next few days I can came up with something to support this. This is not a trivial problem ;D – Miki Feb 17 '16 at 22:51
-
thanks for your help, please let me know if you will have other ideas – Pavel Podlipensky Feb 19 '16 at 01:26