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

Class RandomLSBInputStream

StegaPy/plugin/randlsb/random_lsb_input_stream.py:12–79  ·  view source on GitHub ↗

RandomLSB 输入流,使用与嵌入端相同的伪随机序列还原散布位。

Source from the content-addressed store, hash-verified

10
11
12class RandomLSBInputStream:
13 """RandomLSB 输入流,使用与嵌入端相同的伪随机序列还原散布位。"""
14
15 def __init__(self, image: Image.Image, config: LSBConfig, password: str = None):
16 self.config = config
17 self.pixels = np.array(image)
18 self.height, self.width, self.channels = self.pixels.shape
19
20 seed = CommonUtil.password_hash(password) if password else 0
21 total_positions = self.width * self.height * self.channels
22 rng = np.random.default_rng(seed % (2 ** 63))
23 self.position_sequence = rng.permutation(total_positions)
24
25 self._bit_offset = 0
26
27 # 先读固定部分以获取 filename_len,再读变长文件名
28 fixed_size = (len(LSBDataHeader.DATA_STAMP) + len(LSBDataHeader.HEADER_VERSION)
29 + LSBDataHeader.FIXED_HEADER_LENGTH + LSBDataHeader.CRYPT_ALGO_LENGTH)
30 fixed_bytes = self._read_bytes(fixed_size)
31
32 fixed_header_offset = len(LSBDataHeader.DATA_STAMP) + len(LSBDataHeader.HEADER_VERSION)
33 filename_len = fixed_bytes[fixed_header_offset + 5]
34
35 filename_bytes = self._read_bytes(filename_len) if filename_len > 0 else b''
36 self.header = LSBDataHeader.from_bytes(fixed_bytes + filename_bytes, config)
37
38 def _read_bytes(self, n_bytes: int) -> bytes:
39 bits_per_ch = self.config.get_max_bits_used_per_channel()
40 n_bits = n_bytes * 8
41
42 if bits_per_ch == 1:
43 positions = self.position_sequence[self._bit_offset:self._bit_offset + n_bits].astype(np.int64)
44 pixel_indices = positions // self.channels
45 channel_indices = positions % self.channels
46 rows = pixel_indices // self.width
47 cols = pixel_indices % self.width
48 bits = (self.pixels[rows, cols, channel_indices] & 1).astype(np.uint8)
49 self._bit_offset += n_bits
50 return np.packbits(bits).tobytes()
51 else:
52 result = bytearray()
53 for _ in range(n_bytes):
54 byte_val = 0
55 for bit_pos in range(8):
56 bit = self._read_one_bit()
57 byte_val = (byte_val << 1) | bit
58 result.append(byte_val)
59 return bytes(result)
60
61 def _read_one_bit(self) -> int:
62 bits_per_ch = self.config.get_max_bits_used_per_channel()
63 ch_pos = self._bit_offset // bits_per_ch
64 bit_slot = self._bit_offset % 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 2

extract_msg_filenameMethod · 0.85
extract_dataMethod · 0.85

Calls

no outgoing calls

Tested by

no test coverage detected