Ape Creation エッセイ | OpenCV プログラミング - コンピューター ビジョンの入り口

コンピュータ ビジョンとは、機械に「見える」ようにする方法を研究する科学です。より具体的には、人間の目の代わりにカメラとコンピュータを使用してターゲットを識別、追跡、測定し、さらに画像処理を行ってコンピュータ処理を画像にすることを指します。人間の観察に適しているか、検出のために機器に送信されます。科学分野として、コンピュータ ビジョンは関連する理論と技術を研究し、画像や多次元データから「情報」を取得できる人工知能システムを構築しようとします。

画像とビデオは、今日のデジタル世界ではどこにでもあり、強力で手頃な価格のコンピューティング デバイスの出現により、複雑な画像アプリケーションの作成がこれまでになく簡単になりました。市場には、画像や動画を操作するためのソフトウェアやライブラリが数多くありますが、OpenCV ライブラリは、独自のソフトウェアを開発しようとしている人にとって必須のツールです。OpenCV(Open Source Computer Vision)は、500 以上の最適化されたアルゴリズムを含むオープン ソースの画像およびビデオ分析ライブラリです。1999 年の開始以来、コンピューター ビジョンの分野の学者や開発者に選ばれるツールと見なされてきました。一緒にコンピュータービジョンの扉に入りましょう!

お問い合わせイメージ

この章では、最も基本的な操作 (画像の読み取り、表示、保存) について説明します。開発を始める前に、ライブラリと便利な IDE をインストールする必要があります. ここでは VS または Qt をお勧めします. コンピューター ビジョンは、特定の環境やオペレーティング システムに縛られていません。

  • opencv_core モジュール: コア関数、特に低レベルのデータ構造とアルゴリズム関数が含まれています。
  • opencv_imgproc モジュール: 画像処理関数が含まれています。
  • opencv_highgui モジュール: 画像や動画の読み取りと書き込みのための関数、およびグラフィカル ユーザー インターフェイスを操作するための関数が含まれています。
  • opencv_features2d モジュール: 関心点検出器、記述子、および関心点マッチング フレームワークが含まれています。

準備した画像の読み取りを開始し、ウィンドウを設定してから、ウィンドウに画像を表示し、ユーザーが入力して終了するのを待ちます。

cv::Mat image = cv::imread("/home/dzh/QtOpenCV/image.png", 1);
cv::namedWindow("Image");
cv::imshow("Image", image);
cv::waitKey(0);

以下は、フォルダー内のすべての画像を読み取り、それらを1つずつ表示するクラスで作成したメソッドです。

ここに画像の説明を挿入

第二に、ピクセルの操作

コンピューター ビジョン アプリケーションを作成するには、イメージの変更や作成など、イメージのコンテンツにアクセスする必要があります。この章では、画像の基本的なピクセル、いわゆるピクセルを操作する方法を説明します。画像を反復処理し、そのピクセルを操作する方法を学習します。Opencv は cv::Mat データ構造を使用して、画像の理由を表します。行列の各要素はピクセルを表します。グレースケール イメージの場合、ピクセルは 8 ビットの符号なし数値で表されます。0 は黒を表し、255 は白を表します。カラー画像には 3 つのカラー チャネルが必要です。

ポインターを使用して、画像を走査し、画像のフィールド操作を走査し、単純な画像操作を実行し、関心領域を定義できます。

// 颜色缩减[指针遍历]
void MainWindow::colorReduce(cv::Mat &image, cv::Mat &result, int div) {
    
    
    // 行数
    int nl = image.rows;
    // 每行的元素个数(像素个数 x 通道数)
    int nc = image.cols * image.channels();
    // 左移位数
    int n = 0;
    // 遍历每一行
    for (int j = 0; j < nl; j++) {
    
    
        // 得到第j行的首地址
        uchar* data = image.ptr<uchar>(j);
        // 求解pow的反函数
        for (int k = 1; k <= 8; k++) {
    
    
            if (pow(2, k) == div) {
    
    
                n = k;
                break;
            } else if (pow(2, k) > div) {
    
    
                n = k - 1;
                break;
            }
        }
        // 掩码左移
        uchar mask = 0xFF << n;
        for (int i = 0; i < nc; i++) {
    
    
//            data[i] = data[i] / div * div + div / 2;
            data[i] = (data[i] & mask) + div / 2;
        }
    }
}

// 图像运算
void MainWindow::imageProc(cv::Mat &image1, cv::Mat &image2, cv::Mat &result) {
    
    
    result = image1 * 0.8 + image2 * 0.2;
}

// 感兴趣区域
void MainWindow::imageROI(cv::Mat &image1, cv::Mat &image2) {
    
    
    // 相当于截取图像的一个区域
    cv::Mat roi = image1(cv::Rect(280, 260, image2.cols, image2.rows));
    cv::addWeighted(roi, 0.2, image2, 0.8, 0.1, roi);

}

3. クラスベースの画像処理

コンピューター ビジョン プログラムの品質は、優れたプログラミング プラクティスと密接に関係しています。バグのないアプリケーションの作成は始まりにすぎません。アプリケーションが新しい要件に簡単に対応できることを願っています。デザイン パターンは、ソフトウェア エンジニアリングではよく知られている概念です。設計パターンは、ソフトウェア設計で頻繁に発生する問題に対する信頼できる再利用可能なソリューションです。1 つ目は、コントローラーを使用してモジュール間通信を実装し、シングルトン設計パターンを使用し、MVC アーキテクチャを使用してアプリケーションを設計する戦略パターンです。

void Histogram1D::process() {
    
    
    cv::Mat image = cv::imread("/home/dzh/QtOpenCV/image.png");
    if (!image.data) {
    
    
        cout << "图像不存在" << endl;
        return;
    } else {
    
    
        cout << "图像的宽度:" << image.cols << "图像的高度:" << image.rows << "像素总数:" << image.cols * image.rows << endl;
    }
    Histogram1D hc;
    cv::Mat imageROI; // 获取感兴趣区域
    imageROI = image(cv::Rect(160, 300, 100, 120));
    int minSat = 65;  // 彩色直方图
    cv::MatND colorhist = hc.getHueHistogram(imageROI, minSat);
    ContentFinder finder;
    finder.setHistogram(colorhist);
    cv::Mat detect = cv::imread("/home/dzh/QtOpenCV/image.png");
    cv::imshow("Detect", detect);
    cv::Mat hsv;
    cv::cvtColor(image, hsv, CV_BGR2HSV);
    vector<cv::Mat> v;
    cv::split(hsv, v);
    cv::Mat result;
    cv::threshold(v[1], v[1], minSat, 255, cv::THRESH_BINARY);
    result = finder.find(hsv, 0.0f, 180.0f, channels, 1);
    cv::bitwise_and(result, v[1], result);
    cv::Rect rect(110, 260, 35, 40);
    cv::rectangle(image, rect, cv::Scalar(255, 255, 255));
    cv::TermCriteria criteria(cv::TermCriteria::MAX_ITER, 10, 0.01);
    cv::meanShift(result, rect, criteria);
//    cv::imshow("origin", colorhist);
    cv::imshow("output", result);
    cv::waitKey(0);
}

色空間変換レンダリング:

ここに画像の説明を挿入

4番目に、ヒストグラムを使用してピクセルをカウントします

画像は、異なる色の値を持つピクセルで構成されています。画像内のピクセル値の分布は、この画像の重要な特徴です。ヒストグラムを計算して、イメージの外観を変更するために使用できます。グレースケール イメージの場合、ヒストグラムは長さ 256 の配列に相当し、各配列要素はグレースケール値が現在の添字であるピクセルの数を表します。

cv::Mat Histogram3D::getHistogramImage(cv::Mat &image) {
    
    
    // 先求取直方图(256x256x256)
    cv::MatND hist = getHistogram(image);
    // 设置最大值和最小值
    double maxVal = INT_MIN;
    double minVal = INT_MAX;
    // 创建显示图像256x256x3
    cv::Mat histImg(histSize[0], histSize[0], CV_8UC3, cv::Scalar(255, 255, 255));
    int hpt = static_cast<int>(0.9 * histSize[0]);
    // 3D->2D,可以减少一个维度
    vector<vector<float>> vec(3, vector<float>(256));
    // b通道
    for (int i = 0; i < 256; i++) {
    
    
        // g通道
        for (int j = 0; j < 256; j++) {
    
    
            // r通道
            for (int k = 0; k < 256; k++) {
    
    
                vec[0][i] += hist.at<float>(i, j, k);
                vec[1][j] += hist.at<float>(i, j, k);
                vec[2][k] += hist.at<float>(i, j, k);
            }
        }
    }
    // 先遍历每一行
    for (int i = 0; i < vec.size(); i++) {
    
    
        // 对于每一行直方图取最大值和最小值
        cv::minMaxLoc(vec[i], &minVal, &maxVal, 0, 0);
        cout << "maxVal:" << maxVal <<",minVal:" << minVal << endl;
        // 再遍历每一列
        for (int j = 0; j < vec[i].size(); j++) {
    
    
            float val = vec[i][j];
            int intensity = static_cast<int>(val * hpt / maxVal);
            cv::Scalar color;
            color[i] = 255;
            cv::line(histImg, cv::Point(j, histSize[i]), cv::Point(j, histSize[i] - intensity), color);
        }
    }
    return histImg;
}

最も一般的に使用されている 640x480 の画像でも 30 万ピクセルですが、通常のビデオを見る場合は 1080P と 4K を考えてください。

ここに画像の説明を挿入

5. モルフォロジー演算に基づく画像変換

形態学的フィルタリング理論は 1990 年代に提案され、離散グラフィックスの分析と処理に使用されます。定義済みの形状要素を適用して画像を変換する一連の操作を定義します。形状要素がピクセルの隣接要素と交差する方法によって、操作の結果が決まります。侵食と膨張は、最も基本的なモルフォロジー操作です。通常、モルフォロジー フィルタリングはバイナリ イメージで使用されるため、白いピクセルは前景オブジェクトを表すために使用され、黒いピクセルは背景を表すために使用されます。オープニングとクロージングの操作は最も基本的なものです. ここでは、モルフォロジー フィルタリングを使用して画像のエッジとコーナーを検出する方法を示します。

cv::Mat MorphoFeatures::getEdges(cv::Mat &image) {
    
    
    // 得到梯度图
    cv::Mat result;
    cv::morphologyEx(image, result, cv::MORPH_GRADIENT, cv::Mat());
    // 阈值化得到二值图像
    applyThreshold(result);
    return result;
}

void MorphoFeatures::setThreshold(int value) {
    
    
    threshold = value;
}

cv::Mat MorphoFeatures::getCorners(cv::Mat &image) {
    
    
    cv::Mat result;
    cv::dilate(image, result, cross);
    cv::erode(result, result, diamond);

    cv::Mat result2;
    cv::dilate(image, result2, x);
    cv::erode(result2, result, square);

    cv::absdiff(result2, result, result);
    applyThreshold(result);
    return result;
}

直線は組み込み関数を使用して簡単に検出でき、角は四角、ひし形、十字、X の 4 つの構造要素を定義する必要があります。
ここに画像の説明を挿入

ウォーターシェッド アルゴリズムを使用して画像をセグメント化し、GrabCut アルゴリズムを使用して前景オブジェクトを抽出することもできます。

6. 画像フィルタリング

フィルタリングは、信号処理と画像処理の基本的な操作であり、特定のアプリケーションで重要な情報を伝えると考えられる画像の部分を選択的に抽出することを目的としています。フィルタリングにより、画像からノイズが除去され、関心のある視覚的特徴が抽出され、画像のリサンプリングが可能になります。マトリックスの中央にある要素は、フィルタによって現在処理されているピクセルに対応します。このような行列は、カーネルまたはマスクと呼ばれます。最も一般的に使用されるのは、平均フィルター、メディアン フィルター、ソーベル フィルター、画像のラプラス変換です。

cv::Mat contoursInv;
cv::threshold(contours, contoursInv, 128, 255, cv::THRESH_BINARY_INV);
cv::imshow("Inv", contoursInv);
cv::Mat result1, result2;
cv::Sobel(image, result1, CV_8U, 1, 0, 3, 0.4, 128);
cv::Sobel(image, result2, CV_8U, 0, 1, 3, 0.4, 128);
cv::imshow("result1", result1);
cv::imshow("result2", result2);
cv::Mat sobelX, sobelY;
cv::Sobel(image, sobelX, CV_8U, 1, 0);
cv::Sobel(image, sobelY, CV_8U, 0, 1);
cv::Mat sobel;
sobel = sobelX + sobelY;
cv::imshow("Sobel", sobel);

以下は、左上から元の画像で、平均フィルタリング、メディアン フィルタリング、ソーベル フィルタリングの結果が続きます. 平均フィルタリングは一般的に画像をぼかします. メディアン フィルタリングはノイズ点を除去するのに非常に効果的です. ソーベル演算子は広く使われています.エッジまたは輪郭検出に使用されます。

ここに画像の説明を挿入

7. 特徴点の検出と照合

コンピューター ビジョンにおける関心点 (キーポイントまたは特徴点とも呼ばれる) の概念は、オブジェクト認識、画像マッチング、ビジュアル トラッキング、3D 再構成などの問題を解決するために広く使用されています。cv::cornerHarris はハリスのコーナーを検出します. もちろん, 速いコーナー検出はより高速です. スケール不変性の概念は、検出された各特徴点に対応するサイズ係数が伴うことを意味します。その中には、非常に人気のある特徴点である SURF 特徴点があり、SIFT アルゴリズムの効率的な変形でもあります。

特徴マッチングでは、特徴記述子は通常 N 次元のベクトルであり、照明や少しの透視変換に最適です。特徴点の検出は、次のように要約できます。キー ポイントの抽出と記述子の計算。

// 计算Harris角点
void HarrisDetector::detect(cv::Mat &image) {
    
    
    cv::cornerHarris(image, cornerStrength, neighbourhood, aperture, k);
    double minStrength;
    cv::minMaxLoc(cornerStrength, &minStrength, &maxStregth);
    cv::Mat dilated;
    cv::dilate(cornerStrength, dilated, cv::Mat());
    cv::compare(cornerStrength, dilated, localMax, cv::CMP_EQ);
}

// 由Harris值获取角点图
cv::Mat HarrisDetector::getCornerMap(double qualityLevel) {
    
    
    cv::Mat cornerMap;
    threshold = qualityLevel * maxStregth;
    cv::threshold(cornerStrength, cornerTh, threshold, 255, cv::THRESH_BINARY);
    cornerTh.convertTo(cornerMap, CV_8U);
    cv::bitwise_and(cornerMap, localMax, cornerMap);
    return cornerMap;
}

もちろん、上記の特徴点検出ライブラリ関数はOpenCVにはなく、特許の関係ですべて拡張ライブラリに配置されています。インストール方法については、私のチュートリアルを参照してください: Ubuntu は opencv_contrib 拡張ライブラリをインストールします。

ここに画像の説明を挿入

もちろん、コンピューター ビジョンは非常に大きなテーマです. この記事はスペースが限られているため、1 つまたは 2 つの説明しかできません. 興味のある学生は関連する本を読むことができます. 現在、CV の主なタスクは、ターゲットの認識、ターゲットの検出、およびターゲットの追跡です. ネットワーク構造も従来の CNN から Transformer に変更されました. 人工知能の急速な発展は CV と切り離すことができず、オープンソースの貢献と切り離すこともできません!

おすすめ

転載: blog.csdn.net/qq_42257666/article/details/126650323