Skip to content

Commit 3e4a50d

Browse files
authored
Merge pull request #11 from dev4hobby/release
Release/ new structure into main branch
2 parents a757263 + a6c10c6 commit 3e4a50d

18 files changed

Lines changed: 202 additions & 45 deletions

.gitignore

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,17 @@ __pycache__/
44
*$py.class
55
# << Python << #
66

7+
# >> Python: build package >> #
8+
*.egg-info
9+
build.sh
10+
clean.sh
11+
build
12+
dist
13+
# << Python: build package<< #
14+
715
# >> misc: image >> #
816
misc/
9-
!image_out.png
10-
!image.png
17+
!input.png
18+
!output.png
1119
*.png
1220
# << misc: image << #

README.md

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# Image Steganography
22

3-
![image_out](./image_out.png)
3+
![image_out](./output.png)
44
What you see isn't everything.
55
This image contains hidden data.
66

@@ -38,7 +38,7 @@ python hide.py
3838
or
3939

4040
```bash
41-
python hide.py -in image.png -out image_out.png -m payload.txt
41+
python hide.py -in image.png -out output.png -m message.txt
4242
```
4343

4444
### Seek message
@@ -52,7 +52,7 @@ python seek.py
5252
or
5353

5454
```bash
55-
python seek.py -in image_out.png
55+
python seek.py -in output.png
5656
```
5757

5858
## Serve as WebServer (with FastAPI)
@@ -92,9 +92,9 @@ You can check the image path or other required values.
9292
"encoding": "utf8",
9393
"bits": 8,
9494
"token_string": "#secret#",
95-
"payload": "./payload.txt",
95+
"message": "./message.txt",
9696
"in_image": "./image.png",
97-
"out_image": "./image_out.png",
98-
"modified_image": "./image_out.png"
97+
"out_image": "./output.png",
98+
"modified_image": "./output.png"
9999
}
100100
```

binjector/LICENSE

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
MIT License
2+
3+
Copyright (c) 2021 Seongchuel Ahn
4+
5+
Permission is hereby granted, free of charge, to any person obtaining a copy
6+
of this software and associated documentation files (the "Software"), to deal
7+
in the Software without restriction, including without limitation the rights
8+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
copies of the Software, and to permit persons to whom the Software is
10+
furnished to do so, subject to the following conditions:
11+
12+
The above copyright notice and this permission notice shall be included in all
13+
copies or substantial portions of the Software.
14+
15+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21+
SOFTWARE.

binjector/README.md

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
# Binjector
2+
3+
Binjector a.k.a Binary Injector makes your text inject into image easily.
4+
[Web version](https://image-steganography.vercel.app/)
5+
[Git: main](https://github.com/dev4hobby/image-steganography)
6+
7+
## Usage
8+
9+
### Instalation
10+
11+
```bash
12+
pip install binjector
13+
```
14+
15+
### Hide
16+
17+
```bash
18+
binjector hide -i image.png -m text.txt [-o output.png]
19+
```
20+
21+
### Seek
22+
23+
```bash
24+
binjector seek -i image.png
25+
```
26+
27+
Have Fun

binjector/__init__.py

Whitespace-only changes.

binjector/__main__.py

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
import click
2+
from binjector.utils import read_settings, set_token
3+
from binjector.steganography import Steganography
4+
5+
@click.group()
6+
def cli():
7+
pass
8+
9+
@cli.command()
10+
@click.option('-i', required=True, type=click.Path(exists=True), help='Input file')
11+
@click.option('-o', type=click.Path(exists=False), help='Output file')
12+
@click.option('-m', required= True, type=click.Path(exists=True), help='Message file')
13+
def hide(i, o, m):
14+
s = Steganography()
15+
# hide your message into image
16+
if not o:
17+
from datetime import datetime
18+
o = f'output_{datetime.now().strftime("%Y%m%d_%H%M%S")}.png'
19+
try:
20+
with open(m, "r") as message_file:
21+
message = message_file.read()
22+
s.hide_message(i, o, message)
23+
print('Done')
24+
except Exception as e:
25+
print(e)
26+
raise e
27+
28+
@cli.command()
29+
@click.option('-i', required=True, type=click.Path(exists=True), help='Input file')
30+
def seek(i):
31+
s = Steganography()
32+
# seek your message into image
33+
try:
34+
print(s.seek_message(i))
35+
except Exception as e:
36+
print(e)
37+
raise e
38+
39+
@cli.command()
40+
def settings():
41+
try:
42+
config = read_settings()
43+
print(config)
44+
except Exception as e:
45+
print(e)
46+
raise e
47+
48+
@cli.command()
49+
def refresh_token():
50+
try:
51+
set_token()
52+
except Exception as e:
53+
print(e)
54+
raise e
55+
56+
if __name__ == '__main__':
57+
cli()

binjector/requirements.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
numpy>=1.21.2
2+
Pillow>=8.3.2
Lines changed: 3 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -4,16 +4,15 @@
44
from PIL import Image
55
from typing import Union, Tuple
66
from pathlib import Path
7-
from utils import get_timestamp_as_md5
7+
from .utils import get_timestamp_as_md5, read_settings
88

99
class Steganography():
1010
'''
1111
class for hiding and extracting message on your image
1212
'''
1313

1414
def __init__(self):
15-
with open("settings.json", "r") as setting_file:
16-
settings = json.load(setting_file)
15+
settings = read_settings()
1716
self.bits = int(settings.get('bits', 8))
1817
self.encoding = settings.get('encoding', 'utf-8')
1918
self.token_string = settings.get('token_string', '#secret#')
@@ -31,15 +30,6 @@ def binstr_to_ascii(self, message:str) -> str: ###
3130
'''
3231
Convert binary string to ascii string
3332
'''
34-
35-
# only ASCII
36-
# binstr = map(''.join, zip(*[iter(message)]*8))
37-
# result = str()
38-
# for idx, bin in enumerate(binstr):
39-
# print(bin)
40-
# result += chr(int(bin, 2))
41-
42-
# for UTF-8
4333
byte_list = [message[i:i+8] for i in range(0, len(message), 8)]
4434
result = bytes([int(uint8, 2) for uint8 in byte_list]).decode('utf-8')
4535
return result
@@ -83,8 +73,6 @@ def message_to_binary(self, message:Union[bytes, str, int, np.ndarray, np.uint8]
8373
return ''.join(format(ord(char), '08b') for char in message)
8474
elif type(message) == bytes or type(message) == np.ndarray:
8575
return [ format(i, "08b") for i in message]
86-
# elif type(message) == int or type(message) == np.uint8:
87-
# return format(message, "08b")
8876
else:
8977
raise TypeError("Input type not supported")
9078

@@ -133,7 +121,7 @@ def get_lsb_string(self, imarray: np.ndarray) -> str:
133121
done = False
134122
for row_index, row in enumerate(imarray):
135123
for col_index, col in enumerate(row):
136-
r, g, b = self.message_to_binary(col) # unpack
124+
r, g, b = self.message_to_binary(col)
137125
binary_message += r[-1] + g[-1] + b[-1]
138126
binary_token_index = binary_message.find(binary_token)
139127
if binary_token_index != -1:

binjector/utils.py

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
import json
2+
from hashlib import md5
3+
from datetime import datetime
4+
5+
def get_timestamp_as_md5():
6+
return md5(str(datetime.now()).encode('utf-8')).hexdigest()
7+
8+
def create_settings():
9+
settings = {
10+
"encoding": "utf8",
11+
"bits": 8,
12+
"token_string": get_timestamp_as_md5(),
13+
"message": "./message.txt",
14+
"in_image": "./input.png",
15+
"out_image": "./output.png",
16+
"modified_image": "./output.png"
17+
}
18+
with open("./settings.json", "w") as setting_file:
19+
json.dump(settings, setting_file)
20+
21+
def read_settings() -> dict:
22+
try:
23+
with open("settings.json", "r") as setting_file:
24+
settings = json.load(setting_file)
25+
return settings
26+
except FileNotFoundError:
27+
create_settings()
28+
return read_settings()
29+
30+
def set_token():
31+
settings = read_settings()
32+
settings['token'] = get_timestamp_as_md5()
33+
with open("./settings.json", "w") as setting_file:
34+
json.dump(settings, setting_file)
35+
print ("Token set to: " + settings['token'])

hide.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,16 @@
11
import json
22
import argparse
3-
from steganography import Steganography
3+
from binjector.steganography import Steganography
4+
from binjector.utils import read_settings
45

56
parser = argparse.ArgumentParser(description='Hide a message in an image.')
67
parser.add_argument('-in', '--in-image', help='The image to hide the message in.', required=False)
78
parser.add_argument('-out', '--out-image', help='The image to save the message in.', required=False)
89
parser.add_argument('-m', '--message', help='The message file path to hide.', required=False)
910
args = parser.parse_args()
1011

11-
with open("settings.json", "r") as setting_file:
12-
settings = json.load(setting_file)
13-
message_file_name = args.message if args.message else settings.get('payload')
12+
settings = read_settings()
13+
message_file_name = args.message if args.message else settings.get('message')
1414
in_file_name = args.in_image if args.in_image else settings.get('in_image')
1515
out_file_name = args.out_image if args.out_image else settings.get('out_image')
1616

0 commit comments

Comments
 (0)