14

I have problem with low contrast image segmentation. Task is to find surface defects. They are visible (defects are always dark areas) but the contrast of image is very low. Below two samples.

1 2

I have tried enhance contrast and then tresholding:

Mat tmp1 = imread("C:\\framesRoi\\311.bmp",0);
stretchContrast(tmp1);
threshold(tmp1,tmp1,75,255,THRESH_BINARY);

where stretch contrast impl:

int minValue = 255, maxValue = 0;
const int l = sourceImg.cols * sourceImg.rows * sourceImg.channels();
if(sourceImg.isContinuous())
{
    uchar* ptr = sourceImg.ptr<uchar>(0);
    for(int i = 0; i < l; ++i)
    {
        if(ptr[i] < minValue)
        {
            minValue = ptr[i];
        }
        if(ptr[i] > maxValue)
        {
            maxValue = ptr[i];
        }
    }
}
cout<<"min: "<<minValue<<";"<<"max value: "<<maxValue<<endl;

const int  magicThreshold = 10;
if(sourceImg.isContinuous())
{
    uchar* ptr = sourceImg.ptr<uchar>(0);
    for(int i = 0; i < l; ++i)
    {
        ptr[i] = 255 * (ptr[i]-minValue)/(maxValue - minValue);
    }
}

But this approach failed. There are many false detections and not all defects are detected: 3

Here is zip with test frames: https://dl.dropboxusercontent.com/u/47015140/testFrames.rar

krzych
  • 2,126
  • 7
  • 31
  • 50
  • 1
    When you do a contrast stretch based on the image content, you lose the context necessary to pick a useful threshold. – Mark Ransom Dec 03 '14 at 21:34
  • @MarkRansom : so how should I do it? – krzych Dec 03 '14 at 21:38
  • 2
    If you know the defects will always be dark, you can adjust the brightness without changing the contrast so that the bright areas are consistent. You can do that with the max, although using a 90th percentile or so instead would be less vulnerable to noise. – Mark Ransom Dec 03 '14 at 21:48
  • Defects are always dark. I don't understand what adjusting you suggest, simply multiplying or dividing pixel values? Could you explain? Maybe some pseudocode or something? – krzych Dec 03 '14 at 21:53
  • 2
    Simple addition/subtraction. – Mark Ransom Dec 03 '14 at 22:18
  • 2
    Have you tried doing other contrast enhancement techniques? What about gamma correction? Histogram equalization? – rayryeng Dec 04 '14 at 07:26
  • Tried histogram equalization. Produces more noises than technique I've used. I will try gamma correction. – krzych Dec 04 '14 at 08:02
  • Try adaptive techniques too. Try some sort of adaptive thresholding technique. This one in particular: http://www.researchgate.net/publication/220494200_Adaptive_Thresholding_using_the_Integral_Image/links/00b7d52b9d30a2108d000000 – rayryeng Dec 04 '14 at 16:13
  • Contrast stretching is of absolutely no use: when the transfer function is monotonous, binarization has exactly the same effect. (x > a iff f(x) > f(a)). –  Mar 01 '19 at 21:00

3 Answers3

9

Try clustering the image by gray level using a clustering method such as kmeans. Below I've used kmeans directly on the images without any gray level transformations (using 3 clusters gave me better results). You should be able to improve results by clustering a preprocessed image using methods outlined in the comments.

enter image description here enter image description here

Shape of the clusters may slightly vary due to the randomness of kmeans.

Now if you take connected components of the clustered image and calculate the average gray level of those regions, the defects should have a lower average than the other regions.

I did clustering part in Matlab.

im = imread('r2SOV.png');%Uy1Fq r2SOV
gr = im;
size = size(gr);

% perform closing using a 5x5 circular structuring element
sel = strel('disk', 2, 4);
mcl = imclose(gr, sel);
% cluster gray levels using kmeans: using 3 clusters
x = double(mcl(:));
idx = kmeans(x, 3);
cl = reshape(idx, size);

figure, imshow(label2rgb(cl))
dhanushka
  • 10,492
  • 2
  • 37
  • 47
4

As people said in your comment, you can change the brightness in a negative way and push up the contrast.

Moreover, the sharpen filter is also very useful for your case. You can do this in OpenCV.

Community
  • 1
  • 1
Alto
  • 522
  • 6
  • 13
4

I think you should try adaptiveThreshold function with a large window.

#include "opencv2/opencv.hpp"
using namespace cv;
int main(int argc,char** argv )
{

    Mat im = imread("c:/data/img1.png",0);
    cv::namedWindow("ctrl");
    int win=62;
    int th=2100;
    cv::createTrackbar( "win", "ctrl", &win, 500);
    cv::createTrackbar( "th", "ctrl", &th, 10000);
    while(true)
    {
        Mat thresh;
        medianBlur(im,thresh,15);//helps smooth out smaller noises, which you could also remove by size instead of this way
        adaptiveThreshold(thresh,thresh,255,ADAPTIVE_THRESH_MEAN_C,THRESH_BINARY,win*2+1,(    th/1000.));
        imshow("thresh",thresh);
        if(waitKey(1)==27)
            exit(0);
    }
}

all results here (http://www.datafilehost.com/d/99e3d86c) You might also want to take a look at imagej which implements a bunch of auto-threshold algorithms. I think what you need is something that takes local image information into account.

enter image description here enter image description here

Zaw Lin
  • 5,629
  • 1
  • 23
  • 41