MCPcopy Create free account
hub / github.com/MearaY/StegaPy / RandomLSBOutputStream

Class RandomLSBOutputStream

StegaPy/plugin/randlsb/random_lsb_output_stream.py:12–75  ·  view source on GitHub ↗

RandomLSB 输出流,使用密码学种子驱动的伪随机序列散布嵌入位, 抵抗针对顺序 LSB 的卡方统计攻击。

Source from the content-addressed store, hash-verified

10
11
12class RandomLSBOutputStream:
13 """RandomLSB 输出流,使用密码学种子驱动的伪随机序列散布嵌入位,
14 抵抗针对顺序 LSB 的卡方统计攻击。"""
15
16 def __init__(self, image: Image.Image, data_length: int,
17 filename: str, config: LSBConfig, password: str = None):
18 self.image = image.copy()
19 self.config = config
20 self.header = LSBDataHeader(data_length, config.get_max_bits_used_per_channel(),
21 filename, config)
22
23 self.pixels = np.array(self.image)
24 self.height, self.width, self.channels = self.pixels.shape
25
26 bits_per_pixel = self.channels * config.get_max_bits_used_per_channel()
27 total_bits = (len(self.header.to_bytes()) + data_length) * 8
28 if (total_bits + bits_per_pixel - 1) // bits_per_pixel > self.width * self.height:
29 raise ValueError(f"图像太小,无法嵌入 {data_length} 字节的数据")
30
31 seed = CommonUtil.password_hash(password) if password else 0
32 total_positions = self.width * self.height * self.channels
33 # NumPy 实现的 Fisher-Yates 洗牌,比纯 Python list+shuffle 快约 20 倍
34 rng = np.random.default_rng(seed % (2 ** 63))
35 self.position_sequence = rng.permutation(total_positions)
36
37 self._buffer = bytearray(self.header.to_bytes())
38
39 def write(self, data: bytes):
40 self._buffer.extend(data)
41
42 def flush(self):
43 self._embed(bytes(self._buffer))
44 self.image = Image.fromarray(np.clip(self.pixels, 0, 255).astype(np.uint8))
45
46 def _embed(self, data: bytes):
47 bits_per_ch = self.config.get_max_bits_used_per_channel()
48 bits = np.unpackbits(np.frombuffer(data, dtype=np.uint8))
49 n = len(bits)
50
51 if bits_per_ch == 1:
52 positions = self.position_sequence[:n].astype(np.int64)
53 pixel_indices = positions // self.channels
54 channel_indices = positions % self.channels
55 rows = pixel_indices // self.width
56 cols = pixel_indices % self.width
57 self.pixels[rows, cols, channel_indices] = (
58 (self.pixels[rows, cols, channel_indices].astype(np.int32) & 0xFE) | bits
59 ).astype(np.uint8)
60 else:
61 # 多位模式:第 i 个 bit 写入第 (i // bits_per_ch) 个位置的第 (i % bits_per_ch) 位
62 for i, bit in enumerate(bits):
63 ch_pos = i // bits_per_ch
64 bit_slot = i % bits_per_ch
65 pos = int(self.position_sequence[ch_pos])
66 pixel_idx = pos // self.channels
67 channel = pos % self.channels
68 row = pixel_idx // self.width
69 col = pixel_idx % self.width

Callers 1

embed_dataMethod · 0.85

Calls

no outgoing calls

Tested by

no test coverage detected