亚洲国产精品乱码一区二区,美景房屋2免费观看,哎呀哎呀在线观看视频高清国语,从镜子里看我是怎么C哭你

Article / 文章中心

百度拖動(dòng)旋轉(zhuǎn)驗(yàn)證碼被輕松破解?當(dāng)代流行的人機(jī)驗(yàn)證到底安不安全?

發(fā)布時(shí)間:2020-12-18 點(diǎn)擊數(shù):7297

 

 前言

 

> 百度的驗(yàn)證碼又雙叒更新了。

> 當(dāng)然出于好奇,貓又拿起了鍵盤開始挑戰(zhàn)。

 

 正文來(lái)了。

 

先來(lái)看看繼上次破解百度旋轉(zhuǎn)驗(yàn)證碼后,百度的大佬又做出了哪些改變。

 

> 1.抓取圖片時(shí)加上了馬賽克

> 2.增加了圖片庫(kù)

 

 抓取圖片時(shí)加上了馬賽克

 

 

 -  截圖是這個(gè)亞子的

 

 

 - 后臺(tái)拿到的卻是這個(gè)亞子的

 

哦呦,這個(gè)馬賽克有點(diǎn)東西的呀~

 

圖片抓下來(lái)都不一樣還咋識(shí)別,百度這里也是煞費(fèi)苦心,給您點(diǎn)個(gè)贊。

 

不過(guò)話說(shuō)回來(lái),就算這樣也難不住我們的呀,這里我思考了一下還有幾種方式來(lái)獲取這個(gè)圖片:

 

 -  1 .通過(guò)系統(tǒng)級(jí)鼠標(biāo)來(lái)獲取

 -  2 .通過(guò)網(wǎng)頁(yè)截圖來(lái)獲取

 

1.通過(guò)系統(tǒng)級(jí)鼠標(biāo)來(lái)獲取

 

首先,試了下第一種方式

 

定位到圖片路徑位置拿到圖片途徑,然后再通過(guò)模擬器打來(lái)另一個(gè)頁(yè)面

 

然后通過(guò)下面這段代碼實(shí)現(xiàn)保存圖片的操作(這里用到了`Robot`系統(tǒng)級(jí)鼠標(biāo)控制類 )

 

 

```java

public byte[] sivePic(String url, WebDriver driver, String window_one) {

((JavascriptExecutor) driver).executeScript("window.open('" + url + "')"); // 用js打開新的窗口

sleep(2000);

Set<String> allWindow = driver.getWindowHandles(); // 獲取所有的窗口句柄

sleep(1 * 500);

for (String i : allWindow) {

if (i != window_one) {

driver.switchTo().window(i);

}

}

WebElement img = driver.findElement(By.tagName("img"));

Actions actions = new Actions(driver);

Robot robot;

byte[] picBytes = null;

File imgFile = null;

// 聲明一個(gè)StingSelection 對(duì)象,并使用String的參數(shù)完成實(shí)例化;

String imgName = "baidu_" + System.currentTimeMillis()+".jpg";

// 使用Toolkit對(duì)象的setContents將字符串放到粘貼板中 ;

Toolkit.getDefaultToolkit().getSystemClipboard().setContents( new StringSelection(imgName), null);

try {

robot = new Robot();

robot.setAutoDelay(100);

actions.moveToElement(img).contextClick().perform();

sleep(100);

robot.keyPress(KeyEvent.VK_DOWN);

sleep(100);

robot.keyRelease(KeyEvent.VK_DOWN);

sleep(100);

robot.keyPress(KeyEvent.VK_DOWN);

sleep(100);

robot.keyRelease(KeyEvent.VK_DOWN);

sleep(100);

// 確認(rèn)

robot.keyPress(KeyEvent.VK_ENTER);

robot.keyRelease(KeyEvent.VK_ENTER);

sleep(1000);

// 刪除

robot.keyPress(KeyEvent.VK_DELETE);

robot.keyRelease(KeyEvent.VK_DELETE);

sleep(500);

// 按下crtl v鍵 ;

robot.keyPress(KeyEvent.VK_CONTROL);

robot.keyPress(KeyEvent.VK_V);

sleep(500);

// 釋放crtl v 鍵

robot.keyRelease(KeyEvent.VK_V);

robot.keyRelease(KeyEvent.VK_CONTROL);

sleep(500);

// 文件名字后確認(rèn)

robot.keyPress(KeyEvent.VK_ENTER);

robot.keyRelease(KeyEvent.VK_ENTER);

sleep(5000);

String name = System.getenv().get("USERNAME");

imgFile = new File("C:/Users/" + name + "/Downloads/"+imgName);

picBytes = FileUtils.readFileToByteArray(imgFile);

System.out.println("save ok");

} catch (Exception e) {

e.printStackTrace();

} finally {

imgFile.delete();

}

return picBytes;

}

```

 

啊哈,拿到了。

本以為就這樣結(jié)束了。

 

萬(wàn)萬(wàn)沒想到,抓了幾張之后馬賽克又出現(xiàn)了。。。。。。。

到此,第一種方法宣告西敗。

 

2.通過(guò)網(wǎng)頁(yè)截圖來(lái)獲取

 

這個(gè)方法就比較靠譜了,百度總不能讓用戶看到馬賽克的圖片吧,哈哈(手動(dòng)狗頭)

 

 

```java

// 獲取ID的隨機(jī)數(shù)

WebElement vcodesElemet = driver.findElement(By.className("mod-vcodes"));

String num = vcodesElemet.getAttribute("id");

num = num.split("mod-vcodes")[num.split("mod-vcodes").length - 1];

WebElement imgElemet = driver.findElement(By.id("vcode-spin-img" + num));

File img = getImgFile(driver, imgElemet.getLocation().getX() - 8,imgElemet.getLocation().getY());

```

 

```java

/**

* 截圖(驗(yàn)證碼) 這里的 152 是頁(yè)面顯示圖片的實(shí)際寬高

 */

private File getImgFile(WebDriver driver, int i, int j) {

BufferedImage imgbuf = null;

File srcFile, imgFile = null;

try {

srcFile = ((TakesScreenshot) driver).getScreenshotAs(OutputType.FILE);

imgbuf = ImageIO.read(srcFile).getSubimage(i, j, 152, 152);

imgFile = new File("C:\\daidu_" + System.currentTimeMillis() + ".png");

ImageIO.write(imgbuf, "png", imgFile);

} catch (IOException e) {

e.printStackTrace();

}

return imgFile;

}

```

那么到這里我們就拿到了驗(yàn)證圖,截來(lái)的圖片肯定沒有原圖清晰度高,所以識(shí)別率就會(huì)稍微降低一些。不過(guò)影響不是太大。

到這里抓取圖片的問題就解決了。

 

 增加了圖片庫(kù)

說(shuō)道增加圖片庫(kù)這里倒不是什么大問題,只要抓到一張圖片,就可以通過(guò)旋轉(zhuǎn)生成對(duì)應(yīng)的圖片,只需要找到不同的圖片就可以了。

 

之前百度大概有50-60張不同的圖片,也就是說(shuō)不同的角度(360°)全部加入圖片庫(kù)的話最多21600張圖(估計(jì)值)。

 

現(xiàn)在通過(guò)抓取了幾百?gòu)垐D片觀察,不同的圖片大概有120多張,之前的50-60張也包括在內(nèi),也就是說(shuō)百度又新加了一倍的圖庫(kù)。大概在43000張圖左右。

 

這里不存在什么大問題,只是我們的模型庫(kù)需要更新一下而已。

 

好了上面的問題既然都解決了,那么來(lái)說(shuō)下具體的破解思路及步驟。

 

 1. 抓取到大量圖片并篩選

 2. 根據(jù)篩選的圖片生成模型庫(kù)

 3. 將模型庫(kù)接入到自動(dòng)化模擬程序

 

說(shuō)起來(lái)也不是很復(fù)雜嘛。開搞~

 

一、抓取到大量圖片并篩選

上面有提到抓取圖片我們采用截圖的方式,自動(dòng)化程序這里就先不放了,文章后面有完整代碼。我們先看下結(jié)果。

 

可以看到有很多相似的圖片,我們抓到300-500張左右基本就可以找到全部的不同的圖片,然后把相似的只留下一張就好了。

 

 二、根據(jù)篩選的圖片生成模型庫(kù)

 

 1. 這里我們拿到從步驟一篩選出來(lái)的圖片,將每一張片旋轉(zhuǎn)生成360度各個(gè)角度的圖片,并通過(guò)比例計(jì)算出原圖到對(duì)應(yīng)角度應(yīng)該滑動(dòng)的距離。

 

 2. 然后在每個(gè)模型庫(kù)中找到正的那張圖,將之前標(biāo)記好的距離值標(biāo)記到原圖上。

 

 

 

 3. 再通過(guò)計(jì)算得出模型庫(kù)中每個(gè)圖應(yīng)該滑動(dòng)的距離,并標(biāo)記。

 

為了提升效率,我們將模型庫(kù)以map對(duì)象的形式存入.obj文件中。

啟動(dòng)程序時(shí)只需要讀一個(gè)文件,然后將其存入map中,大大提升識(shí)別效率。

 

模型庫(kù):[baidu_mod.obj](https://download.csdn.net/download/weixin_49701447/13714487)

 

 三、將模型庫(kù)接入到自動(dòng)化模擬程序

 

 1. 自動(dòng)化模擬程序

 

```java

public class Baidu {

private final static Logger logger = LoggerFactory.getLogger(BaiduTrain.class);

private final String INDEX_URL = "http://passport.baidu.com/?getpassindex&tt=1597054938536&gid=00C700C-A457-4CCF-8588-F118FFF70829&tpl=mn&u=https%3A%2F%2Fwww.baidu.com%2Fs%3Fie%3DUTF-8";

private static Map<String, Map<String, PicFinger>> Map = new HashMap<String, Map<String, PicFinger>>();

/**

* 截圖(驗(yàn)證碼)

*/

private File getImgFile(WebDriver driver, int i, int j) {

BufferedImage imgbuf = null;

File srcFile, imgFile = null;

try {

srcFile = ((TakesScreenshot) driver).getScreenshotAs(OutputType.FILE);

imgbuf = ImageIO.read(srcFile).getSubimage(i, j, 152, 152);

imgFile = new File("C:\\daidu_" + System.currentTimeMillis() + ".png");

ImageIO.write(imgbuf, "png", imgFile);

} catch (IOException e) {

e.printStackTrace();

}

return imgFile;

}

//將圖片變?yōu)榭杀容^的對(duì)象

public static PicFinger getPicFinger(File file) {

try {

BufferedImage libBuf = ImageIO.read(file);

PicFinger picFinger = new PicFinger(libBuf);

byte[] img = picFinger.getBinaryzationMatrix();

PicFinger fp = new PicFinger(img);

return fp;

} catch (IOException e) {

e.printStackTrace();

return null;

}

}

//匹配模型庫(kù)獲取滑動(dòng)距離

public String getDistance(File file) {

double succRate = 80;

float ret, maxRet = -1;

PicFinger picFinger = getPicFinger(file);

PicFinger fpSrc = null;

String distance = "";

for (String mapkey : Map.keySet()) {

for (String key : Map.get(mapkey).keySet()) {

fpSrc = Map.get(mapkey).get(key);

ret = picFinger.compare(fpSrc) * 100;

if (ret >= succRate) {

if (ret > maxRet) {

maxRet = ret;

distance = key.substring(key.indexOf('-') + 1, key.length());

}

}

}

}

System.out.println("相似度最高為:" + maxRet + "|distance=" + distance);

return distance;

}

//自動(dòng)化程序

public void seleniumTest() {

String phone = "13888888888";

ChromeDriverManager manager = ChromeDriverManager.getInstance();

String gtText = null, num;

Integer distance = 30;

int status = -1;

By moveBy, gtTextBy, submit, phoneBy, securemobil, close, tipInfo;

WebElement moveElemet, submitElemet, gtTextElement, phoneElemet, securemobilElemet, closeElemet, tipInfoElemet, vcodesElemet;

WebDriver driver = null;

try {

driver = manager.getDriver();

driver.get(INDEX_URL);

sleep(1 * 500);

driver.navigate().refresh();

sleep(1 * 500);

// 輸入手機(jī)號(hào)

phoneBy = By.id("account");

phoneElemet = waitWebElement(driver, phoneBy, 400);

if (phoneElemet == null)

return null;

phoneElemet.clear();

for (int i = 0; i < phone.length(); i++) {

char c = phone.charAt(i);

phoneElemet.sendKeys(c + "");

phoneElemet.click();

}

// 下一步

submit = By.id("submit");

submitElemet = waitWebElement(driver, submit, 400);

if (submitElemet == null)

return null;

submitElemet.click();

// 點(diǎn)擊關(guān)閉

close = By.xpath("http://div[@class='vcode-close']");

closeElemet = waitWebElement(driver, close, 200);

if (closeElemet == null)

return null;

if (closeElemet != null)

closeElemet.click();

// 點(diǎn)擊我選中的是手機(jī)號(hào)

securemobil = By.xpath("http://p[@s='securemobil']");

securemobilElemet = waitWebElement(driver, securemobil, 200);

if (securemobilElemet == null)

return null;

if (securemobilElemet != null && securemobilElemet.isDisplayed())

securemobilElemet.click();

// 下一步

submit = By.id("submit");

submitElemet = waitWebElement(driver, submit, 200);

if (submitElemet == null)

return null;

submitElemet.click();

// 獲取ID的隨機(jī)數(shù)

vcodesElemet = waitWebElement(driver, By.className("mod-vcodes"), 200);

if (vcodesElemet == null)

return null;

num = vcodesElemet.getAttribute("id");

num = num.split("mod-vcodes")[num.split("mod-vcodes").length - 1];

 

tipInfo = By.id("pass-slide-tipInfo2" + num);

tipInfoElemet = waitWebElement(driver, tipInfo, 200);

if (tipInfoElemet == null)

return null;

if (tipInfoElemet.getText().contains("最右")) {

// 點(diǎn)擊關(guān)閉

close = By.className("vcode-close");

closeElemet = waitWebElement(driver, close, 200);

if (closeElemet == null)

return null;

closeElemet.click();

// 下一步

submit = By.id("submit");

submitElemet = waitWebElement(driver, submit, 200);

if (submitElemet == null)

return null;

submitElemet.click();

// 獲取ID的隨機(jī)數(shù)

vcodesElemet = waitWebElement(driver, By.className("mod-vcodes"), 200);

if (vcodesElemet == null)

return null;

num = vcodesElemet.getAttribute("id");

num = num.split("mod-vcodes")[num.split("mod-vcodes").length - 1];

}

for (int i = 0; i < 100; i++) {

// 獲取滑動(dòng)按鈕

moveBy = By.id("vcode-spin-button" + num);

moveElemet = waitWebElement(driver, moveBy, 400);

if (moveElemet != null) {

moveElemet.click();

} else {

System.out.println("error get moveElemet=" + moveElemet);

break;

}

sleep(100);

WebElement imgElemet = driver.findElement(By.id("vcode-spin-img" + num));

File img = getImgFile(driver, imgElemet.getLocation().getX() - 8, imgElemet.getLocation().getY());

distance = Integer.parseInt(getDistance(img));

// 滑動(dòng)

move(driver, moveElemet, distance);

sleep(100);

// 獲取滑動(dòng)結(jié)果 10s

boolean isWaitLoad = false;

for (int k = 0; k < 100; k++) {

gtTextBy = By.id("vcode-spin-icon" + num);

gtTextElement = waitWebElement(driver, gtTextBy, 400);

if (gtTextElement == null)

return null;

gtText = gtTextElement.getAttribute("class");

if (gtText.contains("loading") || gtText.contains("hide")) {

if (!isWaitLoad) {

System.out.print("loading(" + gtTextBy.toString() + ")");

isWaitLoad = true;

}

System.out.print(".");

sleep(100);

continue;

} else

break;

}

if (gtText.contains("success")) {

sleep(100);

break;

}

sleep(2000);

}

} catch (Exception e) {

e.printStackTrace();

} finally {

sleep(1000);

manager.closeDriver(status);

}

}

//線程睡眠

private static void sleep(long millis) {

try {

Thread.sleep(millis);

} catch (InterruptedException e) {

// TODO Auto-generated catch block

e.printStackTrace();

}

}

/**

* 模擬人工移動(dòng)

* @param driver

* @param element頁(yè)面滑塊

* @param distance需要移動(dòng)距離

*/

private static void move(WebDriver driver, WebElement element, int distance) throws InterruptedException {

int randomTime = 0;

if (distance > 90)

randomTime = 250;

else if (distance > 80 && distance <= 90)

randomTime = 150;

List<Integer> track = GeetCanvasApi.getMoveTrack(distance);

int moveY = 1;

try {

Actions actions = new Actions(driver);

actions.clickAndHold(element).perform();

Thread.sleep(200);

for (int i = 0; i < track.size(); i++) {

actions.moveByOffset(track.get(i), moveY).perform();

Thread.sleep(new Random().nextInt(300) + randomTime);

}

Thread.sleep(200);

actions.release(element).perform();

} catch (Exception e) {

logger.error("move:err = " + e.toString());

}

}

 

// 延時(shí)加載

private static WebElement waitWebElement(WebDriver driver, By by, int count) {

WebElement webElement = null;

boolean isWait = false;

for (int k = 0; k < count; k++) {

try {

webElement = driver.findElement(by);

if (isWait)

System.out.println(" ok!");

return webElement;

} catch (org.openqa.selenium.NoSuchElementException | org.openqa.selenium.ElementNotVisibleException ex) {

isWait = true;

if (k == 0)

System.out.print("waitWebElement(" + by.toString() + ")");

else

System.out.print(".");

sleep(50);

}

}

if (isWait) {

System.out.println(" outTime!");

logger.error("WebElement = null");

}

return null;

}

//讀文件,用于讀取模型庫(kù)

public static Object load(String file) throws Exception {

FileInputStream freader = null;

ObjectInputStream objectInputStream = null;

try {

freader = new FileInputStream(file);

objectInputStream = new ObjectInputStream(freader);

Object o = objectInputStream.readObject();

return o;

} catch (Exception e) {

return null;

} finally {

if (freader != null)

freader.close();

if (objectInputStream != null)

objectInputStream.close();

}

}

 

@SuppressWarnings("unchecked")

public static void main(String[] args) throws Exception {

Map = (Map<String, Map<String, PicFinger>>) load("C:\\baidu_mod.obj");

OCRUtil.chromePath = "C://chrome";

Baidu baidu = new Baidu();

baidu.seleniumTest();

 

}

 

}

 

```

 

 2. 將圖片信息轉(zhuǎn)變?yōu)榭杀容^信息

 

```java

public final class PicFinger implements Serializable {

private static final long serialVersionUID = 431106089062884937L;

/**

* 圖像指紋的尺寸,將圖像resize到指定的尺寸,來(lái)計(jì)算哈希數(shù)組

*/

private static final int HASH_SIZE = 16;

/**

* 保存圖像指紋的二值化矩陣

*/

private final byte[] binaryzationMatrix;

 

public PicFinger(byte[] hashValue) {

if (hashValue.length != HASH_SIZE * HASH_SIZE) {

throw new IllegalArgumentException(String.format("length of hashValue must be %d", HASH_SIZE * HASH_SIZE));

}

this.binaryzationMatrix = hashValue;

}

 

public PicFinger(String hashValue) {

this(toBytes(hashValue));

}

 

public PicFinger(BufferedImage src) {

this(hashValue(src));

}

 

public byte[] getBinaryzationMatrix() {

return binaryzationMatrix;

}

 

private static byte[] hashValue(BufferedImage src) {

BufferedImage hashImage = resize(src, HASH_SIZE, HASH_SIZE);

byte[] matrixGray = (byte[]) toGray(hashImage).getData().getDataElements(0, 0, HASH_SIZE, HASH_SIZE, null);

return binaryzation(matrixGray);

}

 

/**

* 從壓縮格式指紋創(chuàng)建{@link PicFinger}對(duì)象

*

* @param compactValue

* @return

*/

public static PicFinger createFromCompact(byte[] compactValue) {

return new PicFinger(uncompact(compactValue));

}

 

public static boolean validHashValue(byte[] hashValue) {

if (hashValue.length != HASH_SIZE) {

return false;

}

for (byte b : hashValue) {

{

if (0 != b && 1 != b) {

return false;

}

}

}

return true;

}

 

public static boolean validHashValue(String hashValue) {

if (hashValue.length() != HASH_SIZE) {

return false;

}

for (int i = 0; i < hashValue.length(); ++i) {

if ('0' != hashValue.charAt(i) && '1' != hashValue.charAt(i)) {

return false;

}

}

return true;

}

 

public byte[] compact() {

return compact(binaryzationMatrix);

}

 

/**

* 指紋數(shù)據(jù)按位壓縮

*

* @param hashValue

* @return

*/

private static byte[] compact(byte[] hashValue) {

byte[] result = new byte[(hashValue.length + 7) >> 3];

byte b = 0;

for (int i = 0; i < hashValue.length; ++i) {

if (0 == (i & 7)) {

b = 0;

}

if (1 == hashValue[i]) {

b |= 1 << (i & 7);

} else if (hashValue[i] != 0) {

throw new IllegalArgumentException("invalid hashValue,every element must be 0 or 1");

}

if (7 == (i & 7) || i == hashValue.length - 1) {

result[i >> 3] = b;

}

}

return result;

}

 

/**

* 壓縮格式的指紋解壓縮

*

* @param compactValue

* @return

*/

private static byte[] uncompact(byte[] compactValue) {

byte[] result = new byte[compactValue.length << 3];

for (int i = 0; i < result.length; ++i) {

if ((compactValue[i >> 3] & (1 << (i & 7))) == 0) {

result[i] = 0;

} else {

result[i] = 1;

}

}

return result;

}

 

/**

* 字符串類型的指紋數(shù)據(jù)轉(zhuǎn)為字節(jié)數(shù)組

*

* @param hashValue

* @return

*/

private static byte[] toBytes(String hashValue) {

hashValue = hashValue.replaceAll("\\s", "");

byte[] result = new byte[hashValue.length()];

for (int i = 0; i < result.length; ++i) {

char c = hashValue.charAt(i);

if ('0' == c) {

result[i] = 0;

} else if ('1' == c) {

result[i] = 1;

} else {

throw new IllegalArgumentException("invalid hashValue String");

}

}

return result;

}

 

/**

* 縮放圖像到指定尺寸

*

* @param src

* @param width

* @param height

* @return

*/

private static BufferedImage resize(Image src, int width, int height) {

BufferedImage result = new BufferedImage(width, height, BufferedImage.TYPE_3BYTE_BGR);

Graphics g = result.getGraphics();

try {

g.drawImage(src.getScaledInstance(width, height, Image.SCALE_SMOOTH), 0, 0, null);

} finally {

g.dispose();

}

return result;

}

 

/**

* 計(jì)算均值

*

* @param src

* @return

*/

private static int mean(byte[] src) {

long sum = 0;

// 將數(shù)組元素轉(zhuǎn)為無(wú)符號(hào)整數(shù)

for (byte b : src) {

sum += (long) b & 0xff;

}

return (int) (Math.round((float) sum / src.length));

}

 

/**

* 二值化處理

*

* @param src

* @return

*/

private static byte[] binaryzation(byte[] src) {

byte[] dst = src.clone();

int mean = mean(src);

for (int i = 0; i < dst.length; ++i) {

// 將數(shù)組元素轉(zhuǎn)為無(wú)符號(hào)整數(shù)再比較

dst[i] = (byte) (((int) dst[i] & 0xff) >= mean ? 1 : 0);

}

return dst;

 

}

 

/**

* 轉(zhuǎn)灰度圖像

*

* @param src

* @return

*/

private static BufferedImage toGray(BufferedImage src) {

if (src.getType() == BufferedImage.TYPE_BYTE_GRAY) {

return src;

} else {

// 圖像轉(zhuǎn)灰

BufferedImage grayImage = new BufferedImage(src.getWidth(), src.getHeight(), BufferedImage.TYPE_BYTE_GRAY);

new ColorConvertOp(ColorSpace.getInstance(ColorSpace.CS_GRAY), null).filter(src, grayImage);

return grayImage;

}

}

 

@Override

public String toString() {

return toString(true);

}

 

/**

* @param multiLine

*            是否分行

* @return

*/

public String toString(boolean multiLine) {

StringBuffer buffer = new StringBuffer();

int count = 0;

for (byte b : this.binaryzationMatrix) {

buffer.append(0 == b ? '0' : '1');

if (multiLine && ++count % HASH_SIZE == 0) {

buffer.append('\n');

}

}

return buffer.toString();

}

 

@Override

public boolean equals(Object obj) {

if (obj instanceof PicFinger) {

return Arrays.equals(this.binaryzationMatrix, ((PicFinger) obj).binaryzationMatrix);

} else {

return super.equals(obj);

}

}

 

/**

* 與指定的壓縮格式指紋比較相似度

*

* @param compactValue

* @return

* @see #compare(PicFinger)

*/

public float compareCompact(byte[] compactValue) {

return compare(createFromCompact(compactValue));

}

 

/**

* @param hashValue

* @return

* @see #compare(PicFinger)

*/

public float compare(String hashValue) {

return compare(new PicFinger(hashValue));

}

 

/**

* 與指定的指紋比較相似度

*

* @param hashValue

* @return

* @see #compare(PicFinger)

*/

public float compare(byte[] hashValue) {

return compare(new PicFinger(hashValue));

}

 

/**

* 與指定圖像比較相似度

*

* @param image2

* @return

* @see #compare(PicFinger)

*/

public float compare(BufferedImage image2) {

return compare(new PicFinger(image2));

}

 

/**

* 比較指紋相似度

*

* @param src

* @return

* @see #compare(byte[], byte[])

*/

public float compare(PicFinger src) {

if (src.binaryzationMatrix.length != this.binaryzationMatrix.length) {

throw new IllegalArgumentException("length of hashValue is mismatch");

}

return compare(binaryzationMatrix, src.binaryzationMatrix);

}

 

/**

* 判斷兩個(gè)數(shù)組相似度,數(shù)組長(zhǎng)度必須一致否則拋出異常

*

* @param f1

* @param f2

* @return 返回相似度(0.0 ~ 1.0)

*/

private static float compare(byte[] f1, byte[] f2) {

if (f1.length != f2.length) {

throw new IllegalArgumentException("mismatch FingerPrint length");

}

int sameCount = 0;

for (int i = 0; i < f1.length; ++i) {

{

if (f1[i] == f2[i]) {

++sameCount;

}

}

}

return (float) sameCount / f1.length;

}

 

public static float compareCompact(byte[] f1, byte[] f2) {

return compare(uncompact(f1), uncompact(f2));

}

 

public static float compare(BufferedImage image1, BufferedImage image2) {

return new PicFinger(image1).compare(new PicFinger(image2));

}

 

public static Map<String, byte[]> getLibMatrix(List<File> imgListLib) {

Map<String, byte[]> binMap = new ConcurrentHashMap<String, byte[]>();

BufferedImage libBuf = null;

PicFinger fpLib = null;

// 初始化Lib庫(kù)

String fileName = null;

System.out.print("getLibMatrix() imgListLib size=" + imgListLib.size());

int c = 0;

for (File imgfileLib : imgListLib) {

if (imgfileLib.exists()) {

fileName = imgfileLib.getName();

fileName = fileName.substring(0, fileName.indexOf("."));

try {

libBuf = ImageIO.read(imgfileLib);

} catch (IOException e) {

e.printStackTrace();

}

if (libBuf != null) {

fpLib = new PicFinger(libBuf);

binMap.put(fileName, fpLib.getBinaryzationMatrix());

if (c % 100 == 0) {

System.out.print("\ngetLib() list c=" + c + " ");

}

System.out.print(fileName + ",");

c++;

} else {

System.out.println("libBuf=" + libBuf + "|imgfileLib=" + imgfileLib.getName());

}

} else {

continue;

}

}

System.out.println("\ngetLibMatrix() size=" + binMap.size());

return binMap;

}

}

```

 

 3. 把圖片旋轉(zhuǎn)360°

 

```java

public static Color bgColor = new Color(255, 255, 255);

 

/**

* 創(chuàng)建任意角度的旋轉(zhuǎn)圖像

* @param image

* @param theta

* @param backgroundColor

* @return

*/

public BufferedImage rotateImage(BufferedImage image, double theta, Color backgroundColor) {

int width = image.getWidth();

int height = image.getHeight();

double angle = theta * Math.PI / 180; // 度轉(zhuǎn)弧度

double[] xCoords = getX(width / 2, height / 2, angle);

double[] yCoords = getY(width / 2, height / 2, angle);

int WIDTH = (int) (xCoords[3] - xCoords[0]);

int HEIGHT = (int) (yCoords[3] - yCoords[0]);

BufferedImage resultImage = new BufferedImage(WIDTH, HEIGHT, BufferedImage.TYPE_INT_RGB);

for (int i = 0; i < WIDTH; i++) {

for (int j = 0; j < HEIGHT; j++) {

int x = i - WIDTH / 2;

int y = HEIGHT / 2 - j;

double radius = Math.sqrt(x * x + y * y);

double angle1;

if (y > 0) {

angle1 = Math.acos(x / radius);

} else {

angle1 = 2 * Math.PI - Math.acos(x / radius);

}

x = (int) Math.round(radius * Math.cos(angle1 - angle));

y = (int) Math.round(radius * Math.sin(angle1 - angle));

if (x < (width / 2) & x > -(width / 2) & y < (height / 2) & y > -(height / 2)) {

int rgb = image.getRGB((int) Math.round(x + width / 2), (int) Math.round(height / 2 - y));

resultImage.setRGB(i, j, rgb);

} else {

resultImage.setRGB(i, j, -1);

}

}

}

return resultImage;

}

 

// 獲取四個(gè)角點(diǎn)旋轉(zhuǎn)后Y方向坐標(biāo)

private double[] getY(int i, int j, double angle) {

double results[] = new double[4];

double radius = Math.sqrt(i * i + j * j);

double angle1 = Math.asin(j / radius);

results[0] = radius * Math.sin(angle1 + angle);

results[1] = radius * Math.sin(Math.PI - angle1 + angle);

results[2] = -results[0];

results[3] = -results[1];

Arrays.sort(results);

return results;

}

 

// 獲取四個(gè)角點(diǎn)旋轉(zhuǎn)后X方向坐標(biāo)

private double[] getX(int i, int j, double angle) {

double results[] = new double[4];

double radius = Math.sqrt(i * i + j * j);

double angle1 = Math.acos(i / radius);

results[0] = radius * Math.cos(angle1 + angle);

results[1] = radius * Math.cos(Math.PI - angle1 + angle);

results[2] = -results[0];

results[3] = -results[1];

Arrays.sort(results);

return results;

}

 

public BufferedImage writeCyclePic(BufferedImage image) {

BufferedImage newImage = new BufferedImage(152, 152, BufferedImage.TYPE_INT_BGR);

try {

int width = image.getWidth();

int heigth = image.getHeight();

double x0 = width / 2;

double y0 = heigth / 2;

int woffset = (width - 152) / 2;

int hoffset = (heigth - 152) / 2;

for (int i = woffset; i < 152 + woffset; i++) {

for (int j = hoffset; j < 152 + hoffset; j++) {

double r = Math.sqrt(Math.pow(Math.abs(i - x0), 2.0) + Math.pow(Math.abs(j - y0), 2.0));

if (r > (x0 - woffset)) {

newImage.setRGB(i - woffset, j - hoffset, -1);

} else {

newImage.setRGB(i - woffset, j - hoffset, image.getRGB(i, j));

}

}

}

return newImage;

} catch (Exception e) {

e.printStackTrace();

return null;

}

}

 

// 旋轉(zhuǎn)生成圖片

//start 原圖轉(zhuǎn)正需要滑動(dòng)的距離

//total 最長(zhǎng)距離

//input 原圖距離

//outPath 新圖目錄

public void rotate360(Integer start, Integer total, File input, String outPath) {

try {

BufferedImage image = ImageIO.read(input);

int distance;

BufferedImage mid, result;

File output;

for (int i = 0; i < 360; i++) {

distance = start + Math.round(i * total / 360);

distance = distance > total ? distance - total : distance;

mid = rotateImage(image, i, bgColor);

result = writeCyclePic(mid);

if (outPath != null && !"".equals(outPath)) {

output = new File(outPath+i+"-"+distance+".png");

ImageIO.write(result, "png", output);

}

}

} catch (Exception e) {

e.printStackTrace();

}

}

```

 

 4. 把圖片模型轉(zhuǎn)為可比較對(duì)象信息存入模型庫(kù)

 

```java

//Map<String, Map<String, PicFinger>> 中的第一個(gè)String唯一就好了可以用自然數(shù)依次記錄,

//第二個(gè)String記錄需要滑動(dòng)的距離,PicFinger為可比較的圖片信息

public static Map<String, Map<String, PicFinger>> allMap = new HashMap<String, Map<String, PicFinger>>();

 

public static PicFinger getPicFinger(File file) {

byte[] img;

try {

BufferedImage libBuf = ImageIO.read(file);

PicFinger picFinger = new PicFinger(libBuf);

img = picFinger.getBinaryzationMatrix();

PicFinger fp = new PicFinger(img);

return fp;

} catch (IOException e) {

e.printStackTrace();

return null;

}

}

 

public static List<File> getFiles(String path) {

File root = new File(path);

List<File> files = new ArrayList<File>();

if (!root.isDirectory()) {

files.add(root);

} else {

File[] subFiles = root.listFiles();

for (File f : subFiles) {

files.addAll(getFiles(f.getAbsolutePath()));

}

}

return files;

}

 

public static void save(String file, Object o) throws Exception {

FileOutputStream outStream = null;

ObjectOutputStream objectOutputStream = null;

try {

outStream = new FileOutputStream(file);

objectOutputStream = new ObjectOutputStream(outStream);

objectOutputStream.writeObject(o);

objectOutputStream.close();

} catch (Exception e) {

} finally {

if (outStream != null)

outStream.close();

if (objectOutputStream != null)

objectOutputStream.close();

}

}

 

public static Object load(String file) throws Exception {

FileInputStream freader = null;

ObjectInputStream objectInputStream = null;

try {

freader = new FileInputStream(file);

objectInputStream = new ObjectInputStream(freader);

Object o = objectInputStream.readObject();

return o;

} catch (Exception e) {

return null;

} finally {

if (freader != null)

freader.close();

if (objectInputStream != null)

objectInputStream.close();

}

}

```

 

 四、結(jié)果展示

 

所有的流程都走完了,不妨做個(gè)測(cè)試。

 

粗略觀察了下,效果還不錯(cuò)。難免其中也存在識(shí)別錯(cuò)誤的情況,接下來(lái)做下結(jié)果分析。

 

五、結(jié)果分析

目標(biāo):

 

> 識(shí)別圖片角度,推算出對(duì)應(yīng)滑動(dòng)距離,模擬滑動(dòng)。

 

實(shí)現(xiàn)思路:

 

> 抓取圖片,篩選

> 生成各個(gè)角度圖片模型,標(biāo)記正向圖

> 將推算距離整合模型數(shù)據(jù),建造模型庫(kù)

> 抓到圖片后通過(guò)圖片相似度比較算法匹配模型庫(kù)

> 根據(jù)匹配出的距離模擬滑動(dòng)

 

檢測(cè)耗時(shí):

 

> 15 - 100毫秒

 

通過(guò)率:

 

> 95%(低樣本)

 

 

最終測(cè)試結(jié)果為300條樣本結(jié)果,這個(gè)樣本數(shù)還是偏少了,不確定在更多的測(cè)試條數(shù)時(shí)還會(huì)不會(huì)達(dá)到這樣的效果,應(yīng)該不會(huì)差太遠(yuǎn)哈。

 

作者:香芋味的貓丶