Реализовать внедрение информации в изображение в формате .bmp методом псевдослучайного
интервала.
Реализация должна принимать на вход файл-контейнер с изображением в формате .bmp и текст для
вставки, внедрять в контейнер сообщение и сохранять информацию о количестве вставленных бит.
Программа должна уметь извлекать текст из заполненного контейнера.
Отчет должен содержать описание метода и его реализации, оценку емкости контейнера для
реализованного метода, количественную оценку потери качества (PSNR) для оригинального и кодированного файла, примеры работы
программы и исходные тексты основных модулей. Реализовать на языке питон.

вот задание, но у меня есть чужой код, видоизмени его и сделай чтобы программа запрашивала в консоли какой текст ввести, на символ используй по 16 бит чтобы русские символы тоже можно было кодировать

import os
2. import random
3. import math
4.
5. IMAGE_FILENAME = 'lab02/kodim15.bmp'
6. IMAGE_ENCODED_FILENAME = 'lab02/kodim_encoded.bmp'
7. TEXT_ORIGIN_FILENAME = 'lab02/text.txt'
8. TEXT_DECODED_FILENAME = 'lab02/decoded.txt'
9.
10.PIXEL_OFFSET_CONST = 138
11.COUNT_BACKUP = 16
12.
13.TEXT_MASK = 128 # 10000000
14.IMG_MASK = 254 # 11111110
15.
16.def generateIndexes(seed, width, height) -> list:
17. generatedList = list(range(COUNT_BACKUP, (width * height) * 3))
18. random.seed(seed)
19.
20. return sorted(generatedList, key=lambda x: random.random())
21.
22.def encrypt(randomKey):
23. textFileLen = os.stat(TEXT_ORIGIN_FILENAME).st_size
24. imgFileLen = os.stat(IMAGE_FILENAME).st_size
25. print(imgFileLen - PIXEL_OFFSET_CONST - COUNT_BACKUP)
26. print(textFileLen * 8)
27. if textFileLen * 8 > imgFileLen - PIXEL_OFFSET_CONST -
COUNT_BACKUP:
28. print("Text is too long")
29. return
30.
31. fullImage, originWidth, originHeight, pixelOffset =
getBmpValues(IMAGE_FILENAME)
32. indexList = generateIndexes(randomKey, originWidth, originHeight)
33. print(len(indexList))
34. print(len(fullImage))
35. count = 0
36. text = open(TEXT_ORIGIN_FILENAME, 'r')
37. while True:
38. symbol = text.read(1)
39. if not symbol:
40. break
41. currentCharacter = ord(symbol)
42.
43. for _ in range(8):
44. imgByte = fullImage[pixelOffset + indexList[count]] &
IMG_MASK
45. encodedByte = imgByte | ((currentCharacter & TEXT_MASK) >
7)
46.
47. fullImage[pixelOffset + indexList[count]] = encodedByte
48.
49. currentCharacter <<= 1
50. count += 1
51.
52.
53. countBinFormat = f'{count:016b}'
54. for i in range(COUNT_BACKUP):
55. curBit = 0 if i >= len(countBinFormat) else
int(countBinFormat[i])
56. imgByte = fullImage[pixelOffset + i] & IMG_MASK
57. encodedByte = imgByte | curBit
58. fullImage[pixelOffset + i] = encodedByte
59.
60. encodedBmpFile = open(IMAGE_ENCODED_FILENAME, 'wb')
61. encodedBmpFile.write(fullImage)
62. encodedBmpFile.close()
63. print(COUNT_BACKUP + count, ' - ', originHeight * originWidth * 3)
64. text.close()
65.
66.def decrypt(key):
67. fullImage, originWidth, originHeight, pixelOffset =
getBmpValues(IMAGE_ENCODED_FILENAME)
68. indexList = generateIndexes(key, originWidth, originHeight)
69.
70. count_bit = 0
71. for i in range(COUNT_BACKUP):
72. imgByte = fullImage[pixelOffset + i] & 1
73. count_bit += (2**(COUNT_BACKUP - i - 1)) * imgByte
74.
75. count = 0
76. textDecrypted = ""
77. while count < count_bit:
78. symbol = 0
79. for _ in range(8):
80. symbol <<= 1
81. symbol |= fullImage[pixelOffset + indexList[count]] &
~IMG_MASK
82. count += 1
83.
84. textDecrypted += chr(symbol)
85.
86. decodedTextFile = open(TEXT_DECODED_FILENAME, 'w')
87. decodedTextFile.write(textDecrypted)
88. decodedTextFile.close()
89.
90.def cadr(value):
91. if value < 0:
92. return 0
93. elif value > 255:
94. return 255
95. return value
96.
97.def getBmpValues(filename):
98. with open(filename, 'rb') as f:
99. header = f.read(PIXEL_OFFSET_CONST)
100. pixel_offset = int.from_bytes(header[10:14],
byteorder='little')
101. width = int.from_bytes(header[18:22], byteorder='little')
102. height = int.from_bytes(header[22:26], byteorder='little')
103.
104. f.seek(0)
105. fullImage = bytearray(f.read())
106.
107. return bytearray(fullImage), width, height, pixel_offset
108.
109. def PSNR(originFilename, encodedFilename):
110. originFile, originWidth, originHeight, originOffset =
getBmpValues(originFilename)
111. encodedFile, encodedWidth, encodedHeight, encodedOffset =
getBmpValues(encodedFilename)
112.
113. originFile = originFile[originOffset:]
114. encodedFile = encodedFile[encodedOffset:]
115.
116. if originHeight != encodedHeight or originWidth != encodedWidth:
117. return
118.
119. sumR = 0
120. sumG = 0
121. sumB = 0
122. for i in range(0, len(originFile), 3):
123. redId = i + 2
124. greenId = i + 1
125. blueId = i
126. sumR = sumR + (originFile[redId] - encodedFile[redId]) ** 2
127. sumG = sumG + (originFile[greenId] - encodedFile[greenId])
** 2
128. sumB = sumB + (originFile[blueId] - encodedFile[blueId]) **
2
129.
130. print('psnr(blue) = ', 10 * math.log10((originWidth *
originHeight * (255 ** 2)) / sumB))
131. print('psnr(green) = ', 10 * math.log10((originWidth *
originHeight * (255 ** 2)) / sumG))
132. print('psnr(red) = ', 10 * math.log10((originWidth *
originHeight * (255 ** 2)) / sumR))
133.
134. randomKey = 123
135. encrypt(randomKey)
136. decrypt(randomKey)
137. PSNR(IMAGE_FILENAME, IMAGE_ENCODED_FILENAME)