From 84d168e3f0020b7ce44aaa46d3e4336721a1b8fc Mon Sep 17 00:00:00 2001 From: narugo1992 Date: Tue, 26 Sep 2023 12:31:12 +0800 Subject: [PATCH 1/5] dev(narugo): add eyes detections --- docs/source/api_doc/detect/eye.rst | 14 ++++ .../detect/eye_detect_benchmark.plot.py | 37 +++++++++ .../api_doc/detect/eye_detect_demo.plot.py | 19 +++++ docs/source/api_doc/detect/index.rst | 1 + imgutils/detect/__init__.py | 1 + imgutils/detect/eye.py | 76 +++++++++++++++++++ requirements-doc.txt | 2 +- test/detect/test_censor.py | 2 +- test/detect/test_eye.py | 32 ++++++++ test/detect/test_halfbody.py | 2 +- 10 files changed, 183 insertions(+), 3 deletions(-) create mode 100644 docs/source/api_doc/detect/eye.rst create mode 100644 docs/source/api_doc/detect/eye_detect_benchmark.plot.py create mode 100644 docs/source/api_doc/detect/eye_detect_demo.plot.py create mode 100644 imgutils/detect/eye.py create mode 100644 test/detect/test_eye.py diff --git a/docs/source/api_doc/detect/eye.rst b/docs/source/api_doc/detect/eye.rst new file mode 100644 index 00000000000..98f39be8362 --- /dev/null +++ b/docs/source/api_doc/detect/eye.rst @@ -0,0 +1,14 @@ +imgutils.detect.eye +========================== + +.. currentmodule:: imgutils.detect.eye + +.. automodule:: imgutils.detect.eye + + +detect_eyes +------------------------------ + +.. autofunction:: detect_eyes + + diff --git a/docs/source/api_doc/detect/eye_detect_benchmark.plot.py b/docs/source/api_doc/detect/eye_detect_benchmark.plot.py new file mode 100644 index 00000000000..f4c31266ad7 --- /dev/null +++ b/docs/source/api_doc/detect/eye_detect_benchmark.plot.py @@ -0,0 +1,37 @@ +import random + +from benchmark import BaseBenchmark, create_plot_cli +from imgutils.detect import detect_eyes + + +class EyeDetectBenchmark(BaseBenchmark): + def __init__(self, level, version): + BaseBenchmark.__init__(self) + self.level = level + self.version = version + + def load(self): + from imgutils.detect.eye import _open_eye_detect_model + _ = _open_eye_detect_model(level=self.level, version=self.version) + + def unload(self): + from imgutils.detect.eye import _open_eye_detect_model + _open_eye_detect_model.cache_clear() + + def run(self): + image_file = random.choice(self.all_images) + _ = detect_eyes(image_file, level=self.level, version=self.version) + + +if __name__ == '__main__': + create_plot_cli( + [ + ('eye v1.0 (yolov8s)', EyeDetectBenchmark('s', 'v1.0')), + ('eye v1.0 (yolov8n)', EyeDetectBenchmark('n', 'v1.0')), + ('eye v0.8 (yolov8s)', EyeDetectBenchmark('s', 'v0.8')), + ('eye v0.7 (yolov8s)', EyeDetectBenchmark('s', 'v0.7')), + ], + title='Benchmark for Anime Eyes Detections', + run_times=10, + try_times=20, + )() diff --git a/docs/source/api_doc/detect/eye_detect_demo.plot.py b/docs/source/api_doc/detect/eye_detect_demo.plot.py new file mode 100644 index 00000000000..643d2007527 --- /dev/null +++ b/docs/source/api_doc/detect/eye_detect_demo.plot.py @@ -0,0 +1,19 @@ +from imgutils.detect import detect_eyes +from imgutils.detect.eye import _LABELS +from imgutils.detect.visual import detection_visualize +from plot import image_plot + + +def _detect(img, **kwargs): + return detection_visualize(img, detect_eyes(img, **kwargs), _LABELS) + + +if __name__ == '__main__': + image_plot( + (_detect('nian.png'), 'large scale'), + (_detect('two_bikini_girls.png'), 'closed heads'), + (_detect('genshin_post.jpg'), 'multiple'), + (_detect('mostima_post.jpg'), 'anime style'), + columns=2, + figsize=(12, 9), + ) diff --git a/docs/source/api_doc/detect/index.rst b/docs/source/api_doc/detect/index.rst index 4e8c53e77c5..d681957a459 100644 --- a/docs/source/api_doc/detect/index.rst +++ b/docs/source/api_doc/detect/index.rst @@ -10,6 +10,7 @@ imgutils.detect :maxdepth: 3 censor + eye face halfbody hand diff --git a/imgutils/detect/__init__.py b/imgutils/detect/__init__.py index fe37f41116b..09973dedfaf 100644 --- a/imgutils/detect/__init__.py +++ b/imgutils/detect/__init__.py @@ -9,6 +9,7 @@ :align: center """ from .censor import detect_censors +from .eye import detect_eyes from .face import detect_faces from .halfbody import detect_halfbody from .hand import detect_hands diff --git a/imgutils/detect/eye.py b/imgutils/detect/eye.py new file mode 100644 index 00000000000..c00771f461a --- /dev/null +++ b/imgutils/detect/eye.py @@ -0,0 +1,76 @@ +""" +Overview: + Detect eyes in anime images. + + Trained on dataset `deepghs/anime_eye_detection `_ with YOLOv8. + + .. image:: eye_detect_demo.plot.py.svg + :align: center + + This is an overall benchmark of all the eye detect models: + + .. image:: eye_detect_benchmark.plot.py.svg + :align: center + +""" +from functools import lru_cache +from typing import List, Tuple + +from huggingface_hub import hf_hub_download + +from ._yolo import _image_preprocess, _data_postprocess +from ..data import ImageTyping, load_image, rgb_encode +from ..utils import open_onnx_model + + +@lru_cache() +def _open_eye_detect_model(level: str = 's', version: str = 'v1.0'): + return open_onnx_model(hf_hub_download( + f'deepghs/anime_eye_detection', + f'eye_detect_{version}_{level}/model.onnx' + )) + + +_LABELS = ["eye"] + + +def detect_eyes(image: ImageTyping, level: str = 's', version: str = 'v1.0', max_infer_size=640, + conf_threshold: float = 0.3, iou_threshold: float = 0.3) \ + -> List[Tuple[Tuple[int, int, int, int], str, float]]: + """ + Overview: + Detect human eyes in anime images. + + :param image: Image to detect. + :param level: The model level being used can be either `s` or `n`. + The `n` model runs faster with smaller system overhead, while the `s` model achieves higher accuracy. + The default value is `s`. + :param version: Version of model, default is ``v1.0``. + :param max_infer_size: The maximum image size used for model inference, if the image size exceeds this limit, + the image will be resized and used for inference. The default value is `640` pixels. + :param conf_threshold: The confidence threshold, only detection results with confidence scores above + this threshold will be returned. The default value is `0.3`. + :param iou_threshold: The detection area coverage overlap threshold, areas with overlaps above this threshold + will be discarded. The default value is `0.3`. + :return: The detection results list, each item includes the detected area `(x0, y0, x1, y1)`, + the target type (always `eye`) and the target confidence score. + + Examples:: + >>> from imgutils.detect import detect_eyes, detection_visualize + >>> + >>> image = 'squat.jpg' + >>> result = detect_eyes(image) # detect it + >>> result + [((127, 21, 629, 637), 'eye', 0.9040350914001465)] + >>> + >>> # visualize it + >>> from matplotlib import pyplot as plt + >>> plt.imshow(detection_visualize(image, result)) + >>> plt.show() + """ + image = load_image(image, mode='RGB') + new_image, old_size, new_size = _image_preprocess(image, max_infer_size) + + data = rgb_encode(new_image)[None, ...] + output, = _open_eye_detect_model(level, version).run(['output0'], {'images': data}) + return _data_postprocess(output[0], conf_threshold, iou_threshold, old_size, new_size, _LABELS) diff --git a/requirements-doc.txt b/requirements-doc.txt index 38f526368de..c48f1aaacb3 100644 --- a/requirements-doc.txt +++ b/requirements-doc.txt @@ -1,7 +1,7 @@ Jinja2~=3.0.0 sphinx~=3.2.0 sphinx_rtd_theme~=0.4.3 -enum_tools +enum_tools~=0.9.0 sphinx-toolbox plantumlcli>=0.0.2 packaging diff --git a/test/detect/test_censor.py b/test/detect/test_censor.py index d711c160ccb..5025ea15e10 100644 --- a/test/detect/test_censor.py +++ b/test/detect/test_censor.py @@ -13,7 +13,7 @@ def _release_model_after_run(): @pytest.mark.unittest -class TestDetectHead: +class TestDetectCensor: def test_detect_censors(self): detections = detect_censors(get_testfile('nude_girl.png')) assert len(detections) == 3 diff --git a/test/detect/test_eye.py b/test/detect/test_eye.py new file mode 100644 index 00000000000..85c88dd7424 --- /dev/null +++ b/test/detect/test_eye.py @@ -0,0 +1,32 @@ +import pytest + +from imgutils.detect.eye import _open_eye_detect_model, detect_eyes +from test.testings import get_testfile + + +@pytest.fixture(scope='module', autouse=True) +def _release_model_after_run(): + try: + yield + finally: + _open_eye_detect_model.cache_clear() + + +@pytest.mark.unittest +class TestDetectEyes: + def test_detect_eye(self): + detections = detect_eyes(get_testfile('nude_girl.png')) + assert len(detections) == 2 + + values = [] + for bbox, label, score in detections: + assert label in {'eye'} + values.append((bbox, int(score * 1000) / 1000)) + + assert values == pytest.approx([ + ((350, 160, 382, 173), 0.788), + ((294, 170, 319, 181), 0.756), + ]) + + def test_detect_eye_none(self): + assert detect_eyes(get_testfile('png_full.png')) == [] diff --git a/test/detect/test_halfbody.py b/test/detect/test_halfbody.py index e10091ebd10..2371b80e0eb 100644 --- a/test/detect/test_halfbody.py +++ b/test/detect/test_halfbody.py @@ -13,7 +13,7 @@ def _release_model_after_run(): @pytest.mark.unittest -class TestDetectHead: +class TestDetectHalfBody: def test_detect_halfbody(self): detections = detect_halfbody(get_testfile('nude_girl.png')) assert len(detections) == 1 From d69e5d04f6303a12bab0a21293912a55ecb7eb14 Mon Sep 17 00:00:00 2001 From: narugo1992 Date: Tue, 26 Sep 2023 04:37:41 +0000 Subject: [PATCH 2/5] dev(narugo): auto sync Tue, 26 Sep 2023 04:37:41 +0000 --- .../detect/eye_detect_benchmark.plot.py.svg | 2546 +++++++++++++++++ .../detect/eye_detect_demo.plot.py.svg | 524 ++++ 2 files changed, 3070 insertions(+) create mode 100644 docs/source/api_doc/detect/eye_detect_benchmark.plot.py.svg create mode 100644 docs/source/api_doc/detect/eye_detect_demo.plot.py.svg diff --git a/docs/source/api_doc/detect/eye_detect_benchmark.plot.py.svg b/docs/source/api_doc/detect/eye_detect_benchmark.plot.py.svg new file mode 100644 index 00000000000..1679499c798 --- /dev/null +++ b/docs/source/api_doc/detect/eye_detect_benchmark.plot.py.svg @@ -0,0 +1,2546 @@ + + + + + + + + 2023-09-26T04:37:15.815012 + image/svg+xml + + + Matplotlib v3.7.3, https://matplotlib.org/ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/docs/source/api_doc/detect/eye_detect_demo.plot.py.svg b/docs/source/api_doc/detect/eye_detect_demo.plot.py.svg new file mode 100644 index 00000000000..880f27cba64 --- /dev/null +++ b/docs/source/api_doc/detect/eye_detect_demo.plot.py.svg @@ -0,0 +1,524 @@ + + + + + + + + 2023-09-26T04:37:20.952582 + image/svg+xml + + + Matplotlib v3.7.3, https://matplotlib.org/ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + From ca6c9381c5172731a1ba9011f8473e22068a462c Mon Sep 17 00:00:00 2001 From: narugo1992 Date: Tue, 26 Sep 2023 12:40:21 +0800 Subject: [PATCH 3/5] dev(narugo): use another image --- .../api_doc/detect/eye_detect_demo.plot.py | 6 +- .../detect/eye_detect_demo.plot.py.svg | 524 ------------------ 2 files changed, 3 insertions(+), 527 deletions(-) delete mode 100644 docs/source/api_doc/detect/eye_detect_demo.plot.py.svg diff --git a/docs/source/api_doc/detect/eye_detect_demo.plot.py b/docs/source/api_doc/detect/eye_detect_demo.plot.py index 643d2007527..675eac261cf 100644 --- a/docs/source/api_doc/detect/eye_detect_demo.plot.py +++ b/docs/source/api_doc/detect/eye_detect_demo.plot.py @@ -12,8 +12,8 @@ def _detect(img, **kwargs): image_plot( (_detect('nian.png'), 'large scale'), (_detect('two_bikini_girls.png'), 'closed heads'), - (_detect('genshin_post.jpg'), 'multiple'), - (_detect('mostima_post.jpg'), 'anime style'), + (_detect('halfbody/squat.jpg'), 'pose'), + (_detect('mostima_post.jpg'), 'multiple'), columns=2, - figsize=(12, 9), + figsize=(10, 9), ) diff --git a/docs/source/api_doc/detect/eye_detect_demo.plot.py.svg b/docs/source/api_doc/detect/eye_detect_demo.plot.py.svg deleted file mode 100644 index 880f27cba64..00000000000 --- a/docs/source/api_doc/detect/eye_detect_demo.plot.py.svg +++ /dev/null @@ -1,524 +0,0 @@ - - - - - - - - 2023-09-26T04:37:20.952582 - image/svg+xml - - - Matplotlib v3.7.3, https://matplotlib.org/ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - From 2a178fe7a2a8651d03ef546c72add48262f6eb21 Mon Sep 17 00:00:00 2001 From: narugo1992 Date: Tue, 26 Sep 2023 04:52:14 +0000 Subject: [PATCH 4/5] dev(narugo): auto sync Tue, 26 Sep 2023 04:52:14 +0000 --- .../detect/eye_detect_demo.plot.py.svg | 481 ++++++++++++++++++ 1 file changed, 481 insertions(+) create mode 100644 docs/source/api_doc/detect/eye_detect_demo.plot.py.svg diff --git a/docs/source/api_doc/detect/eye_detect_demo.plot.py.svg b/docs/source/api_doc/detect/eye_detect_demo.plot.py.svg new file mode 100644 index 00000000000..67ec6b3cf6e --- /dev/null +++ b/docs/source/api_doc/detect/eye_detect_demo.plot.py.svg @@ -0,0 +1,481 @@ + + + + + + + + 2023-09-26T04:51:53.668506 + image/svg+xml + + + Matplotlib v3.7.3, https://matplotlib.org/ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + From 49d74314f97bfe64e559cf1680ef5f39f6b956d9 Mon Sep 17 00:00:00 2001 From: narugo1992 Date: Tue, 26 Sep 2023 13:58:01 +0800 Subject: [PATCH 5/5] dev(narugo): fix examples --- imgutils/detect/eye.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/imgutils/detect/eye.py b/imgutils/detect/eye.py index c00771f461a..ace44fc87b0 100644 --- a/imgutils/detect/eye.py +++ b/imgutils/detect/eye.py @@ -61,7 +61,7 @@ def detect_eyes(image: ImageTyping, level: str = 's', version: str = 'v1.0', max >>> image = 'squat.jpg' >>> result = detect_eyes(image) # detect it >>> result - [((127, 21, 629, 637), 'eye', 0.9040350914001465)] + [((297, 239, 341, 271), 'eye', 0.7760562896728516), ((230, 289, 263, 308), 'eye', 0.7682342529296875)] >>> >>> # visualize it >>> from matplotlib import pyplot as plt