Java + Selenium + OpenCV解決自動化測試中的滑塊驗證

大宇yu 發表於 2022-07-18
Java 自動化測試 OpenCV

最近工作過程中,一個常用的被測網站突然增加了滑塊驗證環節,導致整個自動化專案失效了。

為了解決這個滑塊驗證問題,在網上查閱了一些資料後,總結並實現瞭解決方案,現記錄如下。

 

1、滑塊驗證思路

Java + Selenium + OpenCV解決自動化測試中的滑塊驗證

 

被測物件的滑塊物件長這個樣子。相對而言是比較簡單的一種形式,需要將左側的拼圖通過下方的滑塊進行拖動,嵌入到右側空槽中,即完成驗證。

要自動化完成這個驗證過程,關鍵點就在於確定滑塊滑動的距離。

 

根據上面的分析,驗證的關鍵點在於確定滑塊滑動的距離。但是看似簡單的一個需求,完成起來卻並不簡單。

如果使用自然邏輯來分析這個過程,可以拆解如下:

1. 定位到左側拼圖所在的位置,由於拼圖的形狀和大小固定,那麼其實只需要定位其左邊邊界離背景圖片的左側距離。(實際在本例中,拼圖的起始位置也是固定的,節省了不少工夫)

2. 定位到右側凹槽所在位置,同樣其形狀和大小是固定的,那麼只需要定位其左邊邊界離背景圖片的左側距離。

3. 用2中探測到的距離減去1中的距離,既是滑塊需要被拖動的距離。

 

要完成上述的探測計算,首先我們想到的是使用元素定位的方法定位到拼圖和凹槽的位置。

然而這一想法是不可行的,原因在於這個驗證模組是使用兩個canvas即畫布元素實現的:

Java + Selenium + OpenCV解決自動化測試中的滑塊驗證

 

 拼圖和凹槽都是“畫”在畫布上的,其本身並不是一個頁面元素,不能使用元素定位的方法。

因此我們考慮使用圖片解析的方法,分析畫布影像本身,來確定相應圖形的位置。

 

2、使用OpenCV進行圖片解析

這裡我們將引入OpenCV庫,來幫我完成圖片解析過程:

OpenCV是一個基於Apache2.0許可(開源)發行的跨平臺計算機視覺和機器學習軟體庫,可以執行在Linux、Windows、Android和Mac OS作業系統上。 
它輕量級而且高效——由一系列 C 函式和少量 C++ 類構成,同時提供了Python、Ruby、MATLAB等語言的介面,實現了影像處理和計算機視覺方面的很多通用演算法。 OpenCV用C++語言編寫,它具有C ++,Python,Java和MATLAB介面,並支援Windows,Linux,Android和Mac OS,OpenCV主要傾向於實時視覺應用,並在可用時利用MMX和SSE指令, 如今也提供對於C#、Ch、Ruby,GO的支援。

2.1 OpenCV引入專案

1:下載 OpenCV

進入到官網 https://opencv.org/releases/ 下載對應系統的 openCV 軟體包後,解壓放置到本地。

使用Maven依賴並不能引入正確的OpenCV外部依賴,這裡需使用外部

2:工程中新增 jar 包

Intellij 中選擇 File -> Project Structure -> Modules -> Dependencies

點選 add -> JARS or directories... 選擇
Java + Selenium + OpenCV解決自動化測試中的滑塊驗證

 

 3. 新建滑塊驗證工具類,引入OpenCV動態連結庫檔案:opencv_java450.dll

public class slideUtil {

    public static String dllPath = "D:\\AutoTest\\src\\main\\resources\\lib\\opencv\\opencv_java450.dll";

    public static void main(String[] args) {

        //getDistance();//除錯用的main方法,呼叫一個getDistance方法,獲取拼圖和凹槽之間的距離,返回double型別數值。
    }

  

2.2 實現圖片解析,計算所需距離

由於本專案的特點,拼圖的形狀和位置是固定的,首先我們將拼圖和凹槽圖片下載到本地,方便後續處理。(其它專案可能出現圖片形狀不固定的情況,可以直接用selenium實時下載圖片,這過程比較簡單,因此不贅述)。
下載完的圖片如下:

凹槽圖片:

Java + Selenium + OpenCV解決自動化測試中的滑塊驗證

 

 拼圖圖片:

Java + Selenium + OpenCV解決自動化測試中的滑塊驗證

 

下面直接上程式碼再做說明:

    public static double getDistance(){

        // 載入OpenCV本地庫
        System.load(dllPath);

        //System.loadLibrary(Core.NATIVE_LIBRARY_NAME);

//對拼圖圖形進行處理,儲存為Mat型別①

        Mat slideBlockMat=Imgcodecs.imread("slide_blk.png");//由於本專案的特點,拼圖的形狀和位置是固定的,因此直接將拼圖圖片儲存到本地進行使用了

//Step1、灰度化圖片②

        Imgproc.cvtColor(slideBlockMat,slideBlockMat,Imgproc.COLOR_BGR2GRAY);

        imwrite("cvt_blk.png",slideBlockMat);

//Step2、去除周圍黑邊


        for (int row = 0; row < slideBlockMat.height(); row++) {

            for (int col = 0; col < slideBlockMat.width(); col++) {

                if (slideBlockMat.get(row, col)[0] == 0) {

                    slideBlockMat.put(row, col, 96);

                }

            }

        }

        imwrite("nsr_blk.png",slideBlockMat);


//Step3、轉黑白圖

        Core.inRange(slideBlockMat, Scalar.all(96), Scalar.all(96), slideBlockMat);

        imwrite("ezh_blk.png",slideBlockMat);
//對滑動背景圖進行處理③ Mat slideBgMat = Imgcodecs.imread("slide_bg.png");//背景凹槽圖片需要動態獲取,見下面的解析 //Step1、灰度化圖片④ Imgproc.cvtColor(slideBgMat,slideBgMat,Imgproc.COLOR_BGR2GRAY); imwrite("hdh_bg.png",slideBgMat); //Step2、二值化 //Core.inRange(slideBgMat, Scalar.all(96), Scalar.all(96), slideBgMat); Imgproc.threshold(slideBgMat,slideBgMat,127,255, Imgproc.THRESH_BINARY); imwrite("ezh_bg.png",slideBgMat); Mat g_result = new Mat(); /* * 將凹槽背景和拼圖圖形進行匹配⑤ */ Imgproc.matchTemplate(slideBgMat,slideBlockMat,g_result, Imgproc.TM_CCOEFF_NORMED); Point matchLocation= Core.minMaxLoc(g_result).maxLoc; //返回匹配點的橫向距離 System.out.println(matchLocation.x); return matchLocation.x; }

 

2.3 演算法解析說明

①什麼是Mat型別:

Mat 是 OpenCV 中用來儲存影像資訊的記憶體物件。Mat 物件中除了儲存影像的畫素資料外,還包括影像的其它屬性:寬、高、型別、維度、大小、深度等。
可以認為在OpenCV中,一個Mat物件就定義了一個影像。
 
②對於slide_blk.png的處理經過了以下過程:
灰度化:Java + Selenium + OpenCV解決自動化測試中的滑塊驗證 去黑邊:Java + Selenium + OpenCV解決自動化測試中的滑塊驗證 二值化:Java + Selenium + OpenCV解決自動化測試中的滑塊驗證

 

 最終的目的在於將圖形轉化為黑白分明的圖形,便於後續匹配。

 

③本專案中,由於背景凹槽圖片,凹槽的位置是動態的,所以需要實時動態獲取:(如果遇到拼圖也需要動態獲取,可以同樣處理)

WebElement bg_canvas = driver.findElement(slide_ver_bg_by);//元素定位,定位到背景圖片

        Object base64 = ((JavascriptExecutor) driver)
                .executeScript("return arguments[0].toDataURL('image/png').substring(21);", bg_canvas);//頁面元素轉Base64
        String base64Str = base64.toString();
        generateImage(base64Str , "slide_bg.png");// 將base64把字串裝換成圖片

  

④對於slide_bg.png的處理經過了以下過程:

灰度化:Java + Selenium + OpenCV解決自動化測試中的滑塊驗證 二值化:Java + Selenium + OpenCV解決自動化測試中的滑塊驗證

 

這裡省略了去黑邊這一過程,因為實踐發現,經過上述兩部後,我們已經能夠進行較為準確的圖片匹配了。

 

⑤matchTemplate:在模板和輸入影像之間尋找匹配,獲得匹配結果影像

 esult:儲存匹配的結果矩陣

TM_CCOEFF_NORMED標準相關匹配演算法

minMaxLoc:在給定的結果矩陣中尋找最大和最小值,並給出它們的位置

 

3、Selenium處理滑塊滑動

Selenium的滑塊處理是庫裡的標準玩法,使用actions類或者javaScript的方式都可以實現,本例採用的是actions類方法:

    public void slide_verify(WebDriver driver) throws InterruptedException {

        double  slideDistance = getDistance();//此處就是呼叫2中的OpenCV計算拼圖和凹槽距離

        System.out.println("滑動距離是" + slideDistance);

        WebElement dragElement = driver.findElement(slide_obj_by);//定位到滑塊

        Actions actions = new Actions(driver);

        actions.clickAndHold(dragElement);//模擬滑鼠動作,按住滑塊
        Thread.sleep(300);

//滑動,分兩次進行① actions.moveByOffset(((int)slideDistance - 11)/2,0); Thread.sleep(1000); actions.moveByOffset(((int)slideDistance - 11)/2,0); Thread.sleep(500); actions.release(); actions.perform(); }

  

①這裡進行滑動時,首先滑動距離之所以要減去11,是因為本例中拼圖的初始位置固定離整體圖形的左邊距是11.

分兩次滑行並且中間sleep了一個時間,是為了防止全勻速拖動而被識別為機器人。

其它文章中有提到使用比較複雜的拖動軌跡演算法,本專案中實踐得知,滑動軌跡並沒有太重要,分兩次拖動就可以了,沒必要複雜化。



4、最終效果

最終的滑動效果,因為被測網站的敏感性就不放上來了,最終實現成果是較為理想的。

希望這篇文章能夠幫助到有需要的人。