mirror of
https://github.com/cedricbonhomme/Stegano.git
synced 2025-06-27 19:06:12 +02:00
chg: [typing] Make Mypy Happy Again.
This commit is contained in:
parent
21004219fa
commit
18fd8a8a49
5 changed files with 43 additions and 21 deletions
|
@ -21,7 +21,7 @@ repos:
|
||||||
additional_dependencies:
|
additional_dependencies:
|
||||||
- flake8-bugbear
|
- flake8-bugbear
|
||||||
- flake8-implicit-str-concat
|
- flake8-implicit-str-concat
|
||||||
args: ["--max-line-length=125"]
|
args: ["--max-line-length=125", "--ignore=E203"]
|
||||||
- repo: https://github.com/pre-commit/pre-commit-hooks
|
- repo: https://github.com/pre-commit/pre-commit-hooks
|
||||||
rev: v4.1.0
|
rev: v4.1.0
|
||||||
hooks:
|
hooks:
|
||||||
|
|
|
@ -73,7 +73,7 @@ optional = true
|
||||||
|
|
||||||
|
|
||||||
[tool.mypy]
|
[tool.mypy]
|
||||||
python_version = "3.12"
|
python_version = "3.13"
|
||||||
check_untyped_defs = true
|
check_untyped_defs = true
|
||||||
ignore_errors = false
|
ignore_errors = false
|
||||||
ignore_missing_imports = true
|
ignore_missing_imports = true
|
||||||
|
@ -87,7 +87,12 @@ warn_unreachable = true
|
||||||
show_error_context = true
|
show_error_context = true
|
||||||
pretty = true
|
pretty = true
|
||||||
|
|
||||||
exclude = "build|dist|docs|stegano.egg-info"
|
exclude = "build|dist|docs"
|
||||||
|
|
||||||
|
|
||||||
[tool.isort]
|
[tool.isort]
|
||||||
profile = "black"
|
profile = "black"
|
||||||
|
|
||||||
|
|
||||||
|
[tool.flake8]
|
||||||
|
ignore = ["E203"]
|
||||||
|
|
|
@ -23,7 +23,7 @@ __date__ = "$Date: 2010/10/01 $"
|
||||||
__revision__ = "$Date: 2017/02/06 $"
|
__revision__ = "$Date: 2017/02/06 $"
|
||||||
__license__ = "GPLv3"
|
__license__ = "GPLv3"
|
||||||
|
|
||||||
from typing import IO, Union
|
from typing import IO, Union, cast
|
||||||
|
|
||||||
from stegano import tools
|
from stegano import tools
|
||||||
|
|
||||||
|
@ -40,13 +40,17 @@ def hide(input_image: Union[str, IO[bytes]], message: str):
|
||||||
assert message_length != 0, "message message_length is zero"
|
assert message_length != 0, "message message_length is zero"
|
||||||
assert message_length < 255, "message is too long"
|
assert message_length < 255, "message is too long"
|
||||||
img = tools.open_image(input_image)
|
img = tools.open_image(input_image)
|
||||||
|
# Ensure image mode is RGB
|
||||||
|
if img.mode != "RGB":
|
||||||
|
img = img.convert("RGB")
|
||||||
# Use a copy of image to hide the text in
|
# Use a copy of image to hide the text in
|
||||||
encoded = img.copy()
|
encoded = img.copy()
|
||||||
width, height = img.size
|
width, height = img.size
|
||||||
index = 0
|
index = 0
|
||||||
for row in range(height):
|
for row in range(height):
|
||||||
for col in range(width):
|
for col in range(width):
|
||||||
(r, g, b) = img.getpixel((col, row))
|
pixel = cast(tuple[int, int, int], img.getpixel((col, row)))
|
||||||
|
r, g, b = pixel
|
||||||
# first value is message_length of message
|
# first value is message_length of message
|
||||||
if row == 0 and col == 0 and index < message_length:
|
if row == 0 and col == 0 and index < message_length:
|
||||||
asc = message_length
|
asc = message_length
|
||||||
|
@ -70,12 +74,16 @@ def reveal(input_image: Union[str, IO[bytes]]):
|
||||||
The red value of the first pixel is used for message_length of string.
|
The red value of the first pixel is used for message_length of string.
|
||||||
"""
|
"""
|
||||||
img = tools.open_image(input_image)
|
img = tools.open_image(input_image)
|
||||||
|
# Ensure image mode is RGB
|
||||||
|
if img.mode != "RGB":
|
||||||
|
img = img.convert("RGB")
|
||||||
width, height = img.size
|
width, height = img.size
|
||||||
message = ""
|
message = ""
|
||||||
index = 0
|
index = 0
|
||||||
for row in range(height):
|
for row in range(height):
|
||||||
for col in range(width):
|
for col in range(width):
|
||||||
r, g, b = img.getpixel((col, row))
|
pixel = cast(tuple[int, int, int], img.getpixel((col, row)))
|
||||||
|
r, g, b = pixel
|
||||||
# First pixel r value is length of message
|
# First pixel r value is length of message
|
||||||
if row == 0 and col == 0:
|
if row == 0 and col == 0:
|
||||||
message_length = r
|
message_length = r
|
||||||
|
|
|
@ -23,6 +23,8 @@ __date__ = "$Date: 2010/10/01 $"
|
||||||
__revision__ = "$Date: 2019/06/06 $"
|
__revision__ = "$Date: 2019/06/06 $"
|
||||||
__license__ = "GPLv3"
|
__license__ = "GPLv3"
|
||||||
|
|
||||||
|
from typing import cast
|
||||||
|
|
||||||
from PIL import Image
|
from PIL import Image
|
||||||
|
|
||||||
|
|
||||||
|
@ -34,7 +36,7 @@ def steganalyse(img: Image.Image) -> Image.Image:
|
||||||
width, height = img.size
|
width, height = img.size
|
||||||
for row in range(height):
|
for row in range(height):
|
||||||
for col in range(width):
|
for col in range(width):
|
||||||
if pixel := img.getpixel((col, row)):
|
if pixel := cast(tuple[int, int, int], img.getpixel((col, row))):
|
||||||
r, g, b = pixel[0:3]
|
r, g, b = pixel[0:3]
|
||||||
else:
|
else:
|
||||||
raise Exception("Error during steganlysis.")
|
raise Exception("Error during steganlysis.")
|
||||||
|
|
|
@ -26,7 +26,7 @@ __license__ = "GPLv3"
|
||||||
import base64
|
import base64
|
||||||
import itertools
|
import itertools
|
||||||
from functools import reduce
|
from functools import reduce
|
||||||
from typing import IO, List, Union
|
from typing import IO, List, Union, cast
|
||||||
|
|
||||||
from PIL import Image
|
from PIL import Image
|
||||||
|
|
||||||
|
@ -101,11 +101,12 @@ def base642binary(b64_fname: str) -> bytes:
|
||||||
return base64.b64decode(b64_fname)
|
return base64.b64decode(b64_fname)
|
||||||
|
|
||||||
|
|
||||||
def open_image(fname_or_instance: Union[str, IO[bytes]]):
|
def open_image(fname_or_instance: Union[str, IO[bytes], Image.Image]) -> Image.Image:
|
||||||
"""Opens a Image and returns it.
|
"""Opens an image and returns it.
|
||||||
|
|
||||||
:param fname_or_instance: Can either be the location of the image as a
|
:param fname_or_instance: Can be a path to the image (str),
|
||||||
string or the Image.Image instance itself.
|
a file-like object (IO[bytes]),
|
||||||
|
or a PIL Image instance.
|
||||||
"""
|
"""
|
||||||
if isinstance(fname_or_instance, Image.Image):
|
if isinstance(fname_or_instance, Image.Image):
|
||||||
return fname_or_instance
|
return fname_or_instance
|
||||||
|
@ -157,8 +158,15 @@ class Hider:
|
||||||
return True if self._index + 3 <= self._len_message_bits else False
|
return True if self._index + 3 <= self._len_message_bits else False
|
||||||
|
|
||||||
def encode_pixel(self, coordinate: tuple):
|
def encode_pixel(self, coordinate: tuple):
|
||||||
# Get the colour component.
|
# Determine expected pixel format based on mode
|
||||||
r, g, b, *a = self.encoded_image.getpixel(coordinate)
|
if self.encoded_image.mode == "RGBA":
|
||||||
|
r, g, b, *a = cast(
|
||||||
|
tuple[int, int, int, int], self.encoded_image.getpixel(coordinate)
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
r, g, b, *a = cast(
|
||||||
|
tuple[int, int, int], self.encoded_image.getpixel(coordinate)
|
||||||
|
)
|
||||||
|
|
||||||
# Change the Least Significant Bit of each colour component.
|
# Change the Least Significant Bit of each colour component.
|
||||||
r = setlsb(r, self._message_bits[self._index])
|
r = setlsb(r, self._message_bits[self._index])
|
||||||
|
@ -190,8 +198,11 @@ class Revealer:
|
||||||
self.close_file = close_file
|
self.close_file = close_file
|
||||||
|
|
||||||
def decode_pixel(self, coordinate: tuple):
|
def decode_pixel(self, coordinate: tuple):
|
||||||
# pixel = [r, g, b] or [r,g,b,a]
|
# Tell mypy that this will be a 3- or 4-tuple of ints
|
||||||
pixel = self.encoded_image.getpixel(coordinate)
|
pixel = cast(
|
||||||
|
tuple[int, int, int] | tuple[int, int, int, int],
|
||||||
|
self.encoded_image.getpixel(coordinate),
|
||||||
|
)
|
||||||
|
|
||||||
if self.encoded_image.mode == "RGBA":
|
if self.encoded_image.mode == "RGBA":
|
||||||
pixel = pixel[:3] # ignore the alpha
|
pixel = pixel[:3] # ignore the alpha
|
||||||
|
@ -211,13 +222,9 @@ class Revealer:
|
||||||
raise IndexError("Impossible to detect message.")
|
raise IndexError("Impossible to detect message.")
|
||||||
|
|
||||||
if len(self._bitab) - len(str(self._limit)) - 1 == self._limit:
|
if len(self._bitab) - len(str(self._limit)) - 1 == self._limit:
|
||||||
self.secret_message = "".join(self._bitab)[
|
self.secret_message = "".join(self._bitab)[len(str(self._limit)) + 1 :]
|
||||||
len(str(self._limit)) + 1 : # noqa: E203
|
|
||||||
]
|
|
||||||
if self.close_file:
|
if self.close_file:
|
||||||
self.encoded_image.close()
|
self.encoded_image.close()
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
else:
|
else:
|
||||||
return False
|
return False
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue