最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

詳解利用OpenCV提取圖像中的矩形區(qū)域(PPT屏幕等)

 更新時間:2019年07月01日 14:22:03   作者:才才才  
這篇文章主要介紹了詳解利用OpenCV提取圖像中的矩形區(qū)域(PPT屏幕等),小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧

前言

最近參加了大創(chuàng)項目,題目涉及到計算機視覺,學姐發(fā)了個修正圖像的博客鏈接,于是打算用這個題目入門OpenCV。

分析問題

照片中的PPT區(qū)域總是沿著x,y,z三個軸都有傾斜(如下圖),要想把照片翻轉(zhuǎn)到平行位置,需要進行透視變換,而透視變換需要同一像素點變換前后的坐標。由此可以想到,提取矩形區(qū)域四個角的坐標作為變換前的坐標,變換后的坐標可以設為照片的四個角落,經(jīng)過投影變換,矩形區(qū)域?qū)D(zhuǎn)并充滿圖像。

因此我們要解決的問題變?yōu)椋禾崛【匦蔚乃膫€角落、進行透視變換。

提取矩形角落坐標

矩形的檢測主要是提取邊緣,PPT顯示部分的亮度通常高于周圍環(huán)境,我們可以將圖片閾值化,將PPT部分與周圍環(huán)境明顯的分別開來,這對后邊的邊緣檢測非常有幫助。

檢測矩形并提取坐標需要對圖像進行預處理、邊緣檢測、提取輪廓、檢測凸包、角點檢測。

預處理

由于手機拍攝的照片像素可能會很高,為了加快處理速度,我們首先縮小圖片,這里縮小了4倍。

pyrDown(srcPic,   shrinkedPic);    //減小尺寸 加快運算速度
pyrDown(shrinkedPic, shrinkedPic);  

轉(zhuǎn)化為灰度圖

cvtColor(shrinkedPic, greyPic, COLOR_BGR2GRAY); //轉(zhuǎn)化為灰度圖

中值濾波

medianBlur(greyPic, greyPic, 7); //中值濾波

轉(zhuǎn)為二值圖片

threshold(greyPic, binPic, 80, 255, THRESH_BINARY); //閾值化為二值圖片

此時圖片已經(jīng)變成了這個樣子:

可見PPT部分已經(jīng)與環(huán)境分離開來。

邊緣檢測與輪廓處理

進行Canny邊緣檢測

Canny(binPic, cannyPic, cannyThr, cannyThr*FACTOR); //Canny邊緣檢測

這里 cannyThr = 200, FACTOR = 2.5
可能由于邊緣特征過于明顯,系數(shù)在100-600范圍(具體數(shù)字可能有出入,反正范圍非常大)內(nèi)產(chǎn)生的效果幾乎相同。

提取輪廓

vector<vector<Point>> contours;  //儲存輪廓
vector<Vec4i> hierarchy;
  
findContours(cannyPic, contours, hierarchy, RETR_CCOMP, CHAIN_APPROX_SIMPLE);  //獲取輪廓

findContour函數(shù)原型如下:

CV_EXPORTS_W void findContours( InputOutputArray image, OutputArrayOfArrays contours,
               OutputArray hierarchy, int mode,
              int method, Point offset = Point());

檢測到的輪廓都存在contours里,每個輪廓保存為一個vector<Point>
hierarchy為可選的輸出向量,包括圖像的拓撲信息,這里可以選擇不用。

我們可以反復調(diào)用drawContours函數(shù)將輪廓畫出

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函數(shù)原型:

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顏色繪制到image中,thickness為線條的粗細, contourIdx為負數(shù)時畫出所有輪廓

這里要注意的是在繪制輪廓前要提前為輸出矩陣分配空間,否則會出現(xiàn)以下錯誤

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的矩形已經(jīng)成為了圖片的主要部分,接下來的思路是提取面積最大的輪廓,得到矩形輪廓。

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函數(shù)原型

CV_EXPORTS_W void convexHull( InputArray points, OutputArray hull,
              bool clockwise = false, bool returnPoints = true );

hull為輸出參數(shù), clockwise決定凸包順逆時針方向, returnPoints為真時返回凸包的各個點,否則返回各點的指數(shù)
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);

現(xiàn)在我們已經(jīng)比較準確地獲得了需要的點,下面就要利用這些點進行坐標映射。

投影變換

投影變換需要像素在兩個坐標系中的坐標一一對應,雖然我們已經(jīng)有了四個坐標,但還沒有區(qū)分它們的位置。

新建兩個數(shù)組

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坐標進行冒泡排序分出左右,再根據(jù)兩對坐標的y值比較分出上下
(筆者試圖通過凸包的順逆時針順序以及凸包點與原點的距離來活得位置信息,卻均以失敗告終)

坐標變換需要矩陣運算,OpenCV中給我們提供了getPerspectiveTransform函數(shù)用來得到矩陣

Mat transMat = getPerspectiveTransform(srcPoints, dstPoints); //得到變換矩陣

接下來進行坐標變換,網(wǎng)上查到的步驟都是通過perspectiveTransform函數(shù)變換,但嘗試多次都出現(xiàn)了報錯,Google了好長時間才知道原來這個函數(shù)的傳入輸入輸出參數(shù)均為點集,我們這個場景用起來比較麻煩。

warpPerspective函數(shù)可以直接傳入輸入Mat類型數(shù)據(jù),比較方便

warpPerspective(srcPic, outPic, transMat, srcPic.size()); //進行坐標變換

參數(shù)分別為輸入輸出圖像、變換矩陣、大小。

坐標變換后就得到了我們要的最終圖像。

總結

我們利用了屏幕亮度較高的特點,通過二值化突出輪廓提取坐標,進行透視變換。

但局限性在于,如果矩形的亮度與背景相差不大,就很難用這種方法檢測到輪廓。

以上就是本文的全部內(nèi)容,希望對大家的學習有所幫助,也希望大家多多支持腳本之家。

相關文章

  • 對python中return與yield的區(qū)別詳解

    對python中return與yield的區(qū)別詳解

    這篇文章主要介紹了對python中return與yield的區(qū)別詳解,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2020-03-03
  • 如何將自己的python庫打包成wheel文件并上傳到pypi

    如何將自己的python庫打包成wheel文件并上傳到pypi

    這篇文章主要介紹了如何將自己的python庫打包成wheel文件并上傳到pypi,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2021-04-04
  • 微信跳一跳自動運行python腳本

    微信跳一跳自動運行python腳本

    這篇文章主要為大家詳細介紹了微信小程序跳一跳自動運行腳本,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2018-01-01
  • python中mpi4py的所有基礎使用案例詳解

    python中mpi4py的所有基礎使用案例詳解

    這篇文章主要介紹了python中mpi4py的所有基礎使用,本文通過10個案例給大家詳細講解,結合實例代碼給大家介紹的非常詳細,需要的朋友可以參考下
    2022-08-08
  • 十個Python經(jīng)典小游戲的代碼合集

    十個Python經(jīng)典小游戲的代碼合集

    這篇文章主要為大家分享十個Python經(jīng)典的小游戲代碼,非常適合Python初學者練手。文中的示例代碼講解詳細,感興趣的小伙伴可以嘗試一下
    2022-05-05
  • Python實現(xiàn)操作Redis的高級用法分享

    Python實現(xiàn)操作Redis的高級用法分享

    redis-py是Python操作Redis的第三方庫,它提供了與Redis服務器交互的API,本文為大家介紹了Python利用redis-py操作Redis的高級用法,需要的可以收藏一下
    2023-05-05
  • python集合的創(chuàng)建、添加及刪除操作示例

    python集合的創(chuàng)建、添加及刪除操作示例

    這篇文章主要介紹了python集合的創(chuàng)建、添加及刪除操作,結合實例形式分析了Python集合的概念、功能及針對集合的創(chuàng)建、添加與刪除等相關操作實現(xiàn)技巧,需要的朋友可以參考下
    2019-10-10
  • python中cv2模塊如何安裝使用

    python中cv2模塊如何安裝使用

    這篇文章主要給大家介紹了關于python中cv2模塊如何安裝的相關資料,CV2指的是OpenCV2(Open?Source?Computer?Vision?Library),是一個開源的庫平臺計算機視覺庫,有很強大的圖片處理功能,可實現(xiàn)圖像處理和計算機視覺方面的很多通用算法,需要的朋友可以參考下
    2023-10-10
  • python按照行來讀取txt文件全部內(nèi)容(去除空行處理掉\t,\n后以列表方式返回)

    python按照行來讀取txt文件全部內(nèi)容(去除空行處理掉\t,\n后以列表方式返回)

    這篇文章主要介紹了python按照行來讀取txt文件全部內(nèi)容 ,去除空行,處理掉\t,\n后,以列表方式返回,本文通過實例代碼給大家介紹的非常詳細,需要的朋友可以參考下
    2023-06-06
  • Python參數(shù)傳遞中雙星號(**)和單星號(*)是作用

    Python參數(shù)傳遞中雙星號(**)和單星號(*)是作用

    雙星號(**)和單星號(*)在參數(shù)傳遞中扮演著關鍵角色,本文主要介紹了Python參數(shù)傳遞中雙星號(**)和單星號(*)是作用,具有一定的參考價值,感興趣的可以了解一下
    2024-05-05

最新評論

庐江县| 冀州市| 临桂县| 石台县| 甘谷县| 明光市| 新邵县| 罗源县| 谷城县| 宜春市| 沂水县| 乌兰浩特市| 东平县| 万全县| 襄汾县| 阿拉尔市| 玛沁县| 和硕县| 息烽县| 观塘区| 吉木萨尔县| 沙坪坝区| 宜春市| 海原县| 石泉县| 楚雄市| 南部县| 宜兴市| 永春县| 三原县| 中超| 六安市| 新蔡县| 华阴市| 吉林市| 安顺市| 隆德县| 洛南县| 天峨县| 宁陵县| 集安市|