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

Class LSBOutputStream

StegaPy/plugin/lsb/lsb_output_stream.py:11–60  ·  view source on GitHub ↗

LSB 输出流,按顺序将数据位嵌入图像各像素通道的最低有效位。

Source from the content-addressed store, hash-verified

9
10
11class LSBOutputStream:
12 """LSB 输出流,按顺序将数据位嵌入图像各像素通道的最低有效位。"""
13
14 def __init__(self, image: Image.Image, data_length: int,
15 filename: str, config: LSBConfig):
16 self.image = image.copy()
17 self.config = config
18 self.header = LSBDataHeader(data_length, config.get_max_bits_used_per_channel(),
19 filename, config)
20
21 self.pixels = np.array(self.image)
22 self.height, self.width, self.channels = self.pixels.shape
23
24 bits_per_pixel = self.channels * config.get_max_bits_used_per_channel()
25 total_bits = (len(self.header.to_bytes()) + data_length) * 8
26 if (total_bits + bits_per_pixel - 1) // bits_per_pixel > self.width * self.height:
27 raise ValueError(f"图像太小,无法嵌入 {data_length} 字节的数据")
28
29 self._buffer = bytearray(self.header.to_bytes())
30
31 def write(self, data: bytes):
32 self._buffer.extend(data)
33
34 def flush(self):
35 self._embed(bytes(self._buffer))
36 self.image = Image.fromarray(np.clip(self.pixels, 0, 255).astype(np.uint8))
37
38 def _embed(self, data: bytes):
39 bits_per_ch = self.config.get_max_bits_used_per_channel()
40 bits = np.unpackbits(np.frombuffer(data, dtype=np.uint8))
41 n = len(bits)
42
43 if bits_per_ch == 1:
44 # 顺序 LSB:flat_idx == position,直接切片,避免 fancy indexing
45 flat = self.pixels.reshape(-1)
46 flat[:n] = ((flat[:n].astype(np.int32) & 0xFE) | bits).astype(np.uint8)
47 else:
48 for i, bit in enumerate(bits):
49 ch_pos = i // bits_per_ch
50 bit_slot = i % bits_per_ch
51 pixel_idx = ch_pos // self.channels
52 channel = ch_pos % self.channels
53 row = pixel_idx // self.width
54 col = pixel_idx % self.width
55 v = int(self.pixels[row, col, channel])
56 mask = 0xFF ^ (1 << bit_slot)
57 self.pixels[row, col, channel] = (v & mask) | (int(bit) << bit_slot)
58
59 def get_image(self) -> Image.Image:
60 return self.image

Callers 1

embed_dataMethod · 0.85

Calls

no outgoing calls

Tested by

no test coverage detected