2012-10-27 16 views
8

Używam otwartego CV, w IOS. Już wykryłem granicę arkusza papieru w obrazie jako obraz na zdjęciu i teraz muszę przeciągnąć tę linię graniczną na dotyk, aby dostosować ramkę przycięcia. jak możemy dostosować linię graniczną i jak możemy przyciąć obraz wewnątrz granicy?dostosuj rogi i przyciąć obraz openCV

Jest to możliwe w openCV lub używam OpenGL do tego?

@moosgummi: Wzywam metodę w sposób poniżej

- (cv::Mat)finshWork:(cv::Mat &)image 
{ 

Mat img0 =image; 

Mat img1; 
cvtColor(img0, img1, CV_RGB2GRAY); 

// apply your filter 
Canny(img1, img1, 100, 200); 

// find the contours 
vector< vector<cv::Point> > contours; 
findContours(img1, contours, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_NONE); 


// you could also reuse img1 here 
Mat mask = Mat::zeros(img1.rows, img1.cols, CV_8UC1); 

// CV_FILLED fills the connected components found 
drawContours(mask, contours, -1, Scalar(255), CV_FILLED); 


// let's create a new image now 
Mat crop(img0.rows, img0.cols, CV_8UC3); 

// set background to green 
crop.setTo(Scalar(0,255,0)); 

// and copy the magic apple 
img0.copyTo(crop, mask); 

// normalize so imwrite(...)/imshow(...) shows the mask correctly! 
normalize(mask.clone(), mask, 0.0, 255.0, CV_MINMAX, CV_8UC1); 



std::vector<cv::Point> biggestContour = contours[contours.size()-1]; 

NSLog(@"%d",biggestContour[0].x); 
NSLog(@"%d",biggestContour[0].y); 

cv::Mat paperImage =[self getPaperAreaFromImage:image:biggestContour]; 


//return crop; 
return paperImage; 

} 

Dzięki wszystkim

enter image description here

+0

Jak utworzyć siatkę? Czy chciałbyś udostępnić przykładowy kod? Mam podobne pytania, zamieszczone tutaj: http://stackoverflow.com/questions/13269432/perspective-transform-crop-in-ios-with-opencv – mmackh

+0

@Gryphon Jestem zainteresowany tym również. Jak stworzyłeś siatkę? – alandalusi

+0

@Gryphon zobacz mój post http://stackoverflow.com/questions/13594391/ios-drawing-a-rectangle-on-an-imageview-and-adjusting-borders – alandalusi

Odpowiedz

9

Po masz rogi trzeba prostowanie papier i „wyciąg” go do nowego obrazu.

należy wykonać następujące czynności:

  1. punktów Sortuj Corner (spraw zamówień, muszą być w tej samej kolejności w obu wektorach)
  2. cv::getAffineTransform
  3. cv::warpAffine

I napisałem sobie funkcję pomocniczą, która pobiera std::vector z czterema cv::Point i sortuje je w kolejności zgodnej z ruchem wskazówek zegara, rozpoczynając od po lewej stronie.Aby uzyskać więcej informacji na ten temat spojrzeć na te wątku:

Inną rzeczą, którą należy wziąć pod uwagę jest wielkość papieru chcesz wyciąg. W moim przykładzie zakładam, że wyodrębniasz papier formatu A4 (210x297mm). Wewnątrz mojego kodu możesz edytować paperWidth i paperHeight.

Łącząc wszystko wygląda tak:

// Helper 
cv::Point getCenter(std::vector<cv::Point> points) { 

    cv::Point center = cv::Point(0.0, 0.0); 

    for(size_t i = 0; i < points.size(); i++) { 
     center.x += points[ i ].x; 
     center.y += points[ i ].y; 
    } 

    center.x = center.x/points.size(); 
    center.y = center.y/points.size(); 

    return center; 

} 

// Helper; 
// 0----1 
// | | 
// | | 
// 3----2 
std::vector<cv::Point> sortSquarePointsClockwise(std::vector<cv::Point> square) { 

    cv::Point center = getCenter(square); 

    std::vector<cv::Point> sorted_square; 
    for(size_t i = 0; i < square.size(); i++) { 
     if ((square[i].x - center.x) < 0 && (square[i].y - center.y) < 0) { 
      switch(i) { 
       case 0: 
        sorted_square = square; 
        break; 
       case 1: 
        sorted_square.push_back(square[1]); 
        sorted_square.push_back(square[2]); 
        sorted_square.push_back(square[3]); 
        sorted_square.push_back(square[0]); 
        break; 
       case 2: 
        sorted_square.push_back(square[2]); 
        sorted_square.push_back(square[3]); 
        sorted_square.push_back(square[0]); 
        sorted_square.push_back(square[1]); 
        break; 
       case 3: 
        sorted_square.push_back(square[3]); 
        sorted_square.push_back(square[0]); 
        sorted_square.push_back(square[1]); 
        sorted_square.push_back(square[2]); 
        break; 
      } 
      break; 
     } 
    } 

    return sorted_square; 

} 

// Helper 
float distanceBetweenPoints(cv::Point p1, cv::Point p2) { 

    if(p1.x == p2.x) { 
     return abs(p2.y - p1.y); 
    } 
    else if(p1.y == p2.y) { 
     return abs(p2.x - p1.x); 
    } 
    else { 
     float dx = p2.x - p1.x; 
     float dy = p2.y - p1.y; 
     return sqrt((dx*dx)+(dy*dy)); 
    } 
} 

cv::Mat getPaperAreaFromImage(cv::Mat image, std::vector<cv::Point> square) 
{ 

    // declare used vars 
    int paperWidth = 210; // in mm, because scale factor is taken into account 
    int paperHeight = 297; // in mm, because scale factor is taken into account 
    cv::Point2f imageVertices[4]; 
    float distanceP1P2; 
    float distanceP1P3; 
    BOOL isLandscape = true; 
    int scaleFactor; 
    cv::Mat paperImage; 
    cv::Mat paperImageCorrected; 
    cv::Point2f paperVertices[4]; 

    // sort square corners for further operations 
    square = sortSquarePointsClockwise(square); 

    // rearrange to get proper order for getPerspectiveTransform() 
    imageVertices[0] = square[0]; 
    imageVertices[1] = square[1]; 
    imageVertices[2] = square[3]; 
    imageVertices[3] = square[2]; 

    // get distance between corner points for further operations 
    distanceP1P2 = distanceBetweenPoints(imageVertices[0], imageVertices[1]); 
    distanceP1P3 = distanceBetweenPoints(imageVertices[0], imageVertices[2]); 

    // calc paper, paperVertices; take orientation into account 
    if (distanceP1P2 > distanceP1P3) { 
     scaleFactor = ceil(lroundf(distanceP1P2/paperHeight)); // we always want to scale the image down to maintain the best quality possible 
     paperImage = cv::Mat(paperWidth*scaleFactor, paperHeight*scaleFactor, CV_8UC3); 
     paperVertices[0] = cv::Point(0, 0); 
     paperVertices[1] = cv::Point(paperHeight*scaleFactor, 0); 
     paperVertices[2] = cv::Point(0, paperWidth*scaleFactor); 
     paperVertices[3] = cv::Point(paperHeight*scaleFactor, paperWidth*scaleFactor); 
    } 
    else { 
     isLandscape = false; 
     scaleFactor = ceil(lroundf(distanceP1P3/paperHeight)); // we always want to scale the image down to maintain the best quality possible 
     paperImage = cv::Mat(paperHeight*scaleFactor, paperWidth*scaleFactor, CV_8UC3); 
     paperVertices[0] = cv::Point(0, 0); 
     paperVertices[1] = cv::Point(paperWidth*scaleFactor, 0); 
     paperVertices[2] = cv::Point(0, paperHeight*scaleFactor); 
     paperVertices[3] = cv::Point(paperWidth*scaleFactor, paperHeight*scaleFactor); 
    } 

    cv::Mat warpMatrix = getPerspectiveTransform(imageVertices, paperVertices); 
    cv::warpPerspective(_image, paperImage, warpMatrix, paperImage.size(), cv::INTER_LINEAR, cv::BORDER_CONSTANT); 

    // we want portrait output 
    if (isLandscape) { 
     cv::transpose(paperImage, paperImageCorrected); 
     cv::flip(paperImageCorrected, paperImageCorrected, 1); 
     return paperImageCorrected; 
    } 

    return paperImage; 

} 

Zastosowanie:

// ... get paper square ... 

cv::Mat paperImage = getPaperAreaFromImage(srcImage, paperSquare); 
+0

dziękuję za odpowiedź, próbuję tego – QueueOverFlow

+0

Dziękuję za pomoc, wykonano ją bezbłędnie, ale obraz nie jest wyświetlany na wyjściu, czasami obraz jest czarny, a czasami szary, jakiś pomysł? – QueueOverFlow

+0

Edytowałem pytanie, proszę spojrzeć. – QueueOverFlow

1

Co należy zrobić, to:

  1. RSS 4 rogach, które znalazłeś i 4 prawdziwe rogi obrazu do cv::getPerspectiveTransform. Da ci matrycę transformacji perspektywy, która wypaczy czworokąt na cały obraz.

  2. Użyj cv::WarpPerspective, aby utworzyć obraz, który chcesz.

Linki prowadzą do dokumentacji.

EDYCJA: Można użyć cv::findHomography, aby wykonać krok 1. Ale jest to więcej o wielu odpowiednich punktów i wartości odstających.

EDYCJA: Oto przykład. Jest z interfejsem C, ale można łatwo dotrzeć do pracy z C++

#include <stdio.h> 
#include "highgui.h" 
#include "cv.h" 

int main(int argc, char** argv) { 
    // cvLoadImage determines an image type and creates datastructure with appropriate size 
    IplImage* img = cvLoadImage(argv[1], CV_LOAD_IMAGE_COLOR); 
    IplImage* img1 = cvCreateImage(
      cvSize(img->width, img->height), 
      img->depth, 
      img->nChannels 
      ); 

    cvNamedWindow("out", CV_WINDOW_AUTOSIZE); 
    cvShowImage("out", img1); 
    // create a window. Window name is determined by a supplied argument 
    cvNamedWindow(argv[1], CV_WINDOW_AUTOSIZE); 
    // Display an image inside and window. Window name is determined by a supplied argument 
    cvShowImage(argv[1], img); 

    // The part you need 
    // Here is the points that you take the image from (the small quadrangle) 
    CvPoint2D32f first[4] = { 
     {0,0}, 
     {(img->width /4)* 3, img->height /4 }, 
     { img->width /4 ,(img->height /4) *3}, 
     {(img->width /4)* 3,(img->height /4) *3}, 
    }; 
    // Here are the points that you draw the quadrangle into (the four corners) 
    CvPoint2D32f second[4] = { 
     {0,0}, 
     {img->width,0}, 
     {0,img->height}, 
     {img->width,img->height} 
    }; 
    // The part you need 
    CvMat *transform = cvCreateMat(3,3, CV_32F); 
    cvGetPerspectiveTransform(first,second, transform); 
    cvWarpPerspective(img, img1, transform, CV_INTER_LINEAR+CV_WARP_FILL_OUTLIERS, 
     cvScalarAll(0)); 
    // End of part you need 

    cvShowImage("out", img1); 


    // wait indefinitely for keystroke 
    cvWaitKey(0); 
    // release pointer to an object 
    cvReleaseImage(&img); 
    // Destroy a window 
    cvDestroyWindow(argv[1]); 
} 

Należy wymienić tablicę first z punktów końcowych czworokąta zostały znalezione.

EDYCJA: Oto niektóre próbki. Nie wyglądam ich zbyt dobrze.

Geometric Image Transformations

cvGetPerspectiveTransform

+0

możesz podać dowolny link lub przykład dotyczący tego. – QueueOverFlow

+0

Dodano przykład kodu. –

Powiązane problemy