ghostbit 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- ghostbit-0.1.0/PKG-INFO +107 -0
- ghostbit-0.1.0/README.md +76 -0
- ghostbit-0.1.0/ghostbit/__init__.py +26 -0
- ghostbit-0.1.0/ghostbit/steganography.py +371 -0
- ghostbit-0.1.0/ghostbit.egg-info/PKG-INFO +107 -0
- ghostbit-0.1.0/ghostbit.egg-info/SOURCES.txt +10 -0
- ghostbit-0.1.0/ghostbit.egg-info/dependency_links.txt +1 -0
- ghostbit-0.1.0/ghostbit.egg-info/requires.txt +9 -0
- ghostbit-0.1.0/ghostbit.egg-info/top_level.txt +1 -0
- ghostbit-0.1.0/pyproject.toml +39 -0
- ghostbit-0.1.0/setup.cfg +4 -0
- ghostbit-0.1.0/setup.py +48 -0
ghostbit-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: ghostbit
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: A Python library for image steganography using LSB encoding
|
|
5
|
+
Home-page: https://github.com/yourusername/ghostbit-py
|
|
6
|
+
Author: Ghostbit
|
|
7
|
+
Author-email:
|
|
8
|
+
License: MIT
|
|
9
|
+
Classifier: Development Status :: 3 - Alpha
|
|
10
|
+
Classifier: Intended Audience :: Developers
|
|
11
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
12
|
+
Classifier: Programming Language :: Python :: 3
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.8
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
17
|
+
Classifier: Topic :: Security :: Cryptography
|
|
18
|
+
Classifier: Topic :: Multimedia :: Graphics
|
|
19
|
+
Requires-Python: >=3.8
|
|
20
|
+
Description-Content-Type: text/markdown
|
|
21
|
+
Requires-Dist: Pillow>=10.0.0
|
|
22
|
+
Requires-Dist: numpy>=1.24.0
|
|
23
|
+
Requires-Dist: scipy>=1.10.0
|
|
24
|
+
Provides-Extra: dev
|
|
25
|
+
Requires-Dist: pytest>=7.0.0; extra == "dev"
|
|
26
|
+
Requires-Dist: pytest-cov>=4.0.0; extra == "dev"
|
|
27
|
+
Requires-Dist: black>=23.0.0; extra == "dev"
|
|
28
|
+
Requires-Dist: flake8>=6.0.0; extra == "dev"
|
|
29
|
+
Dynamic: home-page
|
|
30
|
+
Dynamic: requires-python
|
|
31
|
+
|
|
32
|
+
# Ghostbit
|
|
33
|
+
|
|
34
|
+
A Python library for image steganography using LSB (Least Significant Bit) encoding.
|
|
35
|
+
|
|
36
|
+
## Features
|
|
37
|
+
|
|
38
|
+
- **LSB Steganography**: Encode and decode secret messages in images using the Least Significant Bit method
|
|
39
|
+
- **DCT Steganography**: Alternative encoding method using Discrete Cosine Transform
|
|
40
|
+
- **Image Analysis**: Detect hidden data and calculate image quality metrics
|
|
41
|
+
|
|
42
|
+
## Installation
|
|
43
|
+
|
|
44
|
+
```bash
|
|
45
|
+
pip install ghostbit
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
Or install from source:
|
|
49
|
+
|
|
50
|
+
```bash
|
|
51
|
+
cd ghostbit-py
|
|
52
|
+
pip install -e .
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
## Quick Start
|
|
56
|
+
|
|
57
|
+
```python
|
|
58
|
+
from ghostbit import encode_lsb, decode_lsb
|
|
59
|
+
|
|
60
|
+
# Encode a message into an image
|
|
61
|
+
encode_lsb("cover_image.png", "secret message", "stego_image.png")
|
|
62
|
+
|
|
63
|
+
# Decode a message from an image
|
|
64
|
+
message = decode_lsb("stego_image.png")
|
|
65
|
+
print(message) # Output: secret message
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
## Usage Examples
|
|
69
|
+
|
|
70
|
+
### Basic Encoding/Decoding
|
|
71
|
+
|
|
72
|
+
```python
|
|
73
|
+
from ghostbit import encode_lsb, decode_lsb
|
|
74
|
+
|
|
75
|
+
# Encode a secret message
|
|
76
|
+
encode_lsb("input.png", "My secret password", "output.png")
|
|
77
|
+
|
|
78
|
+
# Decode the message
|
|
79
|
+
secret = decode_lsb("output.png")
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
## API Reference
|
|
84
|
+
|
|
85
|
+
### Steganography Functions
|
|
86
|
+
|
|
87
|
+
- `encode_lsb(img_path, message, output_path)` - Encode message using LSB
|
|
88
|
+
- `decode_lsb(img_path)` - Decode message using LSB
|
|
89
|
+
- `encode_dct(img_path, message, output_path, block_size=8)` - Encode using DCT
|
|
90
|
+
- `decode_dct(img_path, block_size=8)` - Decode using DCT
|
|
91
|
+
- `detect_hidden_data(img_path, method='decode')` - Detect hidden data
|
|
92
|
+
- `calculate_psnr(img1_path, img2_path)` - Calculate PSNR between images
|
|
93
|
+
|
|
94
|
+
## Requirements
|
|
95
|
+
|
|
96
|
+
- Python >= 3.8
|
|
97
|
+
- Pillow >= 10.0.0
|
|
98
|
+
- numpy >= 1.24.0
|
|
99
|
+
- scipy >= 1.10.0
|
|
100
|
+
|
|
101
|
+
## License
|
|
102
|
+
|
|
103
|
+
MIT License
|
|
104
|
+
|
|
105
|
+
## Contributing
|
|
106
|
+
|
|
107
|
+
Contributions are welcome! Please feel free to submit a Pull Request.
|
ghostbit-0.1.0/README.md
ADDED
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
# Ghostbit
|
|
2
|
+
|
|
3
|
+
A Python library for image steganography using LSB (Least Significant Bit) encoding.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- **LSB Steganography**: Encode and decode secret messages in images using the Least Significant Bit method
|
|
8
|
+
- **DCT Steganography**: Alternative encoding method using Discrete Cosine Transform
|
|
9
|
+
- **Image Analysis**: Detect hidden data and calculate image quality metrics
|
|
10
|
+
|
|
11
|
+
## Installation
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
pip install ghostbit
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
Or install from source:
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
cd ghostbit-py
|
|
21
|
+
pip install -e .
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
## Quick Start
|
|
25
|
+
|
|
26
|
+
```python
|
|
27
|
+
from ghostbit import encode_lsb, decode_lsb
|
|
28
|
+
|
|
29
|
+
# Encode a message into an image
|
|
30
|
+
encode_lsb("cover_image.png", "secret message", "stego_image.png")
|
|
31
|
+
|
|
32
|
+
# Decode a message from an image
|
|
33
|
+
message = decode_lsb("stego_image.png")
|
|
34
|
+
print(message) # Output: secret message
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
## Usage Examples
|
|
38
|
+
|
|
39
|
+
### Basic Encoding/Decoding
|
|
40
|
+
|
|
41
|
+
```python
|
|
42
|
+
from ghostbit import encode_lsb, decode_lsb
|
|
43
|
+
|
|
44
|
+
# Encode a secret message
|
|
45
|
+
encode_lsb("input.png", "My secret password", "output.png")
|
|
46
|
+
|
|
47
|
+
# Decode the message
|
|
48
|
+
secret = decode_lsb("output.png")
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
## API Reference
|
|
53
|
+
|
|
54
|
+
### Steganography Functions
|
|
55
|
+
|
|
56
|
+
- `encode_lsb(img_path, message, output_path)` - Encode message using LSB
|
|
57
|
+
- `decode_lsb(img_path)` - Decode message using LSB
|
|
58
|
+
- `encode_dct(img_path, message, output_path, block_size=8)` - Encode using DCT
|
|
59
|
+
- `decode_dct(img_path, block_size=8)` - Decode using DCT
|
|
60
|
+
- `detect_hidden_data(img_path, method='decode')` - Detect hidden data
|
|
61
|
+
- `calculate_psnr(img1_path, img2_path)` - Calculate PSNR between images
|
|
62
|
+
|
|
63
|
+
## Requirements
|
|
64
|
+
|
|
65
|
+
- Python >= 3.8
|
|
66
|
+
- Pillow >= 10.0.0
|
|
67
|
+
- numpy >= 1.24.0
|
|
68
|
+
- scipy >= 1.10.0
|
|
69
|
+
|
|
70
|
+
## License
|
|
71
|
+
|
|
72
|
+
MIT License
|
|
73
|
+
|
|
74
|
+
## Contributing
|
|
75
|
+
|
|
76
|
+
Contributions are welcome! Please feel free to submit a Pull Request.
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Ghostbit - A Python library for image steganography using LSB encoding.
|
|
3
|
+
|
|
4
|
+
This library provides functions for encoding and decoding secret messages
|
|
5
|
+
in images using Least Significant Bit (LSB) steganography.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from .steganography import (
|
|
9
|
+
encode_lsb,
|
|
10
|
+
decode_lsb,
|
|
11
|
+
encode_dct,
|
|
12
|
+
decode_dct,
|
|
13
|
+
calculate_psnr,
|
|
14
|
+
detect_hidden_data,
|
|
15
|
+
)
|
|
16
|
+
|
|
17
|
+
__version__ = "0.1.0"
|
|
18
|
+
__all__ = [
|
|
19
|
+
# Steganography functions
|
|
20
|
+
"encode_lsb",
|
|
21
|
+
"decode_lsb",
|
|
22
|
+
"encode_dct",
|
|
23
|
+
"decode_dct",
|
|
24
|
+
"calculate_psnr",
|
|
25
|
+
"detect_hidden_data",
|
|
26
|
+
]
|
|
@@ -0,0 +1,371 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Steganography implementation with LSB and DCT methods.
|
|
3
|
+
Supports embedding and extracting secret messages in images.
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
from PIL import Image
|
|
7
|
+
import numpy as np
|
|
8
|
+
from scipy.fft import dct, idct
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def encode_lsb(img_path, message, output_path):
|
|
12
|
+
"""
|
|
13
|
+
Encode a message into an image using LSB (Least Significant Bit) steganography.
|
|
14
|
+
|
|
15
|
+
Args:
|
|
16
|
+
img_path: Path to the cover image
|
|
17
|
+
message: Secret message to embed
|
|
18
|
+
output_path: Path to save the stego-image
|
|
19
|
+
|
|
20
|
+
Raises:
|
|
21
|
+
ValueError: If the image is too small to hold the message
|
|
22
|
+
"""
|
|
23
|
+
img = Image.open(img_path)
|
|
24
|
+
|
|
25
|
+
# Convert to RGB if necessary
|
|
26
|
+
if img.mode != 'RGB':
|
|
27
|
+
img = img.convert('RGB')
|
|
28
|
+
|
|
29
|
+
width, height = img.size
|
|
30
|
+
pixels = np.array(img)
|
|
31
|
+
|
|
32
|
+
# Convert message to binary string
|
|
33
|
+
message += chr(0) # Null delimiter
|
|
34
|
+
binary_message = ''.join(format(ord(c), '08b') for c in message)
|
|
35
|
+
message_length = len(binary_message)
|
|
36
|
+
|
|
37
|
+
# Check if image has enough capacity (3 bits per pixel)
|
|
38
|
+
max_capacity = width * height * 3
|
|
39
|
+
if message_length > max_capacity:
|
|
40
|
+
raise ValueError(f"Message too long. Max capacity: {max_capacity} bits, "
|
|
41
|
+
f"message requires: {message_length} bits")
|
|
42
|
+
|
|
43
|
+
# Embed message bits into LSBs
|
|
44
|
+
bit_index = 0
|
|
45
|
+
for y in range(height):
|
|
46
|
+
for x in range(width):
|
|
47
|
+
for channel in range(3): # RGB channels
|
|
48
|
+
if bit_index < message_length:
|
|
49
|
+
# Clear LSB and set it to message bit
|
|
50
|
+
# Use 0xFE (254) instead of ~1 to avoid signed integer issues with uint8
|
|
51
|
+
pixels[y, x, channel] = (pixels[y, x, channel] & 0xFE) | int(binary_message[bit_index])
|
|
52
|
+
bit_index += 1
|
|
53
|
+
else:
|
|
54
|
+
break
|
|
55
|
+
if bit_index >= message_length:
|
|
56
|
+
break
|
|
57
|
+
if bit_index >= message_length:
|
|
58
|
+
break
|
|
59
|
+
|
|
60
|
+
# Save stego-image
|
|
61
|
+
stego_img = Image.fromarray(pixels)
|
|
62
|
+
stego_img.save(output_path)
|
|
63
|
+
print(f"Message encoded successfully. Saved to {output_path}")
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
def decode_lsb(img_path):
|
|
67
|
+
"""
|
|
68
|
+
Decode a message from an image using LSB steganography.
|
|
69
|
+
|
|
70
|
+
Args:
|
|
71
|
+
img_path: Path to the stego-image
|
|
72
|
+
|
|
73
|
+
Returns:
|
|
74
|
+
Extracted secret message
|
|
75
|
+
"""
|
|
76
|
+
img = Image.open(img_path)
|
|
77
|
+
|
|
78
|
+
# Convert to RGB if necessary
|
|
79
|
+
if img.mode != 'RGB':
|
|
80
|
+
img = img.convert('RGB')
|
|
81
|
+
|
|
82
|
+
width, height = img.size
|
|
83
|
+
pixels = np.array(img)
|
|
84
|
+
|
|
85
|
+
# Extract LSBs
|
|
86
|
+
binary_message = []
|
|
87
|
+
for y in range(height):
|
|
88
|
+
for x in range(width):
|
|
89
|
+
for channel in range(3): # RGB channels
|
|
90
|
+
# Extract LSB
|
|
91
|
+
binary_message.append(str(pixels[y, x, channel] & 1))
|
|
92
|
+
|
|
93
|
+
# Convert binary string to message
|
|
94
|
+
message = []
|
|
95
|
+
for i in range(0, len(binary_message), 8):
|
|
96
|
+
byte = ''.join(binary_message[i:i+8])
|
|
97
|
+
if len(byte) == 8:
|
|
98
|
+
char = chr(int(byte, 2))
|
|
99
|
+
if char == chr(0): # Null delimiter found
|
|
100
|
+
return ''.join(message)
|
|
101
|
+
message.append(char)
|
|
102
|
+
|
|
103
|
+
return ''.join(message)
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
def encode_dct(img_path, message, output_path, block_size=8):
|
|
107
|
+
"""
|
|
108
|
+
Encode a message into an image using DCT (Discrete Cosine Transform) steganography.
|
|
109
|
+
|
|
110
|
+
Args:
|
|
111
|
+
img_path: Path to the cover image
|
|
112
|
+
message: Secret message to embed
|
|
113
|
+
output_path: Path to save the stego-image
|
|
114
|
+
block_size: Size of DCT blocks (default: 8x8)
|
|
115
|
+
|
|
116
|
+
Raises:
|
|
117
|
+
ValueError: If the image is too small to hold the message
|
|
118
|
+
"""
|
|
119
|
+
img = Image.open(img_path)
|
|
120
|
+
|
|
121
|
+
# Convert to RGB if necessary
|
|
122
|
+
if img.mode != 'RGB':
|
|
123
|
+
img = img.convert('RGB')
|
|
124
|
+
|
|
125
|
+
width, height = img.size
|
|
126
|
+
pixels = np.array(img, dtype=np.float64)
|
|
127
|
+
|
|
128
|
+
# Convert message to binary string
|
|
129
|
+
message += chr(0) # Null delimiter
|
|
130
|
+
binary_message = ''.join(format(ord(c), '08b') for c in message)
|
|
131
|
+
message_length = len(binary_message)
|
|
132
|
+
|
|
133
|
+
# Calculate capacity (using middle frequency coefficients)
|
|
134
|
+
num_blocks_x = width // block_size
|
|
135
|
+
num_blocks_y = height // block_size
|
|
136
|
+
# Use 1 coefficient per block per channel (middle frequency position)
|
|
137
|
+
max_capacity = num_blocks_x * num_blocks_y * 3
|
|
138
|
+
|
|
139
|
+
if message_length > max_capacity:
|
|
140
|
+
raise ValueError(f"Message too long. Max capacity: {max_capacity} bits, "
|
|
141
|
+
f"message requires: {message_length} bits")
|
|
142
|
+
|
|
143
|
+
# Process each channel separately
|
|
144
|
+
bit_index = 0
|
|
145
|
+
for channel in range(3):
|
|
146
|
+
channel_data = pixels[:, :, channel]
|
|
147
|
+
|
|
148
|
+
for block_y in range(num_blocks_y):
|
|
149
|
+
for block_x in range(num_blocks_x):
|
|
150
|
+
if bit_index >= message_length:
|
|
151
|
+
break
|
|
152
|
+
|
|
153
|
+
# Extract 8x8 block
|
|
154
|
+
y_start = block_y * block_size
|
|
155
|
+
y_end = y_start + block_size
|
|
156
|
+
x_start = block_x * block_size
|
|
157
|
+
x_end = x_start + block_size
|
|
158
|
+
|
|
159
|
+
block = channel_data[y_start:y_end, x_start:x_end]
|
|
160
|
+
|
|
161
|
+
# Apply DCT
|
|
162
|
+
dct_block = dct(dct(block, axis=0, norm='ortho'), axis=1, norm='ortho')
|
|
163
|
+
|
|
164
|
+
# Embed bit in middle frequency coefficient (position 3,3)
|
|
165
|
+
embed_pos = (3, 3)
|
|
166
|
+
if bit_index < message_length:
|
|
167
|
+
bit_value = int(binary_message[bit_index])
|
|
168
|
+
# Quantize and embed: if bit is 1, make quantized coefficient odd; if 0, make even
|
|
169
|
+
coeff = dct_block[embed_pos]
|
|
170
|
+
quantized = int(np.round(coeff))
|
|
171
|
+
# Adjust to match desired bit value
|
|
172
|
+
if (quantized % 2) != bit_value:
|
|
173
|
+
quantized = quantized + 1 if quantized >= 0 else quantized - 1
|
|
174
|
+
dct_block[embed_pos] = quantized
|
|
175
|
+
bit_index += 1
|
|
176
|
+
|
|
177
|
+
# Apply inverse DCT
|
|
178
|
+
idct_block = idct(idct(dct_block, axis=0, norm='ortho'), axis=1, norm='ortho')
|
|
179
|
+
|
|
180
|
+
# Update channel data
|
|
181
|
+
channel_data[y_start:y_end, x_start:x_end] = idct_block
|
|
182
|
+
|
|
183
|
+
if bit_index >= message_length:
|
|
184
|
+
break
|
|
185
|
+
|
|
186
|
+
pixels[:, :, channel] = channel_data
|
|
187
|
+
|
|
188
|
+
# Clip values to valid range and convert back to uint8
|
|
189
|
+
pixels = np.clip(pixels, 0, 255).astype(np.uint8)
|
|
190
|
+
|
|
191
|
+
# Save stego-image
|
|
192
|
+
stego_img = Image.fromarray(pixels)
|
|
193
|
+
stego_img.save(output_path)
|
|
194
|
+
print(f"Message encoded successfully using DCT. Saved to {output_path}")
|
|
195
|
+
|
|
196
|
+
|
|
197
|
+
def decode_dct(img_path, block_size=8):
|
|
198
|
+
"""
|
|
199
|
+
Decode a message from an image using DCT steganography.
|
|
200
|
+
|
|
201
|
+
Args:
|
|
202
|
+
img_path: Path to the stego-image
|
|
203
|
+
block_size: Size of DCT blocks (default: 8x8)
|
|
204
|
+
|
|
205
|
+
Returns:
|
|
206
|
+
Extracted secret message
|
|
207
|
+
"""
|
|
208
|
+
img = Image.open(img_path)
|
|
209
|
+
|
|
210
|
+
# Convert to RGB if necessary
|
|
211
|
+
if img.mode != 'RGB':
|
|
212
|
+
img = img.convert('RGB')
|
|
213
|
+
|
|
214
|
+
width, height = img.size
|
|
215
|
+
pixels = np.array(img, dtype=np.float64)
|
|
216
|
+
|
|
217
|
+
# Extract bits from DCT coefficients
|
|
218
|
+
binary_message = []
|
|
219
|
+
num_blocks_x = width // block_size
|
|
220
|
+
num_blocks_y = height // block_size
|
|
221
|
+
|
|
222
|
+
for channel in range(3):
|
|
223
|
+
channel_data = pixels[:, :, channel]
|
|
224
|
+
|
|
225
|
+
for block_y in range(num_blocks_y):
|
|
226
|
+
for block_x in range(num_blocks_x):
|
|
227
|
+
# Extract 8x8 block
|
|
228
|
+
y_start = block_y * block_size
|
|
229
|
+
y_end = y_start + block_size
|
|
230
|
+
x_start = block_x * block_size
|
|
231
|
+
x_end = x_start + block_size
|
|
232
|
+
|
|
233
|
+
block = channel_data[y_start:y_end, x_start:x_end]
|
|
234
|
+
|
|
235
|
+
# Apply DCT
|
|
236
|
+
dct_block = dct(dct(block, axis=0, norm='ortho'), axis=1, norm='ortho')
|
|
237
|
+
|
|
238
|
+
# Extract bit from middle frequency coefficient (position 3,3)
|
|
239
|
+
embed_pos = (3, 3)
|
|
240
|
+
coeff = dct_block[embed_pos]
|
|
241
|
+
# Extract LSB of quantized coefficient
|
|
242
|
+
bit_value = int(np.round(coeff)) % 2
|
|
243
|
+
binary_message.append(str(bit_value))
|
|
244
|
+
|
|
245
|
+
# Convert binary string to message
|
|
246
|
+
message = []
|
|
247
|
+
for i in range(0, len(binary_message), 8):
|
|
248
|
+
byte = ''.join(binary_message[i:i+8])
|
|
249
|
+
if len(byte) == 8:
|
|
250
|
+
char = chr(int(byte, 2))
|
|
251
|
+
if char == chr(0): # Null delimiter found
|
|
252
|
+
return ''.join(message)
|
|
253
|
+
message.append(char)
|
|
254
|
+
|
|
255
|
+
return ''.join(message)
|
|
256
|
+
|
|
257
|
+
|
|
258
|
+
def calculate_psnr(img1_path, img2_path):
|
|
259
|
+
"""
|
|
260
|
+
Calculate PSNR (Peak Signal-to-Noise Ratio) between two images.
|
|
261
|
+
|
|
262
|
+
Args:
|
|
263
|
+
img1_path: Path to first image
|
|
264
|
+
img2_path: Path to second image
|
|
265
|
+
|
|
266
|
+
Returns:
|
|
267
|
+
PSNR value in dB
|
|
268
|
+
"""
|
|
269
|
+
img1 = np.array(Image.open(img1_path).convert('RGB'))
|
|
270
|
+
img2 = np.array(Image.open(img2_path).convert('RGB'))
|
|
271
|
+
|
|
272
|
+
mse = np.mean((img1 - img2) ** 2)
|
|
273
|
+
if mse == 0:
|
|
274
|
+
return float('inf')
|
|
275
|
+
|
|
276
|
+
max_pixel = 255.0
|
|
277
|
+
psnr = 20 * np.log10(max_pixel / np.sqrt(mse))
|
|
278
|
+
return psnr
|
|
279
|
+
|
|
280
|
+
|
|
281
|
+
def detect_hidden_data(img_path, method='decode'):
|
|
282
|
+
"""
|
|
283
|
+
Detect whether an image contains hidden data using LSB steganography.
|
|
284
|
+
|
|
285
|
+
Args:
|
|
286
|
+
img_path: Path to the image to analyze
|
|
287
|
+
method: Detection method to use:
|
|
288
|
+
- 'decode': Try to decode and check for null-terminated message (fast, reliable)
|
|
289
|
+
- 'statistical': Use statistical analysis of LSB distribution
|
|
290
|
+
- 'both': Use both methods and return combined result
|
|
291
|
+
|
|
292
|
+
Returns:
|
|
293
|
+
dict with keys:
|
|
294
|
+
- has_hidden_data: bool indicating if hidden data was detected
|
|
295
|
+
- confidence: float between 0.0 and 1.0 indicating confidence level
|
|
296
|
+
- method_used: str indicating which method(s) were used
|
|
297
|
+
- decoded_message: str if decode method found a message (None otherwise)
|
|
298
|
+
- message_length: int length of decoded message if found (None otherwise)
|
|
299
|
+
"""
|
|
300
|
+
img = Image.open(img_path)
|
|
301
|
+
|
|
302
|
+
# Convert to RGB if necessary
|
|
303
|
+
if img.mode != 'RGB':
|
|
304
|
+
img = img.convert('RGB')
|
|
305
|
+
|
|
306
|
+
width, height = img.size
|
|
307
|
+
pixels = np.array(img)
|
|
308
|
+
|
|
309
|
+
result = {
|
|
310
|
+
'has_hidden_data': False,
|
|
311
|
+
'confidence': 0.0,
|
|
312
|
+
'method_used': method,
|
|
313
|
+
'decoded_message': None,
|
|
314
|
+
'message_length': None
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
# Method 1: Try to decode and check for null-terminated message
|
|
318
|
+
if method in ('decode', 'both'):
|
|
319
|
+
try:
|
|
320
|
+
decoded = decode_lsb(img_path)
|
|
321
|
+
if decoded: # Found a non-empty null-terminated message
|
|
322
|
+
result['has_hidden_data'] = True
|
|
323
|
+
result['confidence'] = 0.95 # High confidence if decode succeeds
|
|
324
|
+
result['decoded_message'] = decoded
|
|
325
|
+
result['message_length'] = len(decoded)
|
|
326
|
+
if method == 'decode':
|
|
327
|
+
return result
|
|
328
|
+
except Exception:
|
|
329
|
+
pass # Decode failed, continue with other methods
|
|
330
|
+
|
|
331
|
+
# Method 2: Statistical analysis of LSB distribution
|
|
332
|
+
if method in ('statistical', 'both'):
|
|
333
|
+
# Extract all LSBs
|
|
334
|
+
lsbs = pixels & 1
|
|
335
|
+
total_bits = width * height * 3
|
|
336
|
+
|
|
337
|
+
# Count 0s and 1s in LSBs
|
|
338
|
+
zeros = np.sum(lsbs == 0)
|
|
339
|
+
ones = np.sum(lsbs == 1)
|
|
340
|
+
|
|
341
|
+
# In natural images, LSBs should be roughly 50/50 (random)
|
|
342
|
+
# If there's hidden data, we might see patterns
|
|
343
|
+
# Check if distribution is significantly skewed
|
|
344
|
+
expected = total_bits / 2
|
|
345
|
+
if total_bits > 0:
|
|
346
|
+
deviation = abs(zeros - expected) / expected
|
|
347
|
+
|
|
348
|
+
# Also check for patterns: consecutive same bits might indicate data
|
|
349
|
+
# Sample a subset to check for patterns
|
|
350
|
+
sample_size = min(10000, total_bits)
|
|
351
|
+
sample_indices = np.random.choice(total_bits, sample_size, replace=False)
|
|
352
|
+
sample_lsbs = lsbs.flatten()[sample_indices]
|
|
353
|
+
|
|
354
|
+
# Count transitions (0->1 or 1->0)
|
|
355
|
+
transitions = np.sum(np.diff(sample_lsbs) != 0)
|
|
356
|
+
transition_ratio = transitions / (sample_size - 1) if sample_size > 1 else 0
|
|
357
|
+
|
|
358
|
+
# Natural images have high transition ratio (~0.5)
|
|
359
|
+
# Hidden data might have lower transition ratio if it's structured
|
|
360
|
+
# But this is not definitive, so we use low confidence
|
|
361
|
+
|
|
362
|
+
# If deviation is very high or transition ratio is very low, might indicate data
|
|
363
|
+
if deviation > 0.1 or transition_ratio < 0.3:
|
|
364
|
+
if method == 'statistical':
|
|
365
|
+
result['has_hidden_data'] = True
|
|
366
|
+
result['confidence'] = 0.3 # Low confidence for statistical method
|
|
367
|
+
elif method == 'both' and not result['has_hidden_data']:
|
|
368
|
+
result['has_hidden_data'] = True
|
|
369
|
+
result['confidence'] = 0.4 # Medium confidence when decode failed but stats suggest data
|
|
370
|
+
|
|
371
|
+
return result
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: ghostbit
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: A Python library for image steganography using LSB encoding
|
|
5
|
+
Home-page: https://github.com/yourusername/ghostbit-py
|
|
6
|
+
Author: Ghostbit
|
|
7
|
+
Author-email:
|
|
8
|
+
License: MIT
|
|
9
|
+
Classifier: Development Status :: 3 - Alpha
|
|
10
|
+
Classifier: Intended Audience :: Developers
|
|
11
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
12
|
+
Classifier: Programming Language :: Python :: 3
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.8
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
17
|
+
Classifier: Topic :: Security :: Cryptography
|
|
18
|
+
Classifier: Topic :: Multimedia :: Graphics
|
|
19
|
+
Requires-Python: >=3.8
|
|
20
|
+
Description-Content-Type: text/markdown
|
|
21
|
+
Requires-Dist: Pillow>=10.0.0
|
|
22
|
+
Requires-Dist: numpy>=1.24.0
|
|
23
|
+
Requires-Dist: scipy>=1.10.0
|
|
24
|
+
Provides-Extra: dev
|
|
25
|
+
Requires-Dist: pytest>=7.0.0; extra == "dev"
|
|
26
|
+
Requires-Dist: pytest-cov>=4.0.0; extra == "dev"
|
|
27
|
+
Requires-Dist: black>=23.0.0; extra == "dev"
|
|
28
|
+
Requires-Dist: flake8>=6.0.0; extra == "dev"
|
|
29
|
+
Dynamic: home-page
|
|
30
|
+
Dynamic: requires-python
|
|
31
|
+
|
|
32
|
+
# Ghostbit
|
|
33
|
+
|
|
34
|
+
A Python library for image steganography using LSB (Least Significant Bit) encoding.
|
|
35
|
+
|
|
36
|
+
## Features
|
|
37
|
+
|
|
38
|
+
- **LSB Steganography**: Encode and decode secret messages in images using the Least Significant Bit method
|
|
39
|
+
- **DCT Steganography**: Alternative encoding method using Discrete Cosine Transform
|
|
40
|
+
- **Image Analysis**: Detect hidden data and calculate image quality metrics
|
|
41
|
+
|
|
42
|
+
## Installation
|
|
43
|
+
|
|
44
|
+
```bash
|
|
45
|
+
pip install ghostbit
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
Or install from source:
|
|
49
|
+
|
|
50
|
+
```bash
|
|
51
|
+
cd ghostbit-py
|
|
52
|
+
pip install -e .
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
## Quick Start
|
|
56
|
+
|
|
57
|
+
```python
|
|
58
|
+
from ghostbit import encode_lsb, decode_lsb
|
|
59
|
+
|
|
60
|
+
# Encode a message into an image
|
|
61
|
+
encode_lsb("cover_image.png", "secret message", "stego_image.png")
|
|
62
|
+
|
|
63
|
+
# Decode a message from an image
|
|
64
|
+
message = decode_lsb("stego_image.png")
|
|
65
|
+
print(message) # Output: secret message
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
## Usage Examples
|
|
69
|
+
|
|
70
|
+
### Basic Encoding/Decoding
|
|
71
|
+
|
|
72
|
+
```python
|
|
73
|
+
from ghostbit import encode_lsb, decode_lsb
|
|
74
|
+
|
|
75
|
+
# Encode a secret message
|
|
76
|
+
encode_lsb("input.png", "My secret password", "output.png")
|
|
77
|
+
|
|
78
|
+
# Decode the message
|
|
79
|
+
secret = decode_lsb("output.png")
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
## API Reference
|
|
84
|
+
|
|
85
|
+
### Steganography Functions
|
|
86
|
+
|
|
87
|
+
- `encode_lsb(img_path, message, output_path)` - Encode message using LSB
|
|
88
|
+
- `decode_lsb(img_path)` - Decode message using LSB
|
|
89
|
+
- `encode_dct(img_path, message, output_path, block_size=8)` - Encode using DCT
|
|
90
|
+
- `decode_dct(img_path, block_size=8)` - Decode using DCT
|
|
91
|
+
- `detect_hidden_data(img_path, method='decode')` - Detect hidden data
|
|
92
|
+
- `calculate_psnr(img1_path, img2_path)` - Calculate PSNR between images
|
|
93
|
+
|
|
94
|
+
## Requirements
|
|
95
|
+
|
|
96
|
+
- Python >= 3.8
|
|
97
|
+
- Pillow >= 10.0.0
|
|
98
|
+
- numpy >= 1.24.0
|
|
99
|
+
- scipy >= 1.10.0
|
|
100
|
+
|
|
101
|
+
## License
|
|
102
|
+
|
|
103
|
+
MIT License
|
|
104
|
+
|
|
105
|
+
## Contributing
|
|
106
|
+
|
|
107
|
+
Contributions are welcome! Please feel free to submit a Pull Request.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
ghostbit
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=61.0", "wheel"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "ghostbit"
|
|
7
|
+
version = "0.1.0"
|
|
8
|
+
description = "A Python library for image steganography using LSB encoding"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.8"
|
|
11
|
+
license = {text = "MIT"}
|
|
12
|
+
authors = [
|
|
13
|
+
{name = "Ghostbit"}
|
|
14
|
+
]
|
|
15
|
+
classifiers = [
|
|
16
|
+
"Development Status :: 3 - Alpha",
|
|
17
|
+
"Intended Audience :: Developers",
|
|
18
|
+
"License :: OSI Approved :: MIT License",
|
|
19
|
+
"Programming Language :: Python :: 3",
|
|
20
|
+
"Programming Language :: Python :: 3.8",
|
|
21
|
+
"Programming Language :: Python :: 3.9",
|
|
22
|
+
"Programming Language :: Python :: 3.10",
|
|
23
|
+
"Programming Language :: Python :: 3.11",
|
|
24
|
+
"Topic :: Security :: Cryptography",
|
|
25
|
+
"Topic :: Multimedia :: Graphics",
|
|
26
|
+
]
|
|
27
|
+
dependencies = [
|
|
28
|
+
"Pillow>=10.0.0",
|
|
29
|
+
"numpy>=1.24.0",
|
|
30
|
+
"scipy>=1.10.0",
|
|
31
|
+
]
|
|
32
|
+
|
|
33
|
+
[project.optional-dependencies]
|
|
34
|
+
dev = [
|
|
35
|
+
"pytest>=7.0.0",
|
|
36
|
+
"pytest-cov>=4.0.0",
|
|
37
|
+
"black>=23.0.0",
|
|
38
|
+
"flake8>=6.0.0",
|
|
39
|
+
]
|
ghostbit-0.1.0/setup.cfg
ADDED
ghostbit-0.1.0/setup.py
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Setup configuration for Ghostbit Python library.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from setuptools import setup, find_packages
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
|
|
8
|
+
# Read the README file
|
|
9
|
+
readme_file = Path(__file__).parent / "README.md"
|
|
10
|
+
long_description = readme_file.read_text() if readme_file.exists() else ""
|
|
11
|
+
|
|
12
|
+
setup(
|
|
13
|
+
name="ghostbit",
|
|
14
|
+
version="0.1.0",
|
|
15
|
+
description="A Python library for image steganography using LSB encoding",
|
|
16
|
+
long_description=long_description,
|
|
17
|
+
long_description_content_type="text/markdown",
|
|
18
|
+
author="Ghostbit",
|
|
19
|
+
author_email="",
|
|
20
|
+
url="https://github.com/yourusername/ghostbit-py",
|
|
21
|
+
packages=find_packages(),
|
|
22
|
+
classifiers=[
|
|
23
|
+
"Development Status :: 3 - Alpha",
|
|
24
|
+
"Intended Audience :: Developers",
|
|
25
|
+
"License :: OSI Approved :: MIT License",
|
|
26
|
+
"Programming Language :: Python :: 3",
|
|
27
|
+
"Programming Language :: Python :: 3.8",
|
|
28
|
+
"Programming Language :: Python :: 3.9",
|
|
29
|
+
"Programming Language :: Python :: 3.10",
|
|
30
|
+
"Programming Language :: Python :: 3.11",
|
|
31
|
+
"Topic :: Security :: Cryptography",
|
|
32
|
+
"Topic :: Multimedia :: Graphics",
|
|
33
|
+
],
|
|
34
|
+
python_requires=">=3.8",
|
|
35
|
+
install_requires=[
|
|
36
|
+
"Pillow>=10.0.0",
|
|
37
|
+
"numpy>=1.24.0",
|
|
38
|
+
"scipy>=1.10.0",
|
|
39
|
+
],
|
|
40
|
+
extras_require={
|
|
41
|
+
"dev": [
|
|
42
|
+
"pytest>=7.0.0",
|
|
43
|
+
"pytest-cov>=4.0.0",
|
|
44
|
+
"black>=23.0.0",
|
|
45
|
+
"flake8>=6.0.0",
|
|
46
|
+
],
|
|
47
|
+
},
|
|
48
|
+
)
|