一、原由
網(wǎng)上搜索了一下,有一些使用selenium實現(xiàn)滑動圖片的代碼,但是多是需要獲取完整圖的?,F(xiàn)在很多滑動圖片驗證碼沒有完整圖,這里記錄一下我實現(xiàn)的。
二、整理思路
1、獲取背景圖(bgImg)、獲取驗證圖(vrImg)
2、對背景圖與驗證圖做二值化處理(PS:這里二值化的閾值需要調(diào)整,不然可能獲取不到想要的效果)
3、比較背景圖與驗證碼圖相似的地方(二值化之后背景圖就會有跟驗證圖一樣的圖形),校驗白邊緣——一個節(jié)點的四周節(jié)點相同,則相似度加一,獲取相似度最大的x軸數(shù)據(jù),及是我們需要知道的拖動距離
4、使用selenium實現(xiàn)拖動
三、代碼實現(xiàn)
1、獲取圖片以及下載圖片(下載使用httpClient實現(xiàn))
使用webDriver.findElement()方法獲取圖片以及圖片地址
2、圖片二值化代碼
/**
* 對圖片進(jìn)行二值化
* @author <a href="mailto:zhouchao@zhexinit.com" >周超</a>
* @param 原圖
* @param 二值化之后存儲的文件
* @param 是否為背景圖(背景圖與驗證圖處理方式不同)
* @throws IOException
*/
public void binaryImage(File imageFile,File descFile,boolean bgImage) throws IOException{
? ? String fileName=imageFile.getName();
? ? String fileType=imageFile.getName().substring(fileName.lastIndexOf(".")+1);
? ? BufferedImage image = ImageIO.read(imageFile);
? ? int width = image.getWidth();
? ? int height = image.getHeight();
? ? BufferedImage binaryImage = new BufferedImage(width, height, BufferedImage.TYPE_BYTE_BINARY);
? ? for(int i= 0 ; i < width ; i++){
? ? ? ? for(int j = 0 ; j < height; j++){
? ? ? ? int rgb=image.getRGB(i, j);
? ? ? ? if(bgImage) {
? ? ? ? ? ? ? rgb=getBgImageRgb(rgb);
? ? ? ? }else {
? ? ? ? ? ? ? rgb = getImageRgb(rgb);
? ? ? ? }
? ? ? ? binaryImage.setRGB(i, j, rgb);
? ? ? ? }
? ? }
? ? ImageIO.write(binaryImage, fileType, descFile);
}
/**
* 如果使用該方法二值化不行,需要調(diào)整參數(shù)r>200&g>200&b>200的參數(shù)
* @author <a href="mailto:zhouchao@zhexinit.com" >周超</a>
* @param i
* @return
*/
? ? private static int getImageRgb(int i) {
? ? ? ? ?String data=Integer.toHexString(i)+"";
? ? ? ? if(data.length()==6) {
? ? ? ? ? ? return 0x00;
? ? ? ? ?}
? ? ? ? int rgb =i& 0xFFFFFF;
? ? ? ? int r=(rgb& 0xff0000) >> 16;
? ? ? ? int g=(rgb& 0xff00) >> 8;
? ? ? ? int b=(rgb& 0xff);
? ? ? ? if(r>200&g>200&b>200) {
? ? ? ? ? ? return 0xFFFFFF;
? ? ? ? }
? ? ? ? return 0x00;
? ? }
? ? /**
? ? * 如果使用該方法二值化不行,需要調(diào)整參數(shù)avg>120&avg<160的參數(shù)
? ? * @author <a href="mailto:zhouchao@zhexinit.com" >周超</a>
? ? * @param i
? ? * @return
? ? */
? ? private static int getBgImageRgb(int i) {
? ? ? ? int rgb =i& 0xFFFFFF;
? ? ? ? int r=(rgb& 0xff0000) >> 16;
? ? ? ? int g=(rgb& 0xff00) >> 8;
? ? ? ? int b=(rgb& 0xff);
? ? ? ? int avg=(r+g+b)/3;
? ? ? ? if(avg>120&avg<160) {
? ? ? ? ? ? return 0xFFFFFF;
? ? ? ? }
? ? ? ? return 0x00;
? ? }
注意:getImageRgb()與getBgImageRgb()可能需要根據(jù)圖片的不同做一些范圍調(diào)整。
二值化的效果如下:
原圖


二值化之后:


這樣二值化之后的圖,我們就可以將問題變?yōu)樵谝粡垐D片中查下另一張圖的位置了
3、比較圖獲取位置
/**
*
* 搜索圖片位子
* @param 二值化之后的背景圖
* @param 二值化之后的校驗圖
* @return
* @throws IOException
*/
public int searchLocation(File bgImgBinaryFile,File verifyBinaryImgFile) throws IOException {
BufferedImage bgImg = ImageIO.read(bgImgBinaryFile);
BufferedImage vrImg = ImageIO.read(verifyBinaryImgFile);
int[][] bgRgb=getImageGRB(bgImg);
int[][] vrRgb=getImageGRB(vrImg);
return searchImage(bgRgb,vrRgb);
}
/**
? ? * 這里非黑即白 值為0或1,1表示白色,0表示黑色
? ? * @param bfImage
? ? * @return
? ? */
? ? public static int[][] getImageGRB(BufferedImage bfImage) {
? ? ? ? int width = bfImage.getWidth();
? ? ? ? int height = bfImage.getHeight();
? ? ? ? int[][] result = new int[height][width];//這里就是y,x才跟圖一樣
? ? ? ? for (int h = 0; h < height; h++) {
? ? ? ? for (int w = 0; w <width ; w++) {
? ? ? ? ? ? ? ? result[h][w] = getZeroOrOne(bfImage.getRGB(w, h));
? ? ? ? ? ? ? ? System.out.print(result[h][w]+" ");
? ? ? ? ? ? }
? ? ? ? System.out.println();
? ? ? ? }
? ? ? ? System.out.println("===================================");
? ? ? ? return result;
? ? }
/**
? ? * 將圖片像素變成0或1,閾值可以根據(jù)實際情況調(diào)整
? ? * @param bfImage
? ? * @return
? ? */
private static int getZeroOrOne(int i) {
? ? int rgb =i& 0xFFFFFF;
? ? int r=(rgb& 0xff0000) >> 16;
? ? int g=(rgb& 0xff00) >> 8;
? ? int b=(rgb& 0xff);
? ? if(r>210&g>210&b>210) {
? ? return 1;
? ? }
? ? return 0x00;
? ? }
/**
? ? * 遍歷圖片,進(jìn)行比較,得出相似度最高的位置
? ? 這里是一個像素一個像素去比較的,具體比較方法為compare(y,x,bgRgb,vrRgb)
? ? * @param bfImage
? ? * @return
? ? */
private static int searchImage(int[][] bgRgb,int[][] vrRgb) {
? ? int bgY=bgRgb.length;
int bgX=bgRgb[0].length;
int vrY=vrRgb.length;
int vrX=vrRgb[0].length;
int xLocation=0;
int yLocation=0;
int maxCount=0;
for(int y=1;y<bgY-vrY-1;y++) {
for(int x=1;x<bgX-vrX-1;x++) {
int count=compare(y,x,bgRgb,vrRgb);
if(count>maxCount) {
maxCount=count;
yLocation=y;
xLocation=x;
}
}
}
LOGGER.info("最佳位置為({},{}),相同數(shù)比例為={}",new Object[] {xLocation,yLocation,maxCount});
? ? return xLocation;
? ? }
/**
? ? * 針對背景圖中的像素位置(Y,X),那驗證圖與原圖做比較
? ? 1、比較邊框,及比較驗證圖中值為1的點
2、爭奪當(dāng)前驗證圖中1的點,比較它四周的節(jié)點,是否相同,相同就相似度加一
PS:其實就是比較框框
? ? * @param bfImage
? ? * @return
? ? */
? ? private static int compare(int bgY,int bgX,int[][] bgRgb,int[][] vrRgb) {
? ? int count=0;
? ? int vrY=vrRgb.length;
int vrX=vrRgb[0].length;
//遍歷小圖節(jié)點
for(int y=0;y<vrY;y++) {
for(int x=0;x<vrX;x++) {
//只對1做比較
if(vrRgb[y][x]!=1){
continue;
}
boolean isRight=true;
//比較當(dāng)前點四周的節(jié)點是否相同
for(int i=-1;i<2;i++) {
for(int j=-1;j<2;j++) {
int tempX=x+i;
int tempY=y+j;
int tempBgY=y+bgY+j;
int tempBgX=x+bgX+i;
if(tempY<0||tempX<0||tempY>vrY-1||tempX>vrX-1) {
continue;
}
if(tempBgY<0||tempBgX<0||tempBgY>bgRgb.length-1||tempBgX>bgRgb[0].length-1) {
continue;
}
if(vrRgb[tempY][tempX]!=bgRgb[tempBgY][tempBgX]) {
isRight=false;
}
}
}
if(isRight) {
count++;
}
}
}
return count;
? ? }
其中g(shù)etImageGRB()方法打印的效果圖類似這樣:(下面是部分截圖)


4、進(jìn)行滑動
/**
? *
? * moveButton:拖動按鈕
? * @param webDriver
? * @param buttonElement? 按鈕
? * @param location? 拖動距離
? * @param stepBean
? * @throws InterruptedException
*/
public void moveButton(WebDriver webDriver,WebElement buttonElement,int location) throws InterruptedException {
? ? int defaultStep=RandomUtils.getRandomNum(20)+20;
? ? int distance=location;
//生成隨機(jī)軌跡模型
? ? List<Integer> stepNumList=new ArrayList<>();
? ? getRandomDistribution(distance,defaultStep,stepNumList);
//點擊數(shù)據(jù)并按住
? ? Actions action = new Actions(webDriver);
? ? action.clickAndHold(buttonElement);
? ? for(Integer num:stepNumList) {
? ? //拖動按鈕
? ? ? ? action.moveByOffset(num,0);
? ? ? ? action.perform();
? ? }
//釋放按鈕
? ? action.release(buttonElement).perform();
}
/**
? *
? * 隨機(jī)軌跡模型
? * @param webDriver
? * @param distance? 拖動距離
? * @param defaultStep? 進(jìn)行拖動的次數(shù)
? * @param dataList 軌跡模型
? * @throws InterruptedException
*/
private static void getRandomDistribution(int distance,int defaultStep,List<Integer> dataList) {
if (defaultStep == 1) {
? ? dataList.add(distance);
return;
}
int item = distance / defaultStep;
item=item+RandomUtils.getRandomIntNum(-item * 2, item * 3);
dataList.add(item);
getRandomDistribution(distance - item, defaultStep - 1,dataList);
}
四、總結(jié)
要實現(xiàn)滑動圖片驗證,關(guān)鍵還是識別需要滑動的位置。通過二值化可以實現(xiàn),這里的二值化,需要根據(jù)滑動的圖片在設(shè)置二值化的閾值。一般滑動圖片都有明顯的特征的,調(diào)整閾值就可以實現(xiàn)。本文僅用于技術(shù)學(xué)習(xí)參考