2017-12-02 284 views
1

inputImage的OpenCV的抗扭斜的輪廓

enter image description here

ResultImage

enter image description here

我已經能夠過濾最大的輪廓圖像中檢測令牌。

我已經應用了經紗知覺,但它只是在輪廓的邊緣裁剪圖像,沒有別的。

我想要將檢測到的令牌從圖像的其餘部分中裁剪出來,在保持比例的情況下對其進行去偏斜,以便結果圖像應該直立,筆直。然後,我將繼續尋找令牌中的斑點來檢測其中標記的日期。

private Mat processMat(Mat srcMat) { 
    Mat processedMat = new Mat(); 
    Imgproc.cvtColor(srcMat, processedMat, Imgproc.COLOR_BGR2GRAY); 
    Imgproc.GaussianBlur(processedMat, processedMat, new Size(5, 5), 5); 
    Imgproc.threshold(processedMat, processedMat, 127, 255, Imgproc.THRESH_BINARY); 
    List<MatOfPoint> contours = new ArrayList<>(); 
    Mat hierarchy = new Mat(); 
    Imgproc.findContours(processedMat, contours, hierarchy, Imgproc.RETR_EXTERNAL, Imgproc.CHAIN_APPROX_SIMPLE); 

    double maxVal = 0; 
    int maxValIdx = 0; 
    for (int contourIdx = 0; contourIdx < contours.size(); contourIdx++) { 
     double contourArea = Imgproc.contourArea(contours.get(contourIdx)); 
     if (maxVal < contourArea) { 
      maxVal = contourArea; 
      maxValIdx = contourIdx; 
     } 
    } 
    if (!contours.isEmpty()) { 
     Imgproc.drawContours(srcMat, contours, maxValIdx, new Scalar(0,255,0), 3); 
     Rect rect = Imgproc.boundingRect(contours.get(maxValIdx)); 
     Log.e("rect", "" + rect); 
     int top = srcMat.height(); 
     int left = srcMat.width(); 
     int right = 0; 
     int bottom = 0; 

     if(rect.x < left) { 
      left = rect.x; 
     } 
     if(rect.x+rect.width > right){ 
      right = rect.x+rect.width; 
     } 
     if(rect.y < top){ 
      top = rect.y; 
     } 
     if(rect.y+rect.height > bottom){ 
      bottom = rect.y+rect.height; 
     } 

     Point topLeft = new Point(left, top); 
     Point topRight = new Point(right, top); 
     Point bottomRight = new Point(right, bottom); 
     Point bottomLeft = new Point(left, bottom); 

     return warp(srcMat, topLeft, topRight, bottomLeft, bottomRight); 
    } 
    return null; 
} 

Mat warp(Mat inputMat, Point topLeft, Point topRight, Point bottomLeft, Point bottomRight) { 
    int resultWidth = (int)(topRight.x - topLeft.x); 
    int bottomWidth = (int)(bottomRight.x - bottomLeft.x); 
    if(bottomWidth > resultWidth) 
     resultWidth = bottomWidth; 

    int resultHeight = (int)(bottomLeft.y - topLeft.y); 
    int bottomHeight = (int)(bottomRight.y - topRight.y); 
    if (bottomHeight > resultHeight) { 
     resultHeight = bottomHeight; 
    } 

    Mat outputMat = new Mat(resultWidth, resultHeight, CvType.CV_8UC1); 

    List<Point> source = new ArrayList<>(); 
    source.add(topLeft); 
    source.add(topRight); 
    source.add(bottomLeft); 
    source.add(bottomRight); 
    Mat startM = Converters.vector_Point2f_to_Mat(source); 

    Point ocvPOut1 = new Point(0, 0); 
    Point ocvPOut2 = new Point(resultWidth, 0); 
    Point ocvPOut3 = new Point(0, resultHeight); 
    Point ocvPOut4 = new Point(resultWidth, resultHeight); 
    List<Point> dest = new ArrayList<>(); 
    dest.add(ocvPOut1); 
    dest.add(ocvPOut2); 
    dest.add(ocvPOut3); 
    dest.add(ocvPOut4); 
    Mat endM = Converters.vector_Point2f_to_Mat(dest); 

    Mat perspectiveTransform = Imgproc.getPerspectiveTransform(startM, endM); 

    Imgproc.warpPerspective(inputMat, outputMat, perspectiveTransform, new Size(resultWidth, resultHeight)); 
    return outputMat; 
} 

更新1

替換此:

 return warp(srcMat, topLeft, topRight, bottomLeft, bottomRight); 

這一點:

 return warp(srcMat, topLeft, topRight, bottomRight, bottomLeft); 

結果更新1:

Result Now

更新2

public Mat warp(Mat inputMat, MatOfPoint selectedContour) { 
    MatOfPoint2f new_mat = new MatOfPoint2f(selectedContour.toArray()); 
    MatOfPoint2f approxCurve_temp = new MatOfPoint2f(); 
    int contourSize = (int) selectedContour.total(); 
    Imgproc.approxPolyDP(new_mat, approxCurve_temp, contourSize * 0.05, true); 

    double[] temp_double; 
    temp_double = approxCurve_temp.get(0,0); 
    Point p1 = new Point(temp_double[0], temp_double[1]); 
    temp_double = approxCurve_temp.get(1,0); 
    Point p2 = new Point(temp_double[0], temp_double[1]); 
    temp_double = approxCurve_temp.get(2,0); 
    Point p3 = new Point(temp_double[0], temp_double[1]); 
    temp_double = approxCurve_temp.get(3,0); 
    Point p4 = new Point(temp_double[0], temp_double[1]); 
    List<Point> source = new ArrayList<Point>(); 
    source.add(p1); 
    source.add(p2); 
    source.add(p3); 
    source.add(p4); 
    Mat startM = Converters.vector_Point2f_to_Mat(source); 

    int resultWidth = 846; 
    int resultHeight = 2048; 

    Mat outputMat = new Mat(resultWidth, resultHeight, CvType.CV_8UC4); 

    Point ocvPOut1 = new Point(0, 0); 
    Point ocvPOut2 = new Point(0, resultHeight); 
    Point ocvPOut3 = new Point(resultWidth, resultHeight); 
    Point ocvPOut4 = new Point(resultWidth, 0); 
    List<Point> dest = new ArrayList<Point>(); 
    dest.add(ocvPOut1); 
    dest.add(ocvPOut2); 
    dest.add(ocvPOut3); 
    dest.add(ocvPOut4); 
    Mat endM = Converters.vector_Point2f_to_Mat(dest); 

    Mat perspectiveTransform = Imgproc.getPerspectiveTransform(startM, endM); 

    Imgproc.warpPerspective(inputMat, outputMat, perspectiveTransform, new Size(resultWidth, resultHeight), 
      Imgproc.INTER_CUBIC); 
    return outputMat; 
} 

結果更新2:

我已經改變了我的經功能的位和代碼附加。 然而,合成圖像以某種方式在錯誤的方向上旋轉。你能指導我這是做這件事的正確方法嗎?

Android設備方向設置爲:縱向,輸入圖像也是縱向。

result_update_2

更新3

我設法通過排序的角落,像這樣伸直令牌:

List<Point> source = new ArrayList<Point>(); 
    source.add(p2); 
    source.add(p3); 
    source.add(p4); 
    source.add(p1); 
    Mat startM = Converters.vector_Point2f_to_Mat(source); 

結果更新3:

Result Update 3

然而,由此產生的圖像從左側裁剪,我不知道如何解決這個問題。 如果令牌向右或向左傾斜並且輸出圖像是筆直的,我已設法拉直輸入圖像。但是,如果輸入圖像已經將令牌居中且直線向上。它旋轉像這樣的令牌,使用相同的代碼:

問題更新3:

Issue Update 3

回答

1

到糾偏票轉型是接近仿射之一。您可以通過用平行四邊形近似輪廓來獲得它。您可以將平行四邊形的頂點作爲最左邊,最頂端,最右邊和最底部的點。其實,你只需要三個頂點(第四個可以從這些頂點重新計算)。也許平行四邊形的最小二乘擬合是可能的,我不知道。

另一種選擇是考慮從四個點定義的單應變換(但計算更復雜)。它將考慮到視角。 (您可能會在這裏獲得一些見解:https://www.codeproject.com/Articles/674433/Perspective-Projection-of-a-Rectangle-Homography。)

要整理圖像,只需應用逆變換並檢索矩形即可。無論如何,你會注意到這個矩形的大小是未知的,所以你可以任意縮放它。最難的問題是找到合適的寬高比。

+0

我以爲'warpPerception'用於我想在這裏實現的目的。我確實有一個輪廓包裹了我想要糾正的標記區域,我是否無法從我已經過濾的輪廓中找到角落?你能否看看我發佈的代碼,並分享這個錯誤,因爲它只是裁剪基於輪廓拐角的主圖像,而不是拉直它。 –

+0

@MohsinFalak「我不能從我已經過濾的輪廓中找到角落嗎?」:你真的看過我的帖子嗎? –

+0

我對於成爲一名業餘人士表示歉意,但我對OpenCV感到新鮮,並且您的幫助正在一步一步地引起人們的期待。我已經在這個問題上取得了一些進展,問題得到了更新。你能看看並幫我解決這個問題嗎? –