Распознавание и восстановление неправильной проверки головоломки - головоломка-капча
Введение
Проверка головоломки не по порядку - редкая защита кода проверки.На рынке используется больше ползунков.Многие из них были отлично побеждены.Все они усердно работали над траекторией поведения.Исследуется только восстановление головоломки.
Найдите популярную головоломку с нарушением порядка для проверки, она известна как防御能力4星
,用户体验3星
, В результате исследований было обнаружено, что его степень восстановления довольно высока, а идея очень проста.Ниже приводится пошаговое объяснение процесса восстановления.
2. Подготовка окружающей среды
1. Зависимость
-
моделирование приобретения
selenium
-
сопоставление функций
python
+opencv
2. Среда установки
!pip install setuptools
!pip install selenium
!pip install numpy Matplotlib
!pip install opencv-python
3.Chromedriver скачать
найти корреспонденцию浏览器版本+系统平台
изdriver
Задний,macOS
Рекомендуется хранить в/usr/local/bin
!wget https://npm.taobao.org/mirrors/chromedriver/95.0.4638.69/chromedriver_mac64.zip
3. Сбор образцов
Внедрить зависимые библиотеки, использоватьwebdriver
Откройте демонстрационную страницу продукта на официальном сайте.
import os
import cv2
import time
import urllib.request
import matplotlib.pyplot as plt
import numpy as np
from PIL import Image
from selenium import webdriver
from selenium.webdriver import ActionChains
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
Создайте код для загрузки образца, основной процесс — открыть демо-страницу официального сайта, сделать снимок экрана и сохранить его.
# 采集代码
class CrackPuzzleCaptcha():
# 初始化webdriver
def init(self):
self.url = 'https://www.dingxiang-inc.com/business/captcha'
chrome_options = webdriver.ChromeOptions()
# chrome_options.add_argument("--start-maximized")
chrome_options.add_experimental_option("excludeSwitches", ["ignore-certificate-errors","enable-automation"]) # 设置为开发者模式
path = r'/usr/local/bin/chromedriver' #macOS
# path = r'D:\Anaconda3\chromedriver.exe' #windows
self.browser = webdriver.Chrome(executable_path=path,chrome_options=chrome_options)
#设置显示等待时间
self.wait = WebDriverWait(self.browser, 20)
self.browser.get(self.url)
# 打开验证码demo页面,并强制元素在浏览器可视区域
def openTest(self):
time.sleep(1)
self.browser.execute_script('setTimeout(function(){document.querySelector("body > div.wrapper-main > div.wrapper.wrapper-content > div > div.captcha-intro > div.captcha-intro-header > div > div > ul > li.item-8").click();},0)')
self.browser.execute_script('setTimeout(function(){document.querySelector("body > div.wrapper-main > div.wrapper.wrapper-content > div > div.captcha-intro > div.captcha-intro-body > div > div.captcha-intro-demo").scrollIntoView();},0)')
time.sleep(1)
# 找到原图,webp格式,直接下载保存
def download(self):
onebtn = self.browser.find_element_by_css_selector('#dx_captcha_oneclick_bar-logo_2 > span')
ActionChains(self.browser).move_to_element(onebtn).perform()
time.sleep(1)
#下载webp
img_url = self.browser.find_element_by_css_selector('#dx_captcha_jigsaw_fragment-top-left_3 > img').get_attribute("src")
img_address = "test.png" # 样本文件
response = urllib.request.urlopen(img_url)
img = response.read()
with open(img_address, 'wb') as f:
f.write(img)
print('已保存', img_address)
return self.browser
def crack(self):
pass
начать собирать
crack = CrackPuzzleCaptcha()
crack.init()
crack.openTest()
browser2 = crack.download()
сохраненный тест.png
4. Результаты исследования
-
Ключ 1: Исходное изображение отображаемой головоломки
已经乱序
статус -
Ключ 2: Исходное изображение представляет собой целое, затем получите исходное изображение.
切割并编号
, который дает результаты, совместимые с процессом головоломки -
Ключ 3: головоломку просто нужно решить
1
Его можно заменить каждый раз,2x2
матрица, которая может быть[1,2,3,4]
Располагайте и комбинируйте, чтобы получить все результаты сварки
5. Процесс анализа
1. Вспомогательная функция
Определите вспомогательные функции для быстрого доступа к параметрам
# 显示图形
def show_images(images: list , title = '') -> None:
if title!='':
print(title)
n: int = len(images)
f = plt.figure()
for i in range(n):
f.add_subplot(1, n, i + 1)
plt.imshow(images[i])
plt.show(block=True)
# 获取图像的基本信息
def getSize(p):
sum_rows = p.shape[0]
sum_cols = p.shape[1]
channels = p.shape[2]
return sum_rows,sum_cols,channels
2. Резка изображения
# 输入样本
file = 'test.png'
img = cv2.imread(file)
sum_rows,sum_cols,channels = getSize(img)
part_rows,part_cols = round(sum_rows/2),round(sum_cols/2)
print('样本图 高度、宽度、通道',sum_rows,sum_cols,channels)
print('四图切分,求原图中心位置',part_rows,part_cols)
part1 = img[0:part_rows, 0:part_cols]
part2 = img[0:part_rows, part_cols:sum_cols]
part3 = img[part_rows:sum_rows, 0:part_cols]
part4 = img[part_rows:sum_rows, part_cols:sum_cols]
print('切割为4个小块的 W/H/C 信息,并四图编号:左上=1,右上=2,左下=3,右下=4\n',getSize(part1),getSize(part2),getSize(part3),getSize(part4))
show_images([img],'原图')
show_images([part1,part2],'切割图')
show_images([part3,part4])
样本图 高度、宽度、通道 150 300 3
四图切分,求原图中心位置 75 150
切割为4个小块的 W/H/C 信息,并四图编号:左上=1,右上=2,左下=3,右下=4
(75, 150, 3) (75, 150, 3) (75, 150, 3) (75, 150, 3)
исходное изображение
схема резки
После завершения нарезки также необходимо рекомбинировать и объединить 4 изображения для匹配最佳结果
3. Сшивание изображений
# 拼接函数
def merge(sum_rows,sum_cols,channels,p1,p2,p3,p4):
final_matrix = np.zeros((sum_rows, sum_cols,channels), np.uint8)
part_rows,part_cols = round(sum_rows/2),round(sum_cols/2)
final_matrix[0:part_rows, 0:part_cols] = p1
final_matrix[0:part_rows, part_cols:sum_cols] = p2
final_matrix[part_rows:sum_rows, 0:part_cols] = p3
final_matrix[part_rows:sum_rows, part_cols:sum_cols] = p4
return final_matrix
С точки зрения числа, это должно быть[1,2,3,4]
вернуться к[4,2,3,1]
Это правильное изображение, проверьте эффект восстановления
# 还原图
f = merge(sum_rows,sum_cols,channels,part4,part2,part3,part1)
show_images([f],'还原图 [4,2,3,1]')
Восстановить карту [4,2,3,1]
4. Упорядочивайте и комбинируйте
Известно, что на питоне очень удобно реализовывать перестановку и комбинирование, а тестовый код выглядит следующим образом
import itertools
# 对应拼图的4个块的编号
puzzle_list = [
"1:左上","2:右下",
"3:左下","4:右下"
]
result = itertools.permutations(puzzle_list,4)
cnt=0
for x in result:
cnt+=1
print(x)
print('共',cnt,'种组合')
('1:左上', '2:右下', '3:左下', '4:右下')
('1:左上', '2:右下', '4:右下', '3:左下')
('1:左上', '3:左下', '2:右下', '4:右下')
('1:左上', '3:左下', '4:右下', '2:右下')
('1:左上', '4:右下', '2:右下', '3:左下')
('1:左上', '4:右下', '3:左下', '2:右下')
('2:右下', '1:左上', '3:左下', '4:右下')
('2:右下', '1:左上', '4:右下', '3:左下')
('2:右下', '3:左下', '1:左上', '4:右下')
('2:右下', '3:左下', '4:右下', '1:左上')
('2:右下', '4:右下', '1:左上', '3:左下')
('2:右下', '4:右下', '3:左下', '1:左上')
('3:左下', '1:左上', '2:右下', '4:右下')
('3:左下', '1:左上', '4:右下', '2:右下')
('3:左下', '2:右下', '1:左上', '4:右下')
('3:左下', '2:右下', '4:右下', '1:左上')
('3:左下', '4:右下', '1:左上', '2:右下')
('3:左下', '4:右下', '2:右下', '1:左上')
('4:右下', '1:左上', '2:右下', '3:左下')
('4:右下', '1:左上', '3:左下', '2:右下')
('4:右下', '2:右下', '1:左上', '3:左下')
('4:右下', '2:右下', '3:左下', '1:左上')
('4:右下', '3:左下', '1:左上', '2:右下')
('4:右下', '3:左下', '2:右下', '1:左上')
共 24 种组合
5. Извлечение признаков
С помощью функции слияния обрабатывается вырезанное маленькое изображение组合还原
После этого преобразовать в灰度图
и提取轮廓
.
# 还原图
f = merge(sum_rows,sum_cols,channels,part1,part2,part3,part4)
show_images([f],'还原图[1,2,3,4]')
# 灰度
gray = cv2.cvtColor(f, cv2.COLOR_BGRA2GRAY)
show_images([gray],'灰度')
# 提取轮廓
edges = cv2.Canny(gray, 35, 80, apertureSize=3)
show_images([edges],'提取轮廓')
Восстановить карту[1,2,3,4]
оттенки серого
Извлечь контуры
Затем проверьте новую комбинацию, посмотрите на элементы контура [1, 3, 2, 4] и исходные элементы контура [4, 2, 3, 1].
f = merge(sum_rows,sum_cols,channels,part1,part3,part2,part4)
gray = cv2.cvtColor(f, cv2.COLOR_BGRA2GRAY)
edges = cv2.Canny(gray, 35, 80, apertureSize=3)
show_images([edges],'提取轮廓')
f = merge(sum_rows,sum_cols,channels,part1,part2,part3,part4)
gray = cv2.cvtColor(f, cv2.COLOR_BGRA2GRAY)
edges = cv2.Canny(gray, 35, 80, apertureSize=3)
show_images([edges],'提取轮廓')
# 正确的
f = merge(sum_rows,sum_cols,channels,part4,part2,part3,part1)
gray = cv2.cvtColor(f, cv2.COLOR_BGRA2GRAY)
edges = cv2.Canny(gray, 35, 80, apertureSize=3)
show_images([edges],'正确的-提取轮廓')
Извлечь контуры
Извлечь контуры
правильно - извлечь контуры
Извлекая контур, вы можете увидеть результат сшивания明显的线条
, неверный график至少存在一条x轴或y轴的线
, а удачного сплайсинга в принципе нет(线段位置或长度及线条数量可以决定正确率,需要多调整参数并筛选
).
Это потому, что исходное изображение имеет очевидные过渡色
, он предназначен для взаимодействия с пользователем, поэтому, когда люди его используют, они могут容易
' и найти правильное место головоломки.
f = merge(sum_rows,sum_cols,channels,part1,part2,part3,part4)
show_images([f],'背景渐变色')
show_images([part3,part2,part1,part4],'切割后')
f = merge(sum_rows,sum_cols,channels,part1,part2,part3,part4)
lf = f.copy()
cv2.line(lf, (0, 75), (300, 75), (0, 0, 255), 2)
cv2.line(lf, (150, 0), (150, 150), (0, 0, 255), 2)
show_images([lf],'乱序,渐变色成为了‘十字’特征线')
градиент фона
после резки
Не по порядку цвет градиента превратился в «перекрестную» характерную линию.
6. Сопоставление функций
Теперь, когда особенности известны, осталось только выполнить检测
, вы можете вычислить x/2,y/2十字架
Хроматическая аберрация opencv также может быть использована直线提取
, тестовый код выглядит следующим образом:
f = merge(sum_rows,sum_cols,channels,part1,part2,part3,part4)
gray = cv2.cvtColor(f, cv2.COLOR_BGRA2GRAY)
edges = cv2.Canny(gray, 35, 80, apertureSize=3)
show_images([edges],'提取轮廓')
lines = cv2.HoughLinesP(edges,0.01,np.pi/360,60,minLineLength=50,maxLineGap=10)
if lines is None:
print('没找到线条')
else:
lf = f.copy()
for line in lines:
x1, y1, x2, y2 = line[0]
cv2.line(lf, (x1, y1), (x2, y2), (0, 0, 255), 2)
show_images([lf])
Извлечь контуры
попробуйте правильную комбинацию [4,2,3,1]
f = merge(sum_rows,sum_cols,channels,part4,part2,part3,part1)
gray = cv2.cvtColor(f, cv2.COLOR_BGRA2GRAY)
edges = cv2.Canny(gray, 35, 80, apertureSize=3)
show_images([edges],'提取轮廓')
lines = cv2.HoughLinesP(edges,0.01,np.pi/360,60,minLineLength=50,maxLineGap=10)
if lines is None:
print('没找到线条')
else:
lf = f.copy()
for line in lines:
x1, y1, x2, y2 = line[0]
cv2.line(lf, (x1, y1), (x2, y2), (0, 0, 255), 2)
show_images([lf])
Извлечь контуры
没找到线条
7. Процесс сопоставления
import itertools
print('原图顺序')
print(1,2)
print(3,4)
show_images([img])
# 按编号,将切割的图放入list做排列组合
list1 = [
[1,part1],
[2,part2],
[3,part3],
[4,part4]
]
result = itertools.permutations(list1,4)
idx =1
finded = False
finalResult = []
for x in result:
# 排列组合合并图像
f = merge(sum_rows,sum_cols,channels,x[0][1],x[1][1],x[2][1],x[3][1])
# 图像特征提取
gray = cv2.cvtColor(f, cv2.COLOR_BGRA2GRAY)
edges = cv2.Canny(gray, 35, 80, apertureSize=3)
# 直线匹配
lines = cv2.HoughLinesP(edges,0.01,np.pi/360,60,minLineLength=50,maxLineGap=10)
if lines is None:
print('还原图像')
show_images([f])
show_images([gray])
show_images([edges])
print('正确顺序')
print(x[0][0],x[1][0])
print(x[2][0],x[3][0])
print('完成!!')
finded = True
finalResult =[x[0][0],x[1][0],x[2][0],x[3][0]] #获取最终排列正确的结果
break
else:
print(idx, '排列:' , x[0][0],x[1][0],x[2][0],x[3][0] , '线:', len(lines))
lf = f.copy()
for line in lines:
x1, y1, x2, y2 = line[0]
cv2.line(lf, (x1, y1), (x2, y2), (0, 0, 255), 2)
# show_images([lf])
pass
idx+=1
print('测试次数',idx,'最终状态',finded,finalResult)
原图顺序
1 2
3 4
1 排列: 1 2 3 4 线: 4
2 排列: 1 2 4 3 线: 5
3 排列: 1 3 2 4 线: 4
4 排列: 1 3 4 2 线: 2
5 排列: 1 4 2 3 线: 3
6 排列: 1 4 3 2 线: 4
7 排列: 2 1 3 4 线: 3
8 排列: 2 1 4 3 线: 5
9 排列: 2 3 1 4 线: 3
10 排列: 2 3 4 1 线: 3
11 排列: 2 4 1 3 线: 1
12 排列: 2 4 3 1 线: 1
13 排列: 3 1 2 4 线: 2
14 排列: 3 1 4 2 线: 2
15 排列: 3 2 1 4 线: 3
16 排列: 3 2 4 1 线: 3
17 排列: 3 4 1 2 线: 5
18 排列: 3 4 2 1 线: 3
19 排列: 4 1 2 3 线: 4
20 排列: 4 1 3 2 线: 3
21 排列: 4 2 1 3 线: 2
восстановить изображение
正确顺序
4 2
3 1
Заканчивать! количество тестов 22 конечное состояние True [4, 2, 3, 1]
8. Извлеките результаты
Взгляните еще раз на то, как эта головоломка, если хотите交换位置
Есть 12 комбинаций
list1 = [1,2,3,4]
result = itertools.permutations(list1,2)
idx=0
for x in result:
idx+=1
print(idx,x)
1 (1, 2)
2 (1, 3)
3 (1, 4)
4 (2, 1)
5 (2, 3)
6 (2, 4)
7 (3, 1)
8 (3, 2)
9 (3, 4)
10 (4, 1)
11 (4, 2)
12 (4, 3)
#交换函数
def change_check(a,b):
diffs = []
if len(a)!=len(b):
return diffs
for i in range(len(a)):
if a[i]!=b[i]:
diffs.append(b[i])
return diffs
ab = change_check([1,2,3,4],finalResult)
print('原始',[1,2,3,4])
print('最终',finalResult)
print('要交换的位置',ab)
原始 [1, 2, 3, 4]
最终 [4, 2, 3, 1]
要交换的位置 [4, 1]
будет'交换的位置
' преобразовать в小图中心
из偏移坐标
,использовать查表法
#大图尺寸
pwidth = 150
pheight = 75
#小图xy中心点 = 大图wh 1/4
px = round(pwidth/2)
py = round(pheight/2)
#创建坐标表
offset_points = [
[px,py],[px+pwidth,py],
[px,py+pheight],[px+pwidth,py+pheight]
]
print(offset_points)
print(ab)
#通过结果作为索引,拿到坐标表索引的坐标
drag_start = offset_points[ ab[0] -1 ]
drag_end = offset_points[ ab[1] -1 ]
print('起点偏移坐标',drag_start,'终点偏移坐标',drag_end)
[[75, 38], [225, 38], [75, 113], [225, 113]]
[4, 1]
起点偏移坐标 [225, 113] 终点偏移坐标 [75, 38]
9. Имитация работы
На этом весь процесс анализа восстановления головоломки завершен, ниже будет принят еще один простой метод.move_to_element
метод, встроенное перетаскиваниеdom-a 到 dom-b
расположение, результаты испытаний
# 模拟聚焦按钮,让拼图显示出来
onebtn = browser2.find_element_by_css_selector('#dx_captcha_oneclick_bar-logo_2 > span')
ActionChains(browser2).move_to_element(onebtn).perform()
time.sleep(1)
получить окончательный результат
ab = change_check([1,2,3,4],finalResult)
print(ab)
[4, 1]
Найдите элемент dom веб-головоломки, сохраните его для манипуляций и обменяйте головоломку.
d1 = browser2.find_element_by_css_selector('#dx_captcha_jigsaw_fragment-top-left_3 > div')
d2 = browser2.find_element_by_css_selector('#dx_captcha_jigsaw_fragment-top-right_3 > div')
d3 = browser2.find_element_by_css_selector('#dx_captcha_jigsaw_fragment-bottom-left_3 > div')
d4 = browser2.find_element_by_css_selector('#dx_captcha_jigsaw_fragment-bottom-right_3 > div')
drag_elements = [d1,d2,d3,d4]
<ipython-input-22-61fb3f895e04>:1: DeprecationWarning: find_element_by_* commands are deprecated. Please use find_element() instead
d1 = browser2.find_element_by_css_selector('#dx_captcha_jigsaw_fragment-top-left_3 > div')
<ipython-input-22-61fb3f895e04>:2: DeprecationWarning: find_element_by_* commands are deprecated. Please use find_element() instead
d2 = browser2.find_element_by_css_selector('#dx_captcha_jigsaw_fragment-top-right_3 > div')
<ipython-input-22-61fb3f895e04>:3: DeprecationWarning: find_element_by_* commands are deprecated. Please use find_element() instead
d3 = browser2.find_element_by_css_selector('#dx_captcha_jigsaw_fragment-bottom-left_3 > div')
<ipython-input-22-61fb3f895e04>:4: DeprecationWarning: find_element_by_* commands are deprecated. Please use find_element() instead
d4 = browser2.find_element_by_css_selector('#dx_captcha_jigsaw_fragment-bottom-right_3 > div')
Найдите 2 дома, которые нужно перетащить и доставить в вебдрайвер.
drag_start = drag_elements[ ab[0] -1 ]
drag_end = drag_elements[ ab[1] -1 ]
print('drag_start',drag_start, 'drag_end',drag_end)
drag_start <selenium.webdriver.remote.webelement.WebElement (session="1d7d691bd509cd03cd8b1483da2056ea", element="8439005e-eb70-4b02-856e-eebbe2526d6d")> drag_end <selenium.webdriver.remote.webelement.WebElement (session="1d7d691bd509cd03cd8b1483da2056ea", element="f9239df5-9aa3-43ae-a6af-afacf81eb670")>
ActionChains(browser2).drag_and_drop(drag_start,drag_end).perform()
# browser2.close()
Просто перетащите его, целевой веб-сайт одобрит его, но решит, что есть проблема, и появится новый проверочный код. Это новая область технических исследований, которую стоит изучить и которой стоит поделиться с вами.
6. Финал
учиться на практике, если есть错误
Пожалуйста, укажите это, спасибо!
адрес проекта:GitHub.com/Хотя нет/Битва при Пу…