OpenCV中的HAL方法調用流程分析

来源:https://www.cnblogs.com/willhua/archive/2020/03/19/12521581.html
-Advertisement-
Play Games

OpenCV中的HAL方法調用流程分析 在OpenCV中有一些所謂HAL(Hardware Acceleration Layer)實現,看名字好像和硬體相關,其實也不盡然,可以理解為比常規的OCV實現更快的版本就好了。此文要做的就是要找到其實現或者切入流程,打通整個函數調用邏輯。本文將以 和`Gau ...


OpenCV中的HAL方法調用流程分析

在OpenCV中有一些所謂HAL(Hardware Acceleration Layer)實現,看名字好像和硬體相關,其實也不盡然,可以理解為比常規的OCV實現更快的版本就好了。此文要做的就是要找到其實現或者切入流程,打通整個函數調用邏輯。本文將以resizeGaussianBlur兩個函數來分析。

resize

首先定位到imgproc模塊的imgproc.hpp文件,找到其中的CV_EXPORTS_W void resize( InputArray src, OutputArray dst, Size dsize, double fx = 0, double fy = 0, int interpolation = INTER_LINEAR );方法。因為我們在外部使用的時候都是引用頭文件來使用,也就是頭文件的函數是我們使用的入口函數。而OCV實現會有許多的分支,一下難以確定,所以我們從入口來找是比較方便的。然後跳轉到該函數的實現,如果IDE不支持,可以在對應的resize.cpp搜索有相同的函數聲明函數就是對應函數的實現,如下:

void cv::resize( InputArray _src, OutputArray _dst, Size dsize,
                 double inv_scale_x, double inv_scale_y, int interpolation )
{
    CV_INSTRUMENT_REGION();

    Size ssize = _src.size();

    CV_Assert( !ssize.empty() );
    if( dsize.empty() )
    {
        CV_Assert(inv_scale_x > 0); CV_Assert(inv_scale_y > 0);
        dsize = Size(saturate_cast<int>(ssize.width*inv_scale_x),
                     saturate_cast<int>(ssize.height*inv_scale_y));
        CV_Assert( !dsize.empty() );
    }
    else
    {
        inv_scale_x = (double)dsize.width/ssize.width;
        inv_scale_y = (double)dsize.height/ssize.height;
        CV_Assert(inv_scale_x > 0); CV_Assert(inv_scale_y > 0);
    }

    if (interpolation == INTER_LINEAR_EXACT && (_src.depth() == CV_32F || _src.depth() == CV_64F))
        interpolation = INTER_LINEAR; // If depth isn't supported fallback to generic resize

    CV_OCL_RUN(_src.dims() <= 2 && _dst.isUMat() && _src.cols() > 10 && _src.rows() > 10,
               ocl_resize(_src, _dst, dsize, inv_scale_x, inv_scale_y, interpolation))

    Mat src = _src.getMat();
    _dst.create(dsize, src.type());
    Mat dst = _dst.getMat();

    if (dsize == ssize)
    {
        // Source and destination are of same size. Use simple copy.
        src.copyTo(dst);
        return;
    }

    hal::resize(src.type(), src.data, src.step, src.cols, src.rows, dst.data, dst.step, dst.cols, dst.rows, inv_scale_x, inv_scale_y, interpolation);
}

我們看到該函數實現做了三件事:

  1. 參數檢查
  2. 檢測是否有OpenCL支持或啟用
  3. 使用hal空間的resize函數來實現

跳轉到hal的實現,同樣位於resize.cpp,部分代碼:

namespace hal {

void resize(int src_type,
            const uchar * src_data, size_t src_step, int src_width, int src_height,
            uchar * dst_data, size_t dst_step, int dst_width, int dst_height,
            double inv_scale_x, double inv_scale_y, int interpolation)
{
    CV_INSTRUMENT_REGION();

    CV_Assert((dst_width > 0 && dst_height > 0) || (inv_scale_x > 0 && inv_scale_y > 0));
    if (inv_scale_x < DBL_EPSILON || inv_scale_y < DBL_EPSILON)
    {
        inv_scale_x = static_cast<double>(dst_width) / src_width;
        inv_scale_y = static_cast<double>(dst_height) / src_height;
    }

    CALL_HAL(resize, cv_hal_resize, src_type, src_data, src_step, src_width, src_height, dst_data, dst_step, dst_width, dst_height, inv_scale_x, inv_scale_y, interpolation);
    //剩下部分代碼是常規實現

然後我們就看到這裡有CALL_HAL這樣一個巨集,跳轉到其實現,位於hal_replacement.hpp,

#define CALL_HAL(name, fun, ...) \
    int res = __CV_EXPAND(fun(__VA_ARGS__)); \
    if (res == CV_HAL_ERROR_OK) \
        return; \
    else if (res != CV_HAL_ERROR_NOT_IMPLEMENTED) \
        CV_Error_(cv::Error::StsInternal, \
            ("HAL implementation " CVAUX_STR(name) " ==> " CVAUX_STR(fun) " returned %d (0x%08x)", res, res));

我們可以看到,它實際上調用了fun函數,如果該函數返回CV_HAL_ERROR_OK,那麼就會return,顯然hal::resize也會返回;否則,會調用CV_Error_,這個並不會讓函數結束或者和程式異常一樣直接終止整個函數,以後再細講。反正其結果就是會讓hal::resize繼續往下執行,下麵就是常規的實現,並不會在此巨集里就return
然後我們在hal_replacement.hpp找到cv_hal_resize的定義為

#define cv_hal_resize hal_ni_resize

然後繼續找到hal_ni_resize的實現為

inline int hal_ni_resize(int src_type, const uchar *src_data, size_t src_step, int src_width, int src_height, uchar *dst_data, size_t dst_step, int dst_width, int dst_height, double inv_scale_x, double inv_scale_y, int interpolation) { return CV_HAL_ERROR_NOT_IMPLEMENTED; }

到這裡,我們發現該函數直接返回CV_HAL_ERROR_NOT_IMPLEMENTED,按照上面的分析,hal::resize繼續往下執行。那麼,hal的實現是怎麼切入進來的呢?
我們發現,hal_replacement.hpp中的CALL_HAL巨集上有一句#include "custom_hal.hpp",好奇怪,include不一般都放在開頭嘛?然後我們看下這個custom_hal.cpp,發現它只有一句#include "carotene/tegra_hal.hpp",我們繼續跟蹤下去。因為前面分析的函數為hal_ni_resize,直接findhal_ni_resize,沒有結果。然後我們findcv_hal_resize,發現有:

#undef cv_hal_resize
#define cv_hal_resize TEGRA_RESIZE

頓時就感覺快打通了,這裡竟然把cv_hal_resize給undef掉了,我們知道在hal_replacement.hpp中是#define cv_hal_resize hal_ni_resize的,並且從文件的位置來看,這個def就會被undef掉,然後重新定義為TEGRA_RESIZE,find它,發現其定義:

#define TEGRA_RESIZE(src_type, src_data, src_step, src_width, src_height, dst_data, dst_step, dst_width, dst_height, inv_scale_x, inv_scale_y, interpolation) \
( \
    interpolation == CV_HAL_INTER_LINEAR ? \
        CV_MAT_DEPTH(src_type) == CV_8U && CAROTENE_NS::isResizeLinearOpenCVSupported(CAROTENE_NS::Size2D(src_width, src_height), CAROTENE_NS::Size2D(dst_width, dst_height), ((src_type >> CV_CN_SHIFT) + 1)) && \
        inv_scale_x > 0 && inv_scale_y > 0 && \
        (dst_width - 0.5)/inv_scale_x - 0.5 < src_width && (dst_height - 0.5)/inv_scale_y - 0.5 < src_height && \
        (dst_width + 0.5)/inv_scale_x + 0.5 >= src_width && (dst_height + 0.5)/inv_scale_y + 0.5 >= src_height && \
        std::abs(dst_width / inv_scale_x - src_width) < 0.1 && std::abs(dst_height / inv_scale_y - src_height) < 0.1 ? \
            CAROTENE_NS::resizeLinearOpenCV(CAROTENE_NS::Size2D(src_width, src_height), CAROTENE_NS::Size2D(dst_width, dst_height), \
                                            src_data, src_step, dst_data, dst_step, 1.0/inv_scale_x, 1.0/inv_scale_y, ((src_type >> CV_CN_SHIFT) + 1)), \
            CV_HAL_ERROR_OK : CV_HAL_ERROR_NOT_IMPLEMENTED : \
    interpolation == CV_HAL_INTER_AREA ? \
        CV_MAT_DEPTH(src_type) == CV_8U && CAROTENE_NS::isResizeAreaSupported(1.0/inv_scale_x, 1.0/inv_scale_y, ((src_type >> CV_CN_SHIFT) + 1)) && \
        std::abs(dst_width / inv_scale_x - src_width) < 0.1 && std::abs(dst_height / inv_scale_y - src_height) < 0.1 ? \
            CAROTENE_NS::resizeAreaOpenCV(CAROTENE_NS::Size2D(src_width, src_height), CAROTENE_NS::Size2D(dst_width, dst_height), \
                                          src_data, src_step, dst_data, dst_step, 1.0/inv_scale_x, 1.0/inv_scale_y, ((src_type >> CV_CN_SHIFT) + 1)), \
            CV_HAL_ERROR_OK : CV_HAL_ERROR_NOT_IMPLEMENTED : \
    /*nearest neighbour interpolation disabled due to rounding accuracy issues*/ \
    /*interpolation == CV_HAL_INTER_NEAREST ? \
        (src_type == CV_8UC1 || src_type == CV_8SC1) && CAROTENE_NS::isResizeNearestNeighborSupported(CAROTENE_NS::Size2D(src_width, src_height), 1) ? \
            CAROTENE_NS::resizeNearestNeighbor(CAROTENE_NS::Size2D(src_width, src_height), CAROTENE_NS::Size2D(dst_width, dst_height), \
                                               src_data, src_step, dst_data, dst_step, 1.0/inv_scale_x, 1.0/inv_scale_y, 1), \
            CV_HAL_ERROR_OK : \
        (src_type == CV_8UC3 || src_type == CV_8SC3) && CAROTENE_NS::isResizeNearestNeighborSupported(CAROTENE_NS::Size2D(src_width, src_height), 3) ? \
            CAROTENE_NS::resizeNearestNeighbor(CAROTENE_NS::Size2D(src_width, src_height), CAROTENE_NS::Size2D(dst_width, dst_height), \
                                               src_data, src_step, dst_data, dst_step, 1.0/inv_scale_x, 1.0/inv_scale_y, 3), \
            CV_HAL_ERROR_OK : \
        (src_type == CV_8UC4 || src_type == CV_8SC4 || src_type == CV_16UC2 || src_type == CV_16SC2 || src_type == CV_32SC1) && \
        CAROTENE_NS::isResizeNearestNeighborSupported(CAROTENE_NS::Size2D(src_width, src_height), 4) ? \
            CAROTENE_NS::resizeNearestNeighbor(CAROTENE_NS::Size2D(src_width, src_height), CAROTENE_NS::Size2D(dst_width, dst_height), \
                                               src_data, src_step, dst_data, dst_step, 1.0/inv_scale_x, 1.0/inv_scale_y, 4), \
            CV_HAL_ERROR_OK : CV_HAL_ERROR_NOT_IMPLEMENTED :*/ \
    CV_HAL_ERROR_NOT_IMPLEMENTED \
)

這個巨集的定義大概做了這些事情:

  1. 如果是雙線性插值,為CV_8U數據類型,且尺寸以及通道滿足一定的要求,那麼就resizeLinearOpenCV去真正實現resize,且返回CV_HAL_ERROR_OK,不滿足這些條件的雙線性插值就不支持,返回CV_HAL_ERROR_NOT_IMPLEMENTED,這樣,就會走hal::resize的普通實現
  2. 如果是AREA插值,情況也是和雙線性插值類似
  3. 其他插值方式則目前不支持。但是從註釋的這些代碼來看,應該是計劃支持的,只是現在還沒做好而已。
    這個巨集的定義用到了不常註意的逗號運算符來實現CV_HAL_ERROR_OK值的返回,逗號運算符返回的是其最右邊的值。
    然後我們就可以跳轉到resizeLinearOpenCV以及resizeAreaOpenCV來追蹤真正的快速實現方法了。

可以發現,切入的關鍵就在於那個undef和define操作了。

GaussianBlur

同樣,我們在smooth.cpp中找到cv_hal_gaussianBlur方法的實現,發現其hal巨集為cv_hal_gaussianBlur,然後到tegra_hal.hpp中findcv_hal_gaussianBlur,發現沒有結果。這說明高斯模糊沒有對應的hal快速版本。然後發現carotene庫中有高斯模糊相關的代碼,看起來應該有實現?我們通過寫demo以及在源碼中打log的方式,發現這些實現函數確實沒有被調用,都是在CALL_HAL巨集那裡就返回CV_HAL_ERROR_NOT_IMPLEMENTED了。應該是這些實現還不夠好,比如我發現已有的一些代碼還沒有考慮到高斯核繫數,所以沒有切入進去,慢慢等待吧。


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 'use strict' //引入node.js路勁模塊 const path = require('path') //引入utils工具模塊,utils主要用來處理css-loader和vue-style-loader的 const utils = require('./utils') //引入c ...
  • zepto中的DOM操作 插入操作 append appendTo 插在最後一個子元素後面 prepend prependTo 插在第一個子元素前面 after insertAfter 插在該元素後面,作為兄弟元素 before insertbefore 插在該元素後面,作為兄弟元素 <!DOCTY ...
  • vue前端項目中開發基於甘特圖的項目計劃模塊 參考鏈接 相比以前jquery的資料,vue的甘特圖插件少很多,中文資料更是少的可憐,以下兩個鏈接是在網上搜到相對還不錯的甘特圖插件 "https://www.cnblogs.com/liang715200/p/12029640.html" "https ...
  • [toc] 運用領域模型 交流與語言的使用 非原創,感謝《領域驅動設計》這本書 領域模型可成為軟體項目通用語言的核心。該模型是一組得自於項目人員頭腦中的概念,以及反映了領域深層含義的術語和關係。這些術語和相互關係提供了模型語言的語義,雖然語言是為領域量身定製的,但就技術開發而言,其依然足夠精確。正是 ...
  • [toc] 運用領域模型 消化知識 非原創,感謝《領域驅動設計》這本書 有效建模的要素 (1) 模型和實現的綁定。最初的原型雖然簡陋,但它在模型與實現之間建立了早期鏈接,而且在所有後續的迭代中我們一直在維護該鏈接。 (2) 建立了一種基於模型的語言。隨著項目的進展,雙方都能夠直接使用模型中的術語,並 ...
  • 設計模式中的原則和法則: 1、開閉原則: 開閉原則(Open Closed Principle,OCP)由勃蘭特·梅耶(Bertrand Meyer)提出,他在 1988 年的著作《面向對象軟體構造》(Object Oriented Software Construction)中提出:軟體實體應當對 ...
  • 1、最簡單的用戶系統 一個最簡單的用戶系統,只需要有用戶和身份驗證兩個模塊就夠了。如圖: 這裡提示一下:上層數據依賴下層數據。舉個慄子,就是身份驗證需要依賴用戶數據。 2、具有許可權管理的用戶系統 如果需要進行許可權管理的話,那麼就加上資源和角色模塊。同時,在身份認證之後需要按需進行鑒權。 資源和用戶都 ...
  • 本次過程僅供學習參考,請遵守相關法律法規。 首先我們分析網站:https://www.mzitu.com/all/ 不難發現,這個頁面上包含了大量的圖片鏈接,可以說是特別方便我們爬取圖片的,這是件好事。那麼我們繼續分析 這是第一頁的地址 這是第二頁的,所以我們爬取的時候只需要在鏈接後面增加“/num ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...