From 1a4bd3778ad2dc353948cf3f393cd08cc8a1b364 Mon Sep 17 00:00:00 2001 From: beiyuouo Date: Tue, 1 Mar 2022 23:37:28 +0800 Subject: [PATCH 1/3] =?UTF-8?q?build=20=F0=9F=9A=80:=20RAG?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- requirements.txt | 1 + .../hcaptcha_challenger/river_challenger.py | 131 ++++++++++++++++++ 2 files changed, 132 insertions(+) create mode 100644 src/services/hcaptcha_challenger/river_challenger.py diff --git a/requirements.txt b/requirements.txt index ff7a838d18..39b9ebb920 100644 --- a/requirements.txt +++ b/requirements.txt @@ -6,3 +6,4 @@ opencv-python~=4.5.5.62 numpy~=1.22.2 undetected_chromedriver==3.1.3 webdriver-manager>=3.5.2 +scikit-image~=0.19.2 \ No newline at end of file diff --git a/src/services/hcaptcha_challenger/river_challenger.py b/src/services/hcaptcha_challenger/river_challenger.py new file mode 100644 index 0000000000..96a21ce460 --- /dev/null +++ b/src/services/hcaptcha_challenger/river_challenger.py @@ -0,0 +1,131 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# @File : src\services\hcaptcha_challenger\river_challenger.py +# @Time : 2022-03-01 20:32:08 +# @Author : Bingjie Yan +# @Email : bj.yan.pa@qq.com +# @License : Apache License 2.0 + +import cv2 +import numpy as np + +import skimage +from skimage.morphology import disk +from skimage.segmentation import watershed, slic, mark_boundaries +from skimage.filters import rank +from skimage.util import img_as_ubyte +from skimage.color import rgb2gray, label2rgb +from skimage.future import graph +from scipy import ndimage as ndi +import matplotlib.pyplot as plt + + +def _weight_mean_color(graph, src, dst, n): + """Callback to handle merging nodes by recomputing mean color. + + The method expects that the mean color of `dst` is already computed. + + Parameters + ---------- + graph : RAG + The graph under consideration. + src, dst : int + The vertices in `graph` to be merged. + n : int + A neighbor of `src` or `dst` or both. + + Returns + ------- + data : dict + A dictionary with the `"weight"` attribute set as the absolute + difference of the mean color between node `dst` and `n`. + """ + + diff = graph.nodes[dst]['mean color'] - graph.nodes[n]['mean color'] + diff = np.linalg.norm(diff) + return {'weight': diff} + + +def merge_mean_color(graph, src, dst): + """Callback called before merging two nodes of a mean color distance graph. + + This method computes the mean color of `dst`. + + Parameters + ---------- + graph : RAG + The graph under consideration. + src, dst : int + The vertices in `graph` to be merged. + """ + graph.nodes[dst]['total color'] += graph.nodes[src]['total color'] + graph.nodes[dst]['pixel count'] += graph.nodes[src]['pixel count'] + graph.nodes[dst]['mean color'] = (graph.nodes[dst]['total color'] / + graph.nodes[dst]['pixel count']) + + +class RiverChallenger(object): + def __init__(self) -> None: + pass + + def challenge(self, img_stream): + img_arr = np.frombuffer(img_stream, np.uint8) + img = cv2.imdecode(img_arr, flags=1) + height, width = img.shape[:2] + + # # filter + img = cv2.pyrMeanShiftFiltering(img, sp=10, sr=40) + img = cv2.bilateralFilter(img, d=9, sigmaColor=100, sigmaSpace=75) + + # # enhance brightness + # img_hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV) + # img_hsv[:, :, 2] = img_hsv[:, :, 2] * 1.2 + # img = cv2.cvtColor(img_hsv, cv2.COLOR_HSV2BGR) + + labels = slic(img, compactness=30, n_segments=400, start_label=1) + g = graph.rag_mean_color(img, labels) + + labels2 = graph.merge_hierarchical(labels, + g, + thresh=35, + rag_copy=False, + in_place_merge=True, + merge_func=merge_mean_color, + weight_func=_weight_mean_color) + + # view results + # out = label2rgb(labels2, img, kind='avg', bg_label=0) + # out = mark_boundaries(out, labels2, (0, 0, 0)) + # skimage.io.imshow(out) + # skimage.io.show() + # print(np.unique(labels2[-1])) + + ref_value = len(np.unique(labels2[-1])) + return ref_value >= 3 + + +if __name__ == '__main__': + import os + import sys + sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) + + result_path = 'result.txt' + if os.path.exists(result_path): + os.remove(result_path) + + result_file = open(result_path, 'w') + # result_file = sys.stdout + + base_path = os.path.join('database', '_challenge') + list_dirs = os.listdir(base_path) + for dir in list_dirs: + print(dir, file=result_file) + for i in range(1, 10): + img_filepath = os.path.join(base_path, dir, f'挑战图片{i}.png') + + with open(img_filepath, "rb") as file: + data = file.read() + + rc = RiverChallenger() + result = rc.challenge(data) + print(f'挑战图片{i}.png:{result}', file=result_file) \ No newline at end of file From b64e2408d624e1c550083bc3e166f0df61930b55 Mon Sep 17 00:00:00 2001 From: QIN2DIM <62018067+QIN2DIM@users.noreply.github.com> Date: Wed, 2 Mar 2022 03:31:09 +0800 Subject: [PATCH 2/3] =?UTF-8?q?feat(add):=20=F0=9F=9A=80=20handle=20`verti?= =?UTF-8?q?cal=20river`?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/apis/scaffold/challenge.py | 23 +- src/services/hcaptcha_challenger/__init__.py | 4 +- src/services/hcaptcha_challenger/core.py | 199 ++---------------- .../hcaptcha_challenger/river_challenger.py | 131 ------------ .../hcaptcha_challenger/solutions/__init__.py | 5 + .../solutions/ski_river.py | 84 ++++++++ .../hcaptcha_challenger/solutions/yolo.py | 184 ++++++++++++++++ src/services/scaffold.py | 7 +- src/services/settings.py | 3 +- 9 files changed, 322 insertions(+), 318 deletions(-) delete mode 100644 src/services/hcaptcha_challenger/river_challenger.py create mode 100644 src/services/hcaptcha_challenger/solutions/__init__.py create mode 100644 src/services/hcaptcha_challenger/solutions/ski_river.py create mode 100644 src/services/hcaptcha_challenger/solutions/yolo.py diff --git a/src/apis/scaffold/challenge.py b/src/apis/scaffold/challenge.py index c87431290e..ce28cac076 100644 --- a/src/apis/scaffold/challenge.py +++ b/src/apis/scaffold/challenge.py @@ -10,10 +10,12 @@ from services.settings import logger, HCAPTCHA_DEMO_SITES, DIR_MODEL, DIR_CHALLENGE from services.utils import get_challenge_ctx -SAMPLE_SITE = HCAPTCHA_DEMO_SITES[0] - -def demo(silence: Optional[bool] = False, onnx_prefix: Optional[str] = None): +def demo( + silence: Optional[bool] = False, + onnx_prefix: Optional[str] = None, + sample_site: Optional[str] = HCAPTCHA_DEMO_SITES[0] +): """人机挑战演示 顶级接口""" logger.info("Starting demo project...") @@ -28,7 +30,7 @@ def demo(silence: Optional[bool] = False, onnx_prefix: Optional[str] = None): ctx = get_challenge_ctx(silence=silence) try: # 读取 hCaptcha challenge 测试站点 - ctx.get(SAMPLE_SITE) + ctx.get(sample_site) # 必要的等待时间 time.sleep(3) @@ -52,12 +54,23 @@ def demo(silence: Optional[bool] = False, onnx_prefix: Optional[str] = None): ctx.quit() +def demo_v2(silence: Optional[bool] = False, onnx_prefix: Optional[str] = None): + """ + 人机挑战演示 顶级接口 演示垂直河流 SKI 解法 + + :param silence: + :param onnx_prefix: + :return: + """ + demo(silence, onnx_prefix, sample_site=HCAPTCHA_DEMO_SITES[1]) + + @logger.catch() def test(): """检查挑战者驱动版本是否适配""" ctx = get_challenge_ctx(silence=True) try: - ctx.get(SAMPLE_SITE) + ctx.get(HCAPTCHA_DEMO_SITES[0]) finally: ctx.quit() diff --git a/src/services/hcaptcha_challenger/__init__.py b/src/services/hcaptcha_challenger/__init__.py index a6eb12935c..dcc25ab956 100644 --- a/src/services/hcaptcha_challenger/__init__.py +++ b/src/services/hcaptcha_challenger/__init__.py @@ -3,8 +3,8 @@ # Author : QIN2DIM # Github : https://github.com/QIN2DIM # Description: -from .core import YOLO, ArmorCaptcha, ArmorUtils - +from .core import ArmorCaptcha, ArmorUtils +from .solutions.yolo import YOLO __all__ = [ "YOLO", "ArmorCaptcha", diff --git a/src/services/hcaptcha_challenger/core.py b/src/services/hcaptcha_challenger/core.py index 0cbd4d8a4d..49b2f3af72 100644 --- a/src/services/hcaptcha_challenger/core.py +++ b/src/services/hcaptcha_challenger/core.py @@ -3,10 +3,6 @@ import re import time import urllib.request -from typing import Optional - -import cv2 -import numpy as np from loguru import logger from selenium.common.exceptions import ( ElementNotVisibleException, @@ -18,9 +14,11 @@ from selenium.webdriver.common.by import By from selenium.webdriver.support import expected_conditions as EC from selenium.webdriver.support.wait import WebDriverWait +from typing import Optional from undetected_chromedriver import Chrome from services.utils import AshFramework +from .solutions import ski_river from .exceptions import ( LabelNotFoundException, ChallengeReset, @@ -29,172 +27,6 @@ ) -class YOLO: - """YOLO model for image classification""" - - def __init__(self, dir_model, onnx_prefix: str = "yolov5s6"): - self.dir_model = "./model" if dir_model is None else dir_model - self.onnx_prefix = ( - "yolov5s6" - if onnx_prefix not in ["yolov5m6", "yolov5s6", "yolov5n6"] - else onnx_prefix - ) - - self.onnx_model = { - "name": f"{self.onnx_prefix}(onnx)_model", - "path": os.path.join(self.dir_model, f"{self.onnx_prefix}.onnx"), - "src": f"https://github.com/QIN2DIM/hcaptcha-challenger/releases/download/model/{self.onnx_prefix}.onnx", - } - - # COCO namespace - self.classes = [ - "person", - "bicycle", - "car", - "motorbike", - "aeroplane", - "bus", - "train", - "truck", - "boat", - "traffic light", - "fire hydrant", - "stop sign", - "parking meter", - "bench", - "bird", - "cat", - "dog", - "horse", - "sheep", - "cow", - "elephant", - "bear", - "zebra", - "giraffe", - "backpack", - "umbrella", - "handbag", - "tie", - "suitcase", - "frisbee", - "skis", - "snowboard", - "sports ball", - "kite", - "baseball bat", - "baseball glove", - "skateboard", - "surfboard", - "tennis racket", - "bottle", - "wine glass", - "cup", - "fork", - "knife", - "spoon", - "bowl", - "banana", - "apple", - "sandwich", - "orange", - "broccoli", - "carrot", - "hot dog", - "pizza", - "donut", - "cake", - "chair", - "sofa", - "pottedplant", - "bed", - "diningtable", - "toilet", - "tvmonitor", - "laptop", - "mouse", - "remote", - "keyboard", - "cell phone", - "microwave", - "oven", - "toaster", - "sink", - "refrigerator", - "book", - "clock", - "vase", - "scissors", - "teddy bear", - "hair drier", - "toothbrush", - ] - - def download_model(self): - """Download model and weight parameters""" - if not os.path.exists(self.dir_model): - os.mkdir(self.dir_model) - if os.path.exists(self.onnx_model["path"]): - return - - print(f"Downloading {self.onnx_model['name']} from {self.onnx_model['src']}") - - urllib.request.urlretrieve(self.onnx_model["src"], self.onnx_model["path"]) - - def detect_common_objects(self, img_stream, confidence=0.4, nms_thresh=0.4): - """ - Object Detection - - Get multiple labels identified in a given image - - :param img_stream: image file binary stream - with open(img_filepath, "rb") as file: - data = file.read() - detect_common_objects(img_stream=data) - :param confidence: - :param nms_thresh: - :return: bbox, label, conf - """ - np_array = np.frombuffer(img_stream, np.uint8) - img = cv2.imdecode(np_array, flags=1) - height, width = img.shape[:2] - - blob = cv2.dnn.blobFromImage( - img, 1 / 255.0, (128, 128), (0, 0, 0), swapRB=True, crop=False - ) - self.download_model() - - net = cv2.dnn.readNetFromONNX(self.onnx_model["path"]) - - net.setInput(blob) - - class_ids = [] - confidences = [] - boxes = [] - - outs = net.forward() - - for out in outs: - for detection in out: - scores = detection[5:] - class_id = np.argmax(scores) - max_conf = scores[class_id] - if max_conf > confidence: - center_x = int(detection[0] * width) - center_y = int(detection[1] * height) - w = int(detection[2] * width) - h = int(detection[3] * height) - x = center_x - (w / 2) - y = center_y - (h / 2) - class_ids.append(class_id) - confidences.append(float(max_conf)) - boxes.append([x, y, w, h]) - - indices = cv2.dnn.NMSBoxes(boxes, confidences, confidence, nms_thresh) - - return [str(self.classes[class_ids[i]]) for i in indices] - - class ArmorCaptcha: """hCAPTCHA challenge drive control""" @@ -219,6 +51,7 @@ def __init__(self, dir_workspace: str = None, debug=False): "船": "boat", "汽车": "car", "摩托车": "motorbike", + "垂直河流": "vertical river" } # Store the `element locator` of challenge images {挑战图片1: locator1, ...} @@ -266,6 +99,14 @@ def tactical_retreat(self) -> bool: return True return False + def switch_solution(self,mirror, label: Optional[str] = None): + """模型卸载""" + label = self.label if label is None else label + + if label in ["垂直河流"]: + return ski_river.RiverChallenger() + return mirror + def mark_samples(self, ctx: Chrome): """ 获取每个挑战图片的下载链接以及网页元素位置 @@ -379,7 +220,7 @@ async def control_driver(self, context, session=None): self.runtime_workspace = workspace_ - def challenge(self, ctx: Chrome, model: YOLO, confidence=0.39, nms_thresh=0.7): + def challenge(self, ctx: Chrome, model): """ 图像分类,元素点击,答案提交 @@ -405,15 +246,13 @@ def challenge(self, ctx: Chrome, model: YOLO, confidence=0.39, nms_thresh=0.7): with open(img_filepath, "rb") as file: data = file.read() - t0 = time.time() # 获取识别结果 - labels = model.detect_common_objects( - data, confidence=confidence, nms_thresh=nms_thresh - ) + t0 = time.time() + result = model.solution(img_stream=data, label=self.label_alias[self.label]) ta.append(time.time() - t0) # 模型会根据置信度给出图片中的多个目标,只要命中一个就算通过 - if self.label_alias[self.label] in labels: + if result: # 选中标签元素 try: self.alias2locator[alias].click() @@ -432,7 +271,7 @@ def challenge(self, ctx: Chrome, model: YOLO, confidence=0.39, nms_thresh=0.7): except (TimeoutException, ElementClickInterceptedException): raise ChallengeTimeout("CPU 算力不足,无法在规定时间内完成挑战") - self.log(message=f"提交挑战 {model.onnx_model['name']}: {round(sum(ta), 2)}s") + self.log(message=f"提交挑战 {model.flag}: {round(sum(ta), 2)}s") def challenge_success(self, ctx: Chrome, init: bool = True): """ @@ -498,7 +337,7 @@ def _high_threat_proxy_access(): self.log("挑战成功") return True - def anti_hcaptcha(self, ctx: Chrome, model: YOLO): + def anti_hcaptcha(self, ctx: Chrome, model): """ Handle hcaptcha challenge @@ -543,6 +382,10 @@ def anti_hcaptcha(self, ctx: Chrome, model: YOLO): ctx.switch_to.default_content() return False + # [👻] 注册解决方案 + # 根据挑战类型自动匹配不同的模型 + model = self.switch_solution(mirror=model) + # [👻] 人机挑战! try: for index in range(2): diff --git a/src/services/hcaptcha_challenger/river_challenger.py b/src/services/hcaptcha_challenger/river_challenger.py deleted file mode 100644 index 96a21ce460..0000000000 --- a/src/services/hcaptcha_challenger/river_challenger.py +++ /dev/null @@ -1,131 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- -# @File : src\services\hcaptcha_challenger\river_challenger.py -# @Time : 2022-03-01 20:32:08 -# @Author : Bingjie Yan -# @Email : bj.yan.pa@qq.com -# @License : Apache License 2.0 - -import cv2 -import numpy as np - -import skimage -from skimage.morphology import disk -from skimage.segmentation import watershed, slic, mark_boundaries -from skimage.filters import rank -from skimage.util import img_as_ubyte -from skimage.color import rgb2gray, label2rgb -from skimage.future import graph -from scipy import ndimage as ndi -import matplotlib.pyplot as plt - - -def _weight_mean_color(graph, src, dst, n): - """Callback to handle merging nodes by recomputing mean color. - - The method expects that the mean color of `dst` is already computed. - - Parameters - ---------- - graph : RAG - The graph under consideration. - src, dst : int - The vertices in `graph` to be merged. - n : int - A neighbor of `src` or `dst` or both. - - Returns - ------- - data : dict - A dictionary with the `"weight"` attribute set as the absolute - difference of the mean color between node `dst` and `n`. - """ - - diff = graph.nodes[dst]['mean color'] - graph.nodes[n]['mean color'] - diff = np.linalg.norm(diff) - return {'weight': diff} - - -def merge_mean_color(graph, src, dst): - """Callback called before merging two nodes of a mean color distance graph. - - This method computes the mean color of `dst`. - - Parameters - ---------- - graph : RAG - The graph under consideration. - src, dst : int - The vertices in `graph` to be merged. - """ - graph.nodes[dst]['total color'] += graph.nodes[src]['total color'] - graph.nodes[dst]['pixel count'] += graph.nodes[src]['pixel count'] - graph.nodes[dst]['mean color'] = (graph.nodes[dst]['total color'] / - graph.nodes[dst]['pixel count']) - - -class RiverChallenger(object): - def __init__(self) -> None: - pass - - def challenge(self, img_stream): - img_arr = np.frombuffer(img_stream, np.uint8) - img = cv2.imdecode(img_arr, flags=1) - height, width = img.shape[:2] - - # # filter - img = cv2.pyrMeanShiftFiltering(img, sp=10, sr=40) - img = cv2.bilateralFilter(img, d=9, sigmaColor=100, sigmaSpace=75) - - # # enhance brightness - # img_hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV) - # img_hsv[:, :, 2] = img_hsv[:, :, 2] * 1.2 - # img = cv2.cvtColor(img_hsv, cv2.COLOR_HSV2BGR) - - labels = slic(img, compactness=30, n_segments=400, start_label=1) - g = graph.rag_mean_color(img, labels) - - labels2 = graph.merge_hierarchical(labels, - g, - thresh=35, - rag_copy=False, - in_place_merge=True, - merge_func=merge_mean_color, - weight_func=_weight_mean_color) - - # view results - # out = label2rgb(labels2, img, kind='avg', bg_label=0) - # out = mark_boundaries(out, labels2, (0, 0, 0)) - # skimage.io.imshow(out) - # skimage.io.show() - # print(np.unique(labels2[-1])) - - ref_value = len(np.unique(labels2[-1])) - return ref_value >= 3 - - -if __name__ == '__main__': - import os - import sys - sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) - - result_path = 'result.txt' - if os.path.exists(result_path): - os.remove(result_path) - - result_file = open(result_path, 'w') - # result_file = sys.stdout - - base_path = os.path.join('database', '_challenge') - list_dirs = os.listdir(base_path) - for dir in list_dirs: - print(dir, file=result_file) - for i in range(1, 10): - img_filepath = os.path.join(base_path, dir, f'挑战图片{i}.png') - - with open(img_filepath, "rb") as file: - data = file.read() - - rc = RiverChallenger() - result = rc.challenge(data) - print(f'挑战图片{i}.png:{result}', file=result_file) \ No newline at end of file diff --git a/src/services/hcaptcha_challenger/solutions/__init__.py b/src/services/hcaptcha_challenger/solutions/__init__.py new file mode 100644 index 0000000000..71a2eebdfc --- /dev/null +++ b/src/services/hcaptcha_challenger/solutions/__init__.py @@ -0,0 +1,5 @@ +# -*- coding: utf-8 -*- +# Time : 2022/3/2 0:52 +# Author : QIN2DIM +# Github : https://github.com/QIN2DIM +# Description: diff --git a/src/services/hcaptcha_challenger/solutions/ski_river.py b/src/services/hcaptcha_challenger/solutions/ski_river.py new file mode 100644 index 0000000000..ea63ed2b1e --- /dev/null +++ b/src/services/hcaptcha_challenger/solutions/ski_river.py @@ -0,0 +1,84 @@ +# -*- coding: utf-8 -*- +# Time : 2022-03-01 20:32 +# Author : Bingjie Yan +# Github : https://github.com/beiyuouo +# Description: +import cv2 +import numpy as np +from skimage.future import graph +from skimage.segmentation import slic + + +class RiverChallenger: + """A fast solution for identifying vertical rivers""" + + def __init__(self): + self.flag = "skimage_model" + + @staticmethod + def _weight_mean_color(graph_, src: int, dst: int, n: int): # noqa + """Callback to handle merging nodes by recomputing mean color. + + The method expects that the mean color of `dst` is already computed. + + Parameters + ---------- + graph_ : RAG + The graph under consideration. + src, dst : int + The vertices in `graph` to be merged. + n : int + A neighbor of `src` or `dst` or both. + + Returns + ------- + data : dict + A dictionary with the `"weight"` attribute set as the absolute + difference of the mean color between node `dst` and `n`. + """ + + diff = graph_.nodes[dst]["mean color"] - graph_.nodes[n]["mean color"] + diff = np.linalg.norm(diff) + return {"weight": diff} + + @staticmethod + def _merge_mean_color(graph_, src, dst): + """Callback called before merging two nodes of a mean color distance graph. + + This method computes the mean color of `dst`. + + Parameters + ---------- + graph_ : RAG + The graph under consideration. + src, dst : int + The vertices in `graph` to be merged. + """ + graph_.nodes[dst]["total color"] += graph_.nodes[src]["total color"] + graph_.nodes[dst]["pixel count"] += graph_.nodes[src]["pixel count"] + graph_.nodes[dst]["mean color"] = ( + graph_.nodes[dst]["total color"] / graph_.nodes[dst]["pixel count"] + ) + + def solution(self, img_stream, **kwargs) -> bool: # noqa + """Implementation process of solution""" + img_arr = np.frombuffer(img_stream, np.uint8) + img = cv2.imdecode(img_arr, flags=1) + + img = cv2.pyrMeanShiftFiltering(img, sp=10, sr=40) + img = cv2.bilateralFilter(img, d=9, sigmaColor=100, sigmaSpace=75) + + labels = slic(img, compactness=30, n_segments=400, start_label=1) + g = graph.rag_mean_color(img, labels) + + labels2 = graph.merge_hierarchical( + labels, + g, + thresh=35, + rag_copy=False, + in_place_merge=True, + merge_func=self._merge_mean_color, + weight_func=self._weight_mean_color, + ) + + return len(np.unique(labels2[-1])) >= 3 diff --git a/src/services/hcaptcha_challenger/solutions/yolo.py b/src/services/hcaptcha_challenger/solutions/yolo.py new file mode 100644 index 0000000000..dbbf2de24f --- /dev/null +++ b/src/services/hcaptcha_challenger/solutions/yolo.py @@ -0,0 +1,184 @@ +# -*- coding: utf-8 -*- +# Time : 2022/3/2 0:52 +# Author : QIN2DIM +# Github : https://github.com/QIN2DIM +# Description: +import cv2 +import numpy as np +import os +import urllib.request + + +class YOLO: + """YOLO model for image classification""" + + def __init__(self, dir_model, onnx_prefix: str = "yolov5s6"): + self.dir_model = "./model" if dir_model is None else dir_model + self.onnx_prefix = ( + "yolov5s6" + if onnx_prefix not in ["yolov5m6", "yolov5s6", "yolov5n6"] + else onnx_prefix + ) + + self.onnx_model = { + "name": f"{self.onnx_prefix}(onnx)_model", + "path": os.path.join(self.dir_model, f"{self.onnx_prefix}.onnx"), + "src": f"https://github.com/QIN2DIM/hcaptcha-challenger/releases/download/model/{self.onnx_prefix}.onnx", + } + + self.flag = self.onnx_model["name"] + + # COCO namespace + self.classes = [ + "person", + "bicycle", + "car", + "motorbike", + "aeroplane", + "bus", + "train", + "truck", + "boat", + "traffic light", + "fire hydrant", + "stop sign", + "parking meter", + "bench", + "bird", + "cat", + "dog", + "horse", + "sheep", + "cow", + "elephant", + "bear", + "zebra", + "giraffe", + "backpack", + "umbrella", + "handbag", + "tie", + "suitcase", + "frisbee", + "skis", + "snowboard", + "sports ball", + "kite", + "baseball bat", + "baseball glove", + "skateboard", + "surfboard", + "tennis racket", + "bottle", + "wine glass", + "cup", + "fork", + "knife", + "spoon", + "bowl", + "banana", + "apple", + "sandwich", + "orange", + "broccoli", + "carrot", + "hot dog", + "pizza", + "donut", + "cake", + "chair", + "sofa", + "pottedplant", + "bed", + "diningtable", + "toilet", + "tvmonitor", + "laptop", + "mouse", + "remote", + "keyboard", + "cell phone", + "microwave", + "oven", + "toaster", + "sink", + "refrigerator", + "book", + "clock", + "vase", + "scissors", + "teddy bear", + "hair drier", + "toothbrush", + ] + + def download_model(self): + """Download model and weight parameters""" + if not os.path.exists(self.dir_model): + os.mkdir(self.dir_model) + if os.path.exists(self.onnx_model["path"]): + return + + print(f"Downloading {self.onnx_model['name']} from {self.onnx_model['src']}") + + urllib.request.urlretrieve(self.onnx_model["src"], self.onnx_model["path"]) + + def detect_common_objects(self, img_stream, confidence=0.4, nms_thresh=0.4): + """ + Object Detection + + Get multiple labels identified in a given image + + :param img_stream: image file binary stream + with open(img_filepath, "rb") as file: + data = file.read() + detect_common_objects(img_stream=data) + :param confidence: + :param nms_thresh: + :return: bbox, label, conf + """ + np_array = np.frombuffer(img_stream, np.uint8) + img = cv2.imdecode(np_array, flags=1) + height, width = img.shape[:2] + + blob = cv2.dnn.blobFromImage( + img, 1 / 255.0, (128, 128), (0, 0, 0), swapRB=True, crop=False + ) + self.download_model() + + net = cv2.dnn.readNetFromONNX(self.onnx_model["path"]) + + net.setInput(blob) + + class_ids = [] + confidences = [] + boxes = [] + + outs = net.forward() + + for out in outs: + for detection in out: + scores = detection[5:] + class_id = np.argmax(scores) + max_conf = scores[class_id] + if max_conf > confidence: + center_x = int(detection[0] * width) + center_y = int(detection[1] * height) + w = int(detection[2] * width) + h = int(detection[3] * height) + x = center_x - (w / 2) + y = center_y - (h / 2) + class_ids.append(class_id) + confidences.append(float(max_conf)) + boxes.append([x, y, w, h]) + + indices = cv2.dnn.NMSBoxes(boxes, confidences, confidence, nms_thresh) + + return [str(self.classes[class_ids[i]]) for i in indices] + + def solution(self, img_stream: bytes, label: str, **kwargs) -> bool: + """Implementation process of solution""" + confidence = kwargs.get("confidence", 0.4) + nms_thresh = kwargs.get("nms_thresh", 0.4) + labels = self.detect_common_objects(img_stream, confidence, nms_thresh) + return bool(label in labels) diff --git a/src/services/scaffold.py b/src/services/scaffold.py index 913f50bb15..183149a5b3 100644 --- a/src/services/scaffold.py +++ b/src/services/scaffold.py @@ -23,5 +23,10 @@ def test(): @staticmethod def demo(silence: Optional[bool] = False, model: Optional[str] = None): - """Dueling with hCaptcha challenge""" + """Dueling with hCAPTCHA challenge using YOLOv5""" challenge.demo(silence=silence, onnx_prefix=model) + + @staticmethod + def demo_v2(silence: Optional[bool] = False, model: Optional[str] = None): + """Processing hCAPTCHA challenges using Image-Segmentation""" + challenge.demo_v2(silence=silence,onnx_prefix=model) diff --git a/src/services/settings.py b/src/services/settings.py index 86c3496b1e..01fdc7cd6a 100644 --- a/src/services/settings.py +++ b/src/services/settings.py @@ -9,7 +9,8 @@ from services.utils import ToolBox HCAPTCHA_DEMO_SITES = [ - "https://maximedrn.github.io/hcaptcha-solver-python-selenium/" + "https://maximedrn.github.io/hcaptcha-solver-python-selenium/", + "https://signup.cloud.oracle.com", ] # --------------------------------------------------- # [√]Lock the project directory From 4f7d008d28e20efdeecd2ee4a17d35f212bde39f Mon Sep 17 00:00:00 2001 From: QIN2DIM <62018067+QIN2DIM@users.noreply.github.com> Date: Wed, 2 Mar 2022 03:32:09 +0800 Subject: [PATCH 3/3] Update requirements.txt --- requirements.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/requirements.txt b/requirements.txt index 39b9ebb920..16a068ccc1 100644 --- a/requirements.txt +++ b/requirements.txt @@ -3,7 +3,7 @@ loguru~=0.6.0 selenium~=4.1.0 aiohttp~=3.8.1 opencv-python~=4.5.5.62 -numpy~=1.22.2 undetected_chromedriver==3.1.3 webdriver-manager>=3.5.2 -scikit-image~=0.19.2 \ No newline at end of file +scikit-image~=0.19.2 +numpy>=1.21.5 \ No newline at end of file