技术标签: 验证码识别 python java 百度验证码 百度 oauth 图像识别
戳这里→康康你手机号在过多少网站注册过!!!
友情推荐:新一代安全短信
谷歌图形验证码在AI 面前已经形同虚设,所以谷歌宣布退出验证码服务, 那么当所有的图形验证码都被破解时
《腾讯防水墙滑动拼图验证码》
《百度旋转图片验证码》
《网易易盾滑动拼图验证码》
《顶象区域面积点选验证码》
《顶象滑动拼图验证码》
《极验滑动拼图验证码》
《使用深度学习来破解 captcha 验证码》
《验证码终结者-基于CNN+BLSTM+CTC的训练部署套件》
百度的验证码又双叒更新了。
当然出于好奇,猫又拿起了键盘开始挑战。
先来看看继上次破解百度旋转验证码后,百度的大佬又做出了哪些改变。
1.抓取图片时加上了马赛克
2.增加了图片库
哦呦,这个马赛克有点东西的呀~
图片抓下来都不一样还咋识别,百度这里也是煞费苦心,给您点个赞。
不过话说回来,就算这样也难不住我们的呀,这里我思考了一下还有几种方式来获取这个图片:
1.通过系统级鼠标来获取
首先,试了下第一种方式
定位到图片路径位置拿到图片途径,然后再通过模拟器打来另一个页面
然后通过下面这段代码实现保存图片的操作(这里用到了Robot
系统级鼠标控制类 )
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;
// 声明一个StingSelection 对象,并使用String的参数完成实例化;
String imgName = "baidu_" + System.currentTimeMillis()+".jpg";
// 使用Toolkit对象的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);
// 确认
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);
// 文件名字后确认
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;
}
啊哈,拿到了。
本以为就这样结束了。
万万没想到,抓了几张之后马赛克又出现了。。。。。。。
到此,第一种方法宣告西败。
2.通过网页截图来获取
这个方法就比较靠谱了,百度总不能让用户看到马赛克的图片吧,哈哈(手动狗头)
// 获取ID的随机数
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());
/**
* 截图(验证码) 这里的 152 是页面显示图片的实际宽高
*/
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;
}
那么到这里我们就拿到了验证图,截来的图片肯定没有原图清晰度高,所以识别率就会稍微降低一些。不过影响不是太大。
到这里抓取图片的问题就解决了。
说道增加图片库这里倒不是什么大问题,只要抓到一张图片,就可以通过旋转生成对应的图片,只需要找到不同的图片就可以了。
之前百度大概有50-60张不同的图片,也就是说不同的角度(360°)全部加入图片库的话最多21600张图(估计值)。
现在通过抓取了几百张图片观察,不同的图片大概有120多张,之前的50-60张也包括在内,也就是说百度又新加了一倍的图库。大概在43000张图左右。
这里不存在什么大问题,只是我们的模型库需要更新一下而已。
正儿八经的分割线
好了上面的问题既然都解决了,那么来说下具体的破解思路及步骤。
说起来也不是很复杂嘛。开搞~
上面有提到抓取图片我们采用截图的方式,自动化程序这里就先不放了,文章后面有完整代码。我们先看下结果。
可以看到有很多相似的图片,我们抓到300-500张左右基本就可以找到全部的不同的图片,然后把相似的只留下一张就好了。
为了提升效率,我们将模型库以map对象的形式存入.obj文件中。
启动程序时只需要读一个文件,然后将其存入map中,大大提升识别效率。
模型库:baidu_mod.obj
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>>();
/**
* 截图(验证码)
*/
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;
}
//将图片变为可比较的对象
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;
}
}
//匹配模型库获取滑动距离
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;
}
//自动化程序
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);
// 输入手机号
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();
// 点击关闭
close = By.xpath("//div[@class='vcode-close']");
closeElemet = waitWebElement(driver, close, 200);
if (closeElemet == null)
return null;
if (closeElemet != null)
closeElemet.click();
// 点击我选中的是手机号
securemobil = By.xpath("//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的随机数
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("最右")) {
// 点击关闭
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的随机数
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++) {
// 获取滑动按钮
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));
// 滑动
move(driver, moveElemet, distance);
sleep(100);
// 获取滑动结果 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();
}
}
/**
* 模拟人工移动
*
* @param driver
* @param element页面滑块
* @param distance需要移动距离
*/
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());
}
}
// 延时加载
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;
}
//读文件,用于读取模型库
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();
}
}
public final class PicFinger implements Serializable {
private static final long serialVersionUID = 431106089062884937L;
/**
* 图像指纹的尺寸,将图像resize到指定的尺寸,来计算哈希数组
*/
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);
}
/**
* 从压缩格式指纹创建{@link PicFinger}对象
*
* @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);
}
/**
* 指纹数据按位压缩
*
* @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;
}
/**
* 字符串类型的指纹数据转为字节数组
*
* @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;
}
/**
* 计算均值
*
* @param src
* @return
*/
private static int mean(byte[] src) {
long sum = 0;
// 将数组元素转为无符号整数
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) {
// 将数组元素转为无符号整数再比较
dst[i] = (byte) (((int) dst[i] & 0xff) >= mean ? 1 : 0);
}
return dst;
}
/**
* 转灰度图像
*
* @param src
* @return
*/
private static BufferedImage toGray(BufferedImage src) {
if (src.getType() == BufferedImage.TYPE_BYTE_GRAY) {
return src;
} else {
// 图像转灰
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);
}
/**
* 判断两个数组相似度,数组长度必须一致否则抛出异常
*
* @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库
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;
}
}
public static Color bgColor = new Color(255, 255, 255);
/**
* 创建任意角度的旋转图像
*
* @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; // 度转弧度
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;
}
// 获取四个角点旋转后Y方向坐标
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;
}
// 获取四个角点旋转后X方向坐标
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;
}
}
// 旋转生成图片
//start 原图转正需要滑动的距离
//total 最长距离
//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();
}
}
//Map<String, Map<String, PicFinger>> 中的第一个String唯一就好了可以用自然数依次记录,
//第二个String记录需要滑动的距离,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();
}
}
所有的流程都走完了,不妨做个测试。
粗略观察了下,效果还不错。难免其中也存在识别错误的情况,接下来做下结果分析。
目标:
识别图片角度,推算出对应滑动距离,模拟滑动。
实现思路:
抓取图片,筛选
生成各个角度图片模型,标记正向图
将推算距离整合模型数据,建造模型库
抓到图片后通过图片相似度比较算法匹配模型库
根据匹配出的距离模拟滑动
检测耗时:
15 - 100毫秒
通过率:
95%(低样本)
最终测试结果为300条样本结果,这个样本数还是偏少了,不确定在更多的测试条数时还会不会达到这样的效果,应该不会差太远哈。
这篇文章到这里就结束了,感谢大佬们驻足观看,大佬们点个关注、点个赞呗~
谢谢大佬~
戳这里→康康你手机号在过多少网站注册过!!!
友情推荐:新一代安全短信
文章浏览阅读645次。这个肯定是末尾的IDAT了,因为IDAT必须要满了才会开始一下个IDAT,这个明显就是末尾的IDAT了。,对应下面的create_head()代码。,对应下面的create_tail()代码。不要考虑爆破,我已经试了一下,太多情况了。题目来源:UNCTF。_攻防世界困难模式攻略图文
文章浏览阅读2.9k次,点赞3次,收藏10次。偶尔会用到,记录、分享。1. 数据库导出1.1 切换到dmdba用户su - dmdba1.2 进入达梦数据库安装路径的bin目录,执行导库操作 导出语句:./dexp cwy_init/[email protected]:5236 file=cwy_init.dmp log=cwy_init_exp.log 注释: cwy_init/init_123..._达梦数据库导入导出
文章浏览阅读1.9k次。1. 在官网上下载KindEditor文件,可以删掉不需要要到的jsp,asp,asp.net和php文件夹。接着把文件夹放到项目文件目录下。2. 修改html文件,在页面引入js文件:<script type="text/javascript" src="./kindeditor/kindeditor-all.js"></script><script type="text/javascript" src="./kindeditor/lang/zh-CN.js"_kindeditor.js
文章浏览阅读2.3k次,点赞6次,收藏14次。SPI的详情简介不必赘述。假设我们通过SPI发送0xAA,我们的数据线就会变为10101010,通过修改不同的内容,即可修改SPI中0和1的持续时间。比如0xF0即为前半周期为高电平,后半周期为低电平的状态。在SPI的通信模式中,CPHA配置会影响该实验,下图展示了不同采样位置的SPI时序图[1]。CPOL = 0,CPHA = 1:CLK空闲状态 = 低电平,数据在下降沿采样,并在上升沿移出CPOL = 0,CPHA = 0:CLK空闲状态 = 低电平,数据在上升沿采样,并在下降沿移出。_stm32g431cbu6
文章浏览阅读1.2k次,点赞2次,收藏8次。数据链路层习题自测问题1.数据链路(即逻辑链路)与链路(即物理链路)有何区别?“电路接通了”与”数据链路接通了”的区别何在?2.数据链路层中的链路控制包括哪些功能?试讨论数据链路层做成可靠的链路层有哪些优点和缺点。3.网络适配器的作用是什么?网络适配器工作在哪一层?4.数据链路层的三个基本问题(帧定界、透明传输和差错检测)为什么都必须加以解决?5.如果在数据链路层不进行帧定界,会发生什么问题?6.PPP协议的主要特点是什么?为什么PPP不使用帧的编号?PPP适用于什么情况?为什么PPP协议不_接收方收到链路层数据后,使用crc检验后,余数为0,说明链路层的传输时可靠传输
文章浏览阅读587次。软件测试工程师移民加拿大 无证移民,未受过软件工程师的教育(第1部分) (Undocumented Immigrant With No Education to Software Engineer(Part 1))Before I start, I want you to please bear with me on the way I write, I have very little gen...
文章浏览阅读304次。Thinkpad X250笔记本电脑,装的是FreeBSD,进入BIOS修改虚拟化配置(其后可能是误设置了安全开机),保存退出后系统无法启动,显示:secure boot failed ,把自己惊出一身冷汗,因为这台笔记本刚好还没开始做备份.....根据错误提示,到bios里面去找相关配置,在Security里面找到了Secure Boot选项,发现果然被设置为Enabled,将其修改为Disabled ,再开机,终于正常启动了。_安装完系统提示secureboot failure
文章浏览阅读10w+次,点赞93次,收藏352次。1、用strtok函数进行字符串分割原型: char *strtok(char *str, const char *delim);功能:分解字符串为一组字符串。参数说明:str为要分解的字符串,delim为分隔符字符串。返回值:从str开头开始的一个个被分割的串。当没有被分割的串时则返回NULL。其它:strtok函数线程不安全,可以使用strtok_r替代。示例://借助strtok实现split#include <string.h>#include <stdio.h&_c++ 字符串分割
文章浏览阅读2.3k次。1 .高斯日记 大数学家高斯有个好习惯:无论如何都要记日记。他的日记有个与众不同的地方,他从不注明年月日,而是用一个整数代替,比如:4210后来人们知道,那个整数就是日期,它表示那一天是高斯出生后的第几天。这或许也是个好习惯,它时时刻刻提醒着主人:日子又过去一天,还有多少时光可以用于浪费呢?高斯出生于:1777年4月30日。在高斯发现的一个重要定理的日记_2013年第四届c a组蓝桥杯省赛真题解答
文章浏览阅读851次,点赞17次,收藏22次。摘要:本文利用供需算法对核极限学习机(KELM)进行优化,并用于分类。
文章浏览阅读1.1k次。一、系统弱密码登录1、在kali上执行命令行telnet 192.168.26.1292、Login和password都输入msfadmin3、登录成功,进入系统4、测试如下:二、MySQL弱密码登录:1、在kali上执行mysql –h 192.168.26.129 –u root2、登录成功,进入MySQL系统3、测试效果:三、PostgreSQL弱密码登录1、在Kali上执行psql -h 192.168.26.129 –U post..._metasploitable2怎么进入
文章浏览阅读257次。本文将为初学者提供Python学习的详细指南,从Python的历史、基础语法和数据类型到面向对象编程、模块和库的使用。通过本文,您将能够掌握Python编程的核心概念,为今后的编程学习和实践打下坚实基础。_python人工智能开发从入门到精通pdf