1. 程式人生 > >OpenCV 入門:用 Node.js 進行圖片處理 (譯文)

OpenCV 入門:用 Node.js 進行圖片處理 (譯文)

在這篇 OpenCV 入門文章中,我將會向大家展示如何使用 Node.js 進行計算機視覺處理。並且結合例項講解使用 OpenCV 這個開源庫進行影象處理的基礎方法。

目前,我正在完成我的碩士論文,其中使用到了 React Native ,神經網路,和 OpenCV 計算機視覺庫。請允許我向你們展示一些我在使用 OpenCV 過程中學習到的一些東西。

計算機視覺是電腦科學中的一個領域,主要專注於使用不同的演算法從影象和視訊中獲取資料。

計算機視覺在許多領域得到了廣泛地應用,例如安全攝像頭的運動跟蹤,控制車輛進行自動駕駛,從圖片或視訊中識別或搜尋物件。

要實現計算機視覺演算法是一件非常繁複的工作,不過幸好有 

OpenCV 這個非常好的開源庫,此庫起源於 1999 年,並一直髮展到現在。

OpenCV 官方支援 C , C ++, Python 和 Java 。幸運的是,由 Peter Braden 領導的一群 Javascript 程式設計師開發了一個 Javascript 的 OpenCV 介面庫,名為 node-opencv

利用該介面庫,我們可以實現用於影象分析的 Node.js 應用。此庫目前還沒有實現所有的 OpenCV 特性 - 特別是 OpenCV 3 的一些特性 - 不過已經基本夠用了。

安裝

要在 Node.js 中使用 OpenCV 庫,你得先進行全域性安裝。在 MacOS 上,你可以通過 

Homebrew 來安裝。在這篇文章中我安裝並使用的是 OpenCV 的 2.4 版本。

譯者注:由於譯者實際使用的是 OpenCV 3.2.0 版本,故在邊緣偵測部分的程式碼相對於原文有所修改。

brew tap homebrew/science
brew install opencv

如果你使用的是其它的作業系統,這裡有 Linux 和 Windows 版本的教程。在成功安裝之後,我們就可以在 Node.js 專案中安裝 node-opencv 了。

npm install --save opencv

有時安裝會失敗(它是開源專案,還沒有到達最終完成的階段),不過你應該可以在該專案的 GitHub 頁面

上找到對應的解決辦法。

OpenCV 基礎

載入及儲存圖片 + 矩陣

OpenCV 的最基本功能是載入和儲存影象。你可以通過下面的方法呼叫這些功能:cv#readImage() 和 **Maritx#save()**;

const cv = require('opencv');

cv.readImage('./img/myImage.jpg', function(err, img) {
  if (err) {
    throw err;
  }
  
  const width = im.width();
  const height = im.height();
  
  if (width < 1 || height < 1) {
    throw new Error('Image has no size');
  }
  
  // do some cool stuff with img
  
  // save img
  img.save('./img/myNewImage.jpg');
});

承載載入圖片資料的物件,是 OpenCV 使用的一個基本資料結構 - 矩陣。所有載入和生成的影象都是用矩陣來描述的,矩陣中的每一個元素都對應影象的一個畫素。矩陣的大小由載入影象的大小決定。在 Node.js 中你可以使用特定引數呼叫 new Matrix() 構造方法來生成一個矩陣。

new cv.Matrix(rows, cols);
new cv.Matrix(rows, cols, type, fillValue);

修改影象

變換影象顏色是一個基礎方法。例如,我們可以呼叫 Matrix#convertGrayscale() 得到一個灰度圖片。

img.convertGrayScale();
img.save('./img/myGrayscaleImg.jpg');

在進行邊緣探測時經常會用到這個方法。

我們可以使用 Matrix#convertHSVscale() 方法將影象轉換為 HSV 圓柱座標表示( HSV cylindrical-coordinate representation )。

img.convertHSVscale();
img.save('./img/myHSVscaleImg.jpg');

我們可以使用 Matrix#crop(x, y, width, height) 方法來裁剪圖片,並指定其中的引數。

這個方法並不會改變當前的影象,而是返回一個全新的影象。

let croppedImg = img.crop(1000, 1000, 1000, 1000);
croppedImg.save('./img/croppedImg');

如果我們想要將影象物件賦值給另一個變數,可以使用 Matrix#copy() 方法返回一個新的圖片物件。

let newImg = img.copy();

這樣,我們可以用一些基礎的 Matrix 方法進行工作了。我們還能找到各種模糊和濾鏡方法來進行圖片編輯。你可以在 GitHub 專案裡的 Matrix.cc 檔案中找到 Matrix 物件實現的所有方法。

腐蝕和膨脹

腐蝕和膨脹是數學形態學( mathematical morphology )的基本方法。我將結合下面的影象修改操作來解釋它們是如何工作的。(譯者注:具體數學定義可參考此文

二進位制影象 A 通過結構元素 B 的膨脹定義如下

OpenCV 有一個 Matrix#dilate(iterations, structEl) 方法,其中的 iterations 引數指定膨脹的量,structEl 引數是用於膨脹的結構元素(預設為 3X3 )。

我們可以用此引數呼叫膨脹方法。

img.dilate(3);

OpenCV 呼叫膨脹方法時如下。

cv::dilate(self->mat, self->mat, sturctEl, cv::Point(-1, -1), 3);

呼叫過此方法後,我們可以得到如下修改過的影象。

二進位制影象 A 通過結構元素 B 的腐蝕定義如下

在 OpenCV 中,我們可以呼叫 Martix#erode(iterations, structEl) 方法,和前面的膨脹方法相似。

我們可以這樣呼叫它:

img.erode(3);

同樣我們可以得到一個腐蝕過的影象。

邊緣偵測

關於邊緣偵測,我們可以使用「坎尼邊緣探測演算法」。該演算法起源於 1986 年,並且一個非常流行,被稱作「最佳探測器」。演算法規定了邊緣偵測中三個重要的標準,列舉如下:

  1. 邊緣偵測要保證低錯誤率;
  2. 良好的邊緣定位 - 探測到的邊緣和實際邊緣畫素差必須最小;
  3. 影象的邊緣只能被標記一次;

在使用「坎尼邊緣探測演算法」前,我們可以先將影象轉為灰度格式,通常這樣做可以讓我們獲得更好的結果。接下來,我們可以通過高斯模糊濾鏡消除影象上的噪點,它需要一個向量作為高斯核大小的引數( which receives a parameter as a field - Gaussian kernel size )。再呼叫這兩個方法處理過後,我們可以在坎尼邊緣偵測時獲得更好更準確的結果。