상세 설명 은 OpenCV 를 이용 하여 그림 의 사각형 영역 추출(PPT 화면 등)

머리말
최근 에 큰 프로젝트 에 참 가 했 는데 제목 은 컴퓨터 시각 과 관련 되 고 선배 가 이미 지 를 수정 하 는 블 로그 링크 를 보 내 서 이 문제 로 OpenCV 에 입문 할 계획 이다.
문 제 를 분석 하 다
사진 속 의 PPT 구역 은 항상 x,y,z 세 축 을 따라 경사 가 있다(아래 그림).사진 을 평행 위치 로 뒤 집 으 려 면 투시 변환 이 필요 하고 투시 변환 은 같은 픽 셀 점 에서 전후의 좌 표를 바 꿔 야 한다.이 를 통 해 알 수 있 듯 이 사각형 구역 의 네 각 의 좌 표를 추출 하여 변환 전의 좌표 로 하고 변 경 된 좌 표 는 사진 의 네 구석 으로 설정 할 수 있 으 며 투영 변 화 를 통 해 사각형 구역 은 뒤 집 히 고 이미지 가 가득 할 것 이다.

그래서 우리 가 해결 해 야 할 문 제 는 사각형 의 네 구석 을 추출 하고 투시 변환 을 하 는 것 으로 바 뀌 었 다.
직사각형 구석 좌표 추출
사각형 의 검 측 은 주로 추출 가장자리 이 고 PPT 디 스 플레이 부분의 밝 기 는 보통 주변 환경 보다 높 습 니 다.우 리 는 그림 의 한도 값 화 를 통 해 PPT 부분 과 주변 환경 을 뚜렷하게 구분 할 수 있 습 니 다.이것 은 뒤의 가장자리 검 측 에 매우 도움 이 됩 니 다.
직사각형 을 검 측 하고 좌 표를 추출 하려 면 이미지 에 대해 예비 처리,가장자리 검 측,윤곽 추출,돌출 가방 검 측,각 점 검 측 을 해 야 한다.
예비 처리
휴대 전화 로 찍 은 사진 의 픽 셀 이 높 을 수 있 기 때문에 처리 속 도 를 높이 기 위해 우 리 는 먼저 사진 을 줄 였 고 여 기 는 4 배 줄 었 다.

pyrDown(srcPic,   shrinkedPic);    //           
pyrDown(shrinkedPic, shrinkedPic);  
그 레이스 케 일 로 전환

cvtColor(shrinkedPic, greyPic, COLOR_BGR2GRAY); //      
중간 값 필터

medianBlur(greyPic, greyPic, 7); //    
2 값 그림 으로 전환

threshold(greyPic, binPic, 80, 255, THRESH_BINARY); //        
이때 그림 은 이미 이렇게 변 했다.

PPT 부분 이 환경 과 분 리 된 것 을 알 수 있다.
테두리 검출 및 윤곽 처리
Canny 테두리 검출 진행

Canny(binPic, cannyPic, cannyThr, cannyThr*FACTOR); //Canny    
여기cannyThr = 200, FACTOR = 2.5가장자리 특징 이 너무 뚜렷 하기 때문에 계 수 는 100-600 범위(구체 적 인 숫자 는 차이 가 있 을 수 있 고 어차피 범위 가 매우 크다)에서 발생 하 는 효 과 는 거의 같다.
윤곽 추출

vector<vector<Point>> contours;  //    
vector<Vec4i> hierarchy;
  
findContours(cannyPic, contours, hierarchy, RETR_CCOMP, CHAIN_APPROX_SIMPLE);  //    
findContour함수 원형 은 다음 과 같다.

CV_EXPORTS_W void findContours( InputOutputArray image, OutputArrayOfArrays contours,
               OutputArray hierarchy, int mode,
              int method, Point offset = Point());
검 측 된 윤곽 이 모두 존재 합 니 다contours.각 윤곽 은 하나 로 저 장 됩 니 다vector<Point>.hierarchy그림 의 토폴로지 정 보 를 포함 하여 선택 할 수 있 는 출력 벡터 입 니 다.여 기 는 사용 하지 않 는 것 을 선택 할 수 있 습 니 다.
우 리 는 반복 적 으로 호출drawContours함수 로 윤곽 을 그 릴 수 있다.

linePic = Mat::zeros(cannyPic.rows, cannyPic.cols, CV_8UC3);
for (int index = 0; index < contours.size(); index++){    
    drawContours(linePic, contours, index, Scalar(rand() & 255, rand() & 255, rand() & 255), 1, 8/*, hierarchy*/);
}
drawContours함수 원형:

CV_EXPORTS_W void drawContours( InputOutputArray image, InputArrayOfArrays contours,
              int contourIdx, const Scalar& color,
              int thickness = 1, int lineType = LINE_8,
              InputArray hierarchy = noArray(),
              int maxLevel = INT_MAX, Point offset = Point() );
작용 은contours중의 제contourIdx조 윤곽 을color색 으로imagethickness선의 굵기,contourIdx음수 일 때 모든 윤곽 을 그 리 는 것 이다
여기 서 주의해 야 할 것 은 윤곽 을 그리 기 전에 출력 행렬 에 공간 을 미리 배정 해 야 합 니 다.그렇지 않 으 면 다음 과 같은 오류 가 발생 할 수 있 습 니 다.
OpenCV(3.4.1) Error: Assertion failed (size.width>0 && size.height>0) in cv::imshow, file C:\build\master_winpack-build-win64-vc15\opencv\modules\highgui\src\window.cpp, line 356

면적 이 가장 큰 윤곽 을 추출 하고 다각형 으로 윤곽 을 포위 합 니 다.
위의 윤곽 도 에서 보 듯 이 PPT 의 사각형 은 이미 그림 의 주요 부분 이 되 었 고 그 다음 의 사 고 는 면적 이 가장 큰 윤곽 을 추출 하여 사각형 의 윤곽 을 얻 는 것 이다.

vector<vector<Point>> polyContours(contours.size());
int maxArea = 0;
for (int index = 0; index < contours.size(); index++){    
    if (contourArea(contours[index]) > contourArea(contours[maxArea]))
      maxArea = index;    
    approxPolyDP(contours[index], polyContours[index], 10, true);
  }
contourArea윤곽 의 면적 을 계산 하 는 데 쓰 인 다.approxPolyDP다각형 으로 윤곽 을 둘러싸 고 엄격 한 사각형 을 얻 을 수 있어 각 점 을 찾 는 데 도움 이 된다.
직사각형 을 그 려 도 마찬가지 로 미리Mat공간 을 분배 해 야 한다.

Mat polyPic = Mat::zeros(shrinkedPic.size(), CV_8UC3);
drawContours(polyPic, polyContours, maxArea, Scalar(0,0,255/*rand() & 255, rand() & 255, rand() & 255*/), 2);

그림 과 같이 다음 에 우 리 는 네 개의 각 의 좌 표를 추출 하기 만 하면 된다.
볼록 가방 찾기

vector<int> hull;
convexHull(polyContours[maxArea], hull, false);  //        
convexHull함수 원형

CV_EXPORTS_W void convexHull( InputArray points, OutputArray hull,
              bool clockwise = false, bool returnPoints = true );
hull는 출력 매개 변수 이 고clockwise볼록 가방 은 시계 반대 방향 으로 결정 한다.returnPoints진짜 일 때 볼록 가방 의 각 점 을 되 돌려 주 고 그렇지 않 으 면 각 점 의 지 수 를 되 돌려 준다.hull유형vector<int>으로 할 수 있 습 니 다.이때 돌출 점 이 원 그림 에 있 는 아래 표 시 된 색인 을 되 돌려 줍 니 다.
우 리 는 점 과 다각형 을 원 그림 에 추가 하여 효 과 를 볼 수 있다.

for (int i = 0; i < hull.size(); ++i){
    circle(polyPic, polyContours[maxArea][i], 10, Scalar(rand() & 255, rand() & 255, rand() & 255), 3);
  }
addWeighted(polyPic, 0.5, shrinkedPic, 0.5, 0, shrinkedPic);

지금 우 리 는 이미 비교적 정확하게 필요 한 점 을 얻 었 으 니,다음은 이 점 을 이용 하여 좌 표를 매 핑 해 야 한다.
투영 변환
투영 변환 은 픽 셀 이 두 좌표계 의 좌표 에 일일이 대응 해 야 한다.비록 우 리 는 이미 네 개의 좌 표를 가지 고 있 지만 그들의 위 치 를 구분 하지 못 했다.
새 두 배열

Point2f srcPoints[4], dstPoints[4];
dstPoints[0] = Point2f(0, 0);
dstPoints[1] = Point2f(srcPic.cols, 0);
dstPoints[2] = Point2f(srcPic.cols, srcPic.rows);
dstPoints[3] = Point2f(0, srcPic.rows);
dstPoints저 장 된 것 은 변 경 된 각 점 의 좌표 이 고 왼쪽 위,오른쪽 위,오른쪽 아래,왼쪽 아래 이다.srcPoints위 에서 얻 은 네 개의 각 의 좌 표를 저장 합 니 다.
다음은 얻 은 네 가지 점 을 처리 하 겠 습 니 다.

for (int i = 0; i < 4; i++){
  polyContours[maxArea][i] = Point2f(polyContours[maxArea][i].x * 4, polyContours[maxArea][i].y * 4); //       
}
    //                      
bool sorted = false;
int n = 4;
while (!sorted){
  for (int i = 1; i < n; i++){
  sorted = true;
    if (polyContours[maxArea][i-1].x > polyContours[maxArea][i].x){
      swap(polyContours[maxArea][i-1], polyContours[maxArea][i]);
      sorted = false;
    }
  }
  n--;
}
if (polyContours[maxArea][0].y < polyContours[maxArea][1].y){
  srcPoints[0] = polyContours[maxArea][0];
  srcPoints[3] = polyContours[maxArea][1];
}
else{
  srcPoints[0] = polyContours[maxArea][1];
  srcPoints[3] = polyContours[maxArea][0];
}

if (polyContours[maxArea][9].y < polyContours[maxArea][10].y){
  srcPoints[1] = polyContours[maxArea][2];
  srcPoints[2] = polyContours[maxArea][3];
}
else{
  srcPoints[1] = polyContours[maxArea][3];
  srcPoints[2] = polyContours[maxArea][2];
}
즉,먼저 네 개의 점 의 x 좌 표를 거품 정렬 하여 좌우 로 나 눈 다음 에 두 쌍 의 좌표 의 y 값 에 따라 상하 로 나 누 는 것 이다.
(필 자 는 볼록 가방 의 시계 반대 방향 순서 와 볼록 가방 점 과 원점 의 거 리 를 통 해 위치 정 보 를 살 려 고 했 지만 모두 실패 로 끝났다)
좌표 변환 은 행렬 연산 이 필요 합 니 다.OpenCV 에서 우리 에 게getPerspectiveTransform함 수 를 제공 하여 행렬 을 얻 을 수 있 습 니 다.

Mat transMat = getPerspectiveTransform(srcPoints, dstPoints); //      
그 다음 에 좌표 변환 을 할 때 인터넷 에서 찾 아 낸 절 차 는 모두perspectiveTransform함수 로 바 뀌 었 지만 여러 번 오류 가 발생 했 습 니 다.구 글 은 오 랜 시간 이 지나 서 야 원래 이 함수 의 입력 과 출력 매개 변수 가 모두 점 집합 이라는 것 을 알 게 되 었 습 니 다.우리 의 이 장면 은 사용 하기에 비교적 귀 찮 습 니 다.
한편,warpPerspective함 수 는 입력Mat유형의 데 이 터 를 직접 입력 할 수 있어 서 비교적 편리 하 다.

warpPerspective(srcPic, outPic, transMat, srcPic.size()); //      
매개 변 수 는 입 출력 이미지,변환 매트릭스,크기 입 니 다.
좌표 가 바 뀌 면 우리 가 원 하 는 최종 그림 을 얻 을 수 있다.

총결산
우 리 는 스크린 밝기 가 비교적 높 은 특징 을 이용 하여 이치 화 를 통 해 윤곽 을 돋 보이 게 하고 좌 표를 추출 하여 투시 변환 을 한다.
그러나 직사각형의 밝기 가 배경 과 크게 다 르 지 않 으 면 이런 방법 으로 윤곽 을 감지 하기 어렵 다 는 한계 가 있다.
이상 이 바로 본 고의 모든 내용 입 니 다.여러분 의 학습 에 도움 이 되 고 저 희 를 많이 응원 해 주 셨 으 면 좋 겠 습 니 다.

좋은 웹페이지 즐겨찾기