Initial commit
This commit is contained in:
@@ -0,0 +1,5 @@
|
||||
__all__ = ["Embedder", "Extractor", "ComponentPipeline"]
|
||||
|
||||
from .embedder import Embedder
|
||||
from .extractor import Extractor
|
||||
from .component_pipeline import ComponentPipeline
|
||||
@@ -0,0 +1,34 @@
|
||||
from __future__ import annotations
|
||||
from dataclasses import dataclass
|
||||
import numpy as np
|
||||
|
||||
from entities.rgb_image512 import RgbImage512
|
||||
from entities.ycbcr_image512 import YcbcrImage512
|
||||
from entities.label64 import Label64
|
||||
from entities.sequence8 import Sequence8
|
||||
|
||||
from .embedder import Embedder
|
||||
from .extractor import Extractor
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class ComponentPipeline:
|
||||
"""Оркестрация встраивания/извлечения в заданный канал Y/Cb/Cr."""
|
||||
|
||||
channel: str # "Y", "Cb", "Cr"
|
||||
embedder: Embedder
|
||||
extractor: Extractor
|
||||
|
||||
def embed_rgb(self, rgb: RgbImage512, label: Label64, seq: Sequence8) -> RgbImage512:
|
||||
"""Вернуть новый RGB с вшитой меткой в выбранный канал."""
|
||||
ycbcr = YcbcrImage512.from_rgb(rgb)
|
||||
comp = ycbcr.get_channel(self.channel)
|
||||
comp_new = self.embedder.embed_component(comp, label, seq)
|
||||
ycbcr_new = ycbcr.with_channel(self.channel, comp_new)
|
||||
return ycbcr_new.to_rgb()
|
||||
|
||||
def extract_from_rgb(self, rgb: RgbImage512, seq: Sequence8) -> Label64:
|
||||
"""Извлечь метку из выбранного канала RGB-изображения."""
|
||||
ycbcr = YcbcrImage512.from_rgb(rgb)
|
||||
comp = ycbcr.get_channel(self.channel)
|
||||
return self.extractor.extract_label(comp, seq)
|
||||
@@ -0,0 +1,31 @@
|
||||
from __future__ import annotations
|
||||
from dataclasses import dataclass
|
||||
import numpy as np
|
||||
|
||||
from entities.block_grid8 import BlockGrid8
|
||||
from entities.lsb_row8 import LsbRow8
|
||||
from entities.label64 import Label64
|
||||
from entities.sequence8 import Sequence8
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class Embedder:
|
||||
"""Встраивание метки в 2D-компоненту Y/Cb/Cr через LSB верхней строки блоков 8×8."""
|
||||
|
||||
def embed_component(self, component: np.ndarray, label: Label64, seq: Sequence8) -> np.ndarray:
|
||||
"""Вернуть новую компоненту с вшитой меткой."""
|
||||
if component.ndim != 2:
|
||||
raise ValueError("Ожидается 2D-компонента.")
|
||||
h, w = component.shape
|
||||
if h % 8 != 0 or w % 8 != 0:
|
||||
raise ValueError("Размеры компоненты должны быть кратны 8.")
|
||||
blocks = BlockGrid8.split(component)
|
||||
bits = label.to_vector() # 4096
|
||||
if blocks.shape[0] != bits.size:
|
||||
raise ValueError("Количество блоков должно равняться 4096.")
|
||||
out_blocks = blocks.copy()
|
||||
# Для каждого бита берём 8-битную сигнатуру и встраиваем в верхнюю строку блока
|
||||
for i in range(bits.size):
|
||||
pattern = seq.for_bit(int(bits[i]))
|
||||
out_blocks[i] = LsbRow8.embed(out_blocks[i], pattern)
|
||||
return BlockGrid8.merge(out_blocks, h, w)
|
||||
@@ -0,0 +1,34 @@
|
||||
from __future__ import annotations
|
||||
from dataclasses import dataclass
|
||||
import numpy as np
|
||||
|
||||
from entities.block_grid8 import BlockGrid8
|
||||
from entities.lsb_row8 import LsbRow8
|
||||
from entities.label64 import Label64
|
||||
from entities.sequence8 import Sequence8
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class Extractor:
|
||||
"""Извлечение метки из 2D-компоненты через LSB верхней строки блоков 8×8."""
|
||||
|
||||
def extract_label(self, component: np.ndarray, seq: Sequence8) -> Label64:
|
||||
"""Вернуть Label64, восстановленную из компоненты."""
|
||||
if component.ndim != 2:
|
||||
raise ValueError("Ожидается 2D-компонента.")
|
||||
h, w = component.shape
|
||||
if h % 8 != 0 or w % 8 != 0:
|
||||
raise ValueError("Размеры компоненты должны быть кратны 8.")
|
||||
blocks = BlockGrid8.split(component)
|
||||
if blocks.shape[0] != 4096:
|
||||
raise ValueError("Для метки 64x64 требуется 4096 блоков 8x8.")
|
||||
bits = np.empty(4096, dtype=np.uint8)
|
||||
s0 = seq.seq0
|
||||
s1 = seq.seq1
|
||||
# Поблочный выбор по минимальной хэмминговой дистанции
|
||||
for i, blk in enumerate(blocks):
|
||||
b = LsbRow8.extract(blk)
|
||||
d0 = np.count_nonzero(b != s0)
|
||||
d1 = np.count_nonzero(b != s1)
|
||||
bits[i] = 0 if d0 <= d1 else 1
|
||||
return Label64.from_vector(bits)
|
||||
Reference in New Issue
Block a user