File size: 4,977 Bytes
96f110e
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
#!/usr/bin/env python3
"""
์ „์ฒ˜๋ฆฌ ํŒŒ์ดํ”„๋ผ์ธ โ€” mode์— ๋”ฐ๋ฅธ ์ฒ˜๋ฆฌ ์ˆœ์„œ. ROS ๋น„์˜์กด.

๋…ธ๋“œ ์•ˆ์— if๋ฌธ์œผ๋กœ ํฉ์–ด๋†“์œผ๋ฉด rclpy ์—†์ด๋Š” "mode๋ฅผ ๋ฐ”๊ฟจ์„ ๋•Œ ์ •๋ง ๋‹ค๋ฅธ
์ฒ˜๋ฆฌ๊ฐ€ ๋„๋Š”๊ฐ€"๋ฅผ ๊ฒ€์ฆํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค. ์—ฌ๊ธฐ๋กœ ๋นผ์„œ ๋…ธ๋“œยท์˜คํ”„๋ผ์ธ ๋„๊ตฌยทํ…Œ์ŠคํŠธ๊ฐ€
**๊ฐ™์€ ์ฝ”๋“œ**๋ฅผ ์“ฐ๊ฒŒ ํ–ˆ์Šต๋‹ˆ๋‹ค.

  passthrough : ๋ฌด์ฒ˜๋ฆฌ          (1๋‹จ๊ณ„ ๋ผˆ๋Œ€ โ€” QoSยทํ—ค๋” ๋ฐฐ์„ ๋งŒ ๊ฒ€์ฆ)
  clahe       : ๊ฐ๋งˆ -> CLAHE   (2๋‹จ๊ณ„)
  dehaze      : ๊ฐ๋งˆ -> DCP     (๊ธฐ์—ฌ๋„ ๋ถ„๋ฆฌ์šฉ)
  full        : ๊ฐ๋งˆ -> DCP -> CLAHE       (3๋‹จ๊ณ„)
  aod         : ๊ฐ๋งˆ -> AOD-Net (DCP ๋Œ€์•ˆ)
  aod_full    : ๊ฐ๋งˆ -> AOD-Net -> CLAHE

AOD-Net์„ **๋ณ„๋„ ๋…ธ๋“œ๊ฐ€ ์•„๋‹ˆ๋ผ mode๋กœ** ๋„ฃ์€ ์ด์œ ๋Š” HANDOVER 4-9. ๋น„๊ต ๋„๊ตฌยท
์ •๋‹ต ์ฑ„์ ยท๋ถˆ์”จ ์ƒ์กด ํ…Œ์ŠคํŠธยท๋ฐ์ดํ„ฐ์…‹ ์ƒ์„ฑ๊ธฐยท๋…ธ๋“œ ๋ฐฐ์„ ์ด ์ „๋ถ€ ๊ทธ๋Œ€๋กœ ์žฌ์‚ฌ์šฉ๋˜๊ณ ,
A/B๊ฐ€ ํŒŒ๋ผ๋ฏธํ„ฐ ํ•œ ์ค„์ด ๋ฉ๋‹ˆ๋‹ค.

์ˆœ์„œ๊ฐ€ '๋””ํ—ค์ด์ฆˆ -> CLAHE'์ธ ์ด์œ : ๋””ํ—ค์ด์ฆˆ๋Š” ๋ฌผ๋ฆฌ ๋ชจ๋ธ์˜ ์—ญ์‚ฐ์ด๋ผ
**์ž…๋ ฅ์ด ์›๋ณธ ๊ด€์ธก๊ฐ’ I์—ฌ์•ผ** ์„ฑ๋ฆฝํ•ฉ๋‹ˆ๋‹ค. CLAHE๋ฅผ ๋จผ์ € ๊ฑธ๋ฉด ํžˆ์Šคํ† ๊ทธ๋žจ์ด
๋น„์„ ํ˜•์œผ๋กœ ๋ณ€ํ˜•๋ผ I = Jยทt + A(1-t) ๊ฐ€์ •์ด ๊นจ์ง‘๋‹ˆ๋‹ค.
"""

from __future__ import annotations

import time

from .dehaze import ClaheEnhancer, DarkChannelDehazer, apply_gamma

MODES = ("passthrough", "clahe", "dehaze", "full", "aod", "aod_full")

# ๋””ํ—ค์ด์ฆˆ๊ฐ€ ๋„๋Š” ๋ชจ๋“œ์™€, ๊ทธ์ค‘ AOD-Net์„ ์“ฐ๋Š” ๋ชจ๋“œ.
DEHAZE_MODES = ("dehaze", "full", "aod", "aod_full")
AOD_MODES = ("aod", "aod_full")
CLAHE_MODES = ("clahe", "full", "aod_full")


class Pipeline:
    def __init__(
        self,
        mode: str = "full",
        gamma: float = 1.0,
        lowlight: bool = False,
        clahe: ClaheEnhancer | None = None,
        dehazer: DarkChannelDehazer | None = None,
        aod=None,
    ):
        self.set_mode(mode)
        self.gamma = gamma
        self.lowlight = lowlight
        self.clahe = clahe or ClaheEnhancer()
        self.dehazer = dehazer or DarkChannelDehazer()
        # AOD-Net ๋””ํ—ค์ด์ €(`aodnet.GatedDehazer` ๋“ฑ). `process(bgr)->bgr` ์ธํ„ฐํŽ˜์ด์Šค๋งŒ
        # ๋งž์œผ๋ฉด ๋ฉ๋‹ˆ๋‹ค. ์—†์œผ๋ฉด aod* ๋ชจ๋“œ๋ฅผ ์“ธ ๋•Œ ์˜ˆ์™ธ โ€” **DCP๋กœ ์กฐ์šฉํžˆ ๋Œ€์ฒดํ•˜์ง€
        # ์•Š์Šต๋‹ˆ๋‹ค.** ๋Œ€์ฒดํ•˜๋ฉด "aod ๋ชจ๋“œ๋กœ ์ธก์ •ํ•œ ๊ฒฐ๊ณผ"๊ฐ€ ์‹ค์ œ๋กœ๋Š” DCP ๊ฒฐ๊ณผ๊ฐ€ ๋˜์–ด
        # A/B ์‹คํ—˜์ด ํ†ต์งธ๋กœ ๋ฌด์˜๋ฏธํ•ด์ง‘๋‹ˆ๋‹ค.
        self.aod = aod

        # ๋งˆ์ง€๋ง‰ ํ”„๋ ˆ์ž„์˜ ๋‹จ๊ณ„๋ณ„ ์†Œ์š”์‹œ๊ฐ„(ms). ๋ณ‘๋ชฉ ์ถ”์ ์šฉ.
        self.timings: dict[str, float] = {"dehaze": 0.0, "clahe": 0.0, "total": 0.0}

    def set_mode(self, mode: str) -> None:
        if mode not in MODES:
            raise ValueError(f"mode๋Š” {MODES} ์ค‘ ํ•˜๋‚˜์—ฌ์•ผ ํ•ฉ๋‹ˆ๋‹ค (๋ฐ›์€ ๊ฐ’: {mode!r})")
        self.mode = mode

    def active_dehazer(self):
        """ํ˜„์žฌ mode๊ฐ€ ์“ฐ๋Š” ๋””ํ—ค์ด์ €. aod* ๋ชจ๋“œ์ธ๋ฐ ์—†์œผ๋ฉด ์˜ˆ์™ธ."""
        if self.mode in AOD_MODES:
            if self.aod is None:
                raise ValueError(
                    f"mode={self.mode!r} ์ธ๋ฐ AOD-Net ๋””ํ—ค์ด์ €๊ฐ€ ์—†์Šต๋‹ˆ๋‹ค. "
                    "onnx ๊ฒฝ๋กœ๋ฅผ ์ฃผ๊ฑฐ๋‚˜ dehaze/full ๋ชจ๋“œ๋ฅผ ์“ฐ์„ธ์š”. "
                    "(DCP๋กœ ์ž๋™ ๋Œ€์ฒดํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค โ€” A/B ์‹คํ—˜์ด ์˜ค์—ผ๋ฉ๋‹ˆ๋‹ค)"
                )
            return self.aod
        return self.dehazer

    def process(self, bgr):
        """bgr uint8 -> bgr uint8. ์ž…๋ ฅ ๋ฐฐ์—ด์€ ๋ณ€๊ฒฝํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค."""
        t_start = time.perf_counter()
        self.timings = {"dehaze": 0.0, "clahe": 0.0, "total": 0.0}

        if self.mode == "passthrough":
            self.timings["total"] = (time.perf_counter() - t_start) * 1000.0
            return bgr

        img = apply_gamma(bgr, self.gamma)

        if self.mode in DEHAZE_MODES:
            dehazer = self.active_dehazer()
            t = time.perf_counter()
            img = dehazer.process(img)
            if self.lowlight:
                if not hasattr(dehazer, "process_lowlight"):
                    # HANDOVER 4-9: AOD-Net์—๋Š” ์ €์กฐ๋„ ๋Œ€์‘ ๊ฒฝ๋กœ๊ฐ€ ์—†์Šต๋‹ˆ๋‹ค.
                    # ์กฐ์šฉํžˆ ๊ฑด๋„ˆ๋›ฐ๋ฉด "์ €์กฐ๋„ ๋ณด์ •์ด ์ผœ์ง„ ์ค„ ์•Œ์•˜๋Š”๋ฐ ์•ˆ ๋ˆ"
                    # ์ƒํƒœ๊ฐ€ ๋˜๋ฏ€๋กœ ๋ง‰์Šต๋‹ˆ๋‹ค.
                    raise ValueError(
                        f"mode={self.mode!r} ์˜ ๋””ํ—ค์ด์ €์—๋Š” process_lowlight๊ฐ€ "
                        "์—†์Šต๋‹ˆ๋‹ค. AOD-Net์€ ์ €์กฐ๋„ ๋Œ€์‘๋ฌผ์ด ์—†์Šต๋‹ˆ๋‹ค "
                        "(HANDOVER 4-9). lowlight๋ฅผ ๋„๊ฑฐ๋‚˜ dehaze/full์„ ์“ฐ์„ธ์š”."
                    )
                img = dehazer.process_lowlight(img)
            self.timings["dehaze"] = (time.perf_counter() - t) * 1000.0

        if self.mode in CLAHE_MODES:
            t = time.perf_counter()
            img = self.clahe.process(img)
            self.timings["clahe"] = (time.perf_counter() - t) * 1000.0

        self.timings["total"] = (time.perf_counter() - t_start) * 1000.0
        return img