从背景中检测纸张几乎与纸张颜色相同

时间:2015-08-05 13:34:01

标签: image-processing emgucv

我正试图从图像中检测出一张纸。好吧,我从this这样的帖子中得到了帮助。但有一点不同的是,我的背景颜色几乎与纸张颜色相同,而且我得到了错误的结果。

[编辑] 错误的结果我的意思是根本没有检测到纸质轮廓轮廓。相反,最大的轮廓覆盖整个图像。

image1

image2

到目前为止我的代码(使用emgu cv和c#)

MemStorage storage = new MemStorage();
        List<Contour<Point>> candidateList = new List<Contour<Point>>();
        List<double> areaList = new List<double>();

        Image<Bgr, Byte> inputImage = new Image<Bgr, Byte>(image);
        //Rectangle roi = new Rectangle(15, 15, image.Width - 15, image.Height - 15);
        //inputImage.ROI = roi;
        //inputImage = inputImage.Copy();

        double threshHeight = inputImage.Size.Height * 0.50;
        double threshWidth = inputImage.Size.Width * 0.50;


        //std::vector<std::vector<cv::Point> > squares;
        //cv::Mat pyr, timg, gray0(_image.size(), CV_8U), gray;
        int thresh = 50, N = 5;
        //cv::pyrDown(_image, pyr, cv::Size(_image.cols/2, _image.rows/2));
        //cv::pyrUp(pyr, timg, _image.size());
        //std::vector<std::vector<cv::Point> > contours;

        Image<Gray, Byte> [] gray0 = new Image<Gray,byte>[3];


        for( int c = 0; c < 3; c++ ) {
            try{
                int [] ch = {c, 0};
                gray0[c] = new Image<Gray,byte>(inputImage.Size);
                CvInvoke.cvMixChannels(new IntPtr[] { inputImage }, 1, new IntPtr[]{gray0[c]}, 1, ch, 1);
                Image<Gray, Byte> gray = new Image<Gray,byte>(inputImage.Size);
                for (int l = 0 ; l < N ; l++){
                    if (l == 0){
                        Image<Gray, Byte> cannyImage = gray0[c].Canny(0, thresh, 5);
                        CvInvoke.cvDilate(cannyImage, gray, IntPtr.Zero, 1);
                        //CvInvoke.cvShowImage("ch " + c + "-" + l, gray);
                    }else{
                        CvInvoke.cvThreshold(gray0[c], gray, (l + 1)*255/N, 255, Emgu.CV.CvEnum.THRESH.CV_THRESH_BINARY);
                        //CvInvoke.cvShowImage("ch " + c + "-" + l, gray0[c]);
                    }

                    //CvInvoke.cvShowImage("image", gray);    

                    for (Contour<Point> contours = gray.FindContours(Emgu.CV.CvEnum.CHAIN_APPROX_METHOD.CV_CHAIN_APPROX_SIMPLE, Emgu.CV.CvEnum.RETR_TYPE.CV_RETR_LIST, storage); contours != null; contours = contours.HNext){
                        Contour<Point> currentContour = contours.ApproxPoly(contours.Perimeter * 0.02, storage);
                        if (currentContour.Count() >= 4){
                            if (currentContour.Area > 3000){
                            //if (currentContour.BoundingRectangle.Width >= threshWidth && currentContour.BoundingRectangle.Height > threshHeight){
                                candidateList.Add(currentContour);
                                areaList.Add(currentContour.Area);
                                inputImage.Draw(currentContour, new Bgr(255, 0, 0), 1);
                            }
                        }
                    }
                }




            }catch(Exception ex){
                Debug.WriteLine(ex.Message);
            }


        }

        /* finding the biggest one */
        double area = -1.0;
        Contour<Point> paper = null;
        for (int i = 0 ; i < candidateList.Count ; i++){
            if (areaList[i] > area){
                area = areaList[i];
                paper = candidateList[i];
            }
        }

        if (paper != null){
            if (paper.BoundingRectangle.Width >= threshWidth && paper.BoundingRectangle.Height > threshHeight){
                inputImage.Draw(paper, new Bgr(0, 0, 255), 2);

            }
        }
        return inputImage.ToBitmap();

请让我知道如何处理这些图片。

1 个答案:

答案 0 :(得分:0)

我是在matlab中做到这一点(抱歉,我不是真的精通OpenCV),但你应该能够模拟代码。我试着让它变得非常简单。我注意到原始图像的渐变确实突出了纸张的位置。所以我用它来制作论文的“粗略”轮廓。使用渐变是一个很好的起点,也许你可以从那里开始。我只是下采样,然后对图像进行上采样(模拟形态操作以清理图像,因为您丢失了所有小细节)。

如果您先将图像平滑(可能使用高斯滤镜),您可能会得到更好的效果。我没有尝试过,但也许您可以尝试一下。这是结果 enter image description here enter image description here

以下是参考代码

im1 = imread('http://www.imageno.com/image.php?id=ai7b91pm9fcs&kk=1089743759');
im2 = imread('http://www.imageno.com/image.php?id=k99c9xpd6phs&kk=3354581295');

%converts to grayscale
gim1 = rgb2gray(im1);
gim2 = rgb2gray(im2);

%gets size of images
[m1, n1] = size(gim1);
[m2, n2] = size(gim2);

%takes gradient of image
[Gx1, Gy1] = gradient(double(gim1));
[Gx2, Gy2] = gradient(double(gim2));

%takes magnitude of gradient in X and Y direction
Gxy1 = sqrt(Gx1.^2 + Gy1.^2);
Gxy2 = sqrt(Gx2.^2 + Gy2.^2);

%downsamples image (to reduce noise)
scale_factor = 100;
small1 = imresize(Gxy1, [m1/scale_factor n1/scale_factor]);
small2 = imresize(Gxy2, [m2/scale_factor n2/scale_factor]);

%upsamples image (to original size)
big1 = imresize(small1, [m1 n1]);
big2 = imresize(small2, [m2 n2]);

%converts to binary mask
bw1 = (big1 >= 1);
bw2 = (big2 >= 1);

%displays images
figure(1);
subplot(2,4,1);imshow(gim1);title('grayscale 1');
subplot(2,4,5);imshow(gim2);title('grayscale 2');

%these gradients are a little deceiving. In matlab when it sees an image 
%of type "double" its maps it so 0=black and 1=white. anything >=1 gets 
%clipped to 1
subplot(2,4,2);imshow(Gxy1);title('gradient 1');
subplot(2,4,6);imshow(Gxy2);title('gradient 2');

subplot(2,4,3);imshow(big1);title('down->up sampled 1');
subplot(2,4,7);imshow(big2);title('down->up sampled 2');

%this is just some matlab witchcraft so I can multiply the 2D mask with a
%3D image (r g b) in a very efficient manner
subplot(2,4,4);imshow(bsxfun(@times,im1,uint8(bw1)));title('masked image 1');
subplot(2,4,8);imshow(bsxfun(@times,im2,uint8(bw2)));title('masked image 2');