Concelogram: obraz w obrazie z wykorzystaniem LSB
Jest to rodzaj steganografii, polegająca na ukryciu obrazu, zdjęcia, grafiki. Metoda ta była szeroko wykorzystywana w historii przez szpiegów, którzy ukrywali zdjęcia i mikro filmy w przeróżnych miejscach. Najbardziej znaną metodą jest metoda mikrokropek, polega na ukrycie zminiaturyzowanych danych takich jak tekst, fotografie lub rysunki techniczne w punkcie o średnicy 1 mm. Skala miniaturyzacji wynosiła 1:300. Mikrokropkę wykonuje się przy użyciu urządzenia będącym połączeniem aparatu fotograficznego i mikroskopu. Co ciekawe mikrokropek nadal używa się do zabezpieczenia i znakowania cennych przedmiotów, żetonów, a nawet samochodów. Tak jak mikrokropka concelogram powoduje, że obraz oglądany nieuzbrojonym okiem, nie budzi żadnych podejrzeń. Najłatwiejszą metodą ukrycia obrazu wewnątrz innego obrazu jest wykorzystanie LSB(czym jest LSB, dowiecie się z tego artykułu). Jeśli wiesz już, czym jest LSB, to możemy zaczynać. Jak już zapewne wiesz, piksel przyjmuje trzy wartości, każdą od 0 do 255 co w reprezentacji binarnej reprezentuje 0-11111111. (Można użyć czwartej wartości, którą jest kanał Alfa) W naszym ćwiczeniu użyjemy 4-3 LSB. Każde wykorzystanie LSB na użytek ukrytego obrazka będzie pogarszać jego jakość. Wygląda to następująco:
Jeśli chcecie przeanalizować sobie obrazki zachęcam do ich pobrania: zerowany kotek
Imports System.Windows.Forms
Imports System.Drawing
Module Module1
Dim lokalizacjaPlikuGraficznego As String
Sub Main()
Dim open_dialog As New OpenFileDialog()
open_dialog.InitialDirectory = "c:\"
open_dialog.Filter = "Image Files (*.jpeg; *.png; *.bmp)|*.jpg; *.png; *.bmp"
If open_dialog.ShowDialog() = System.Windows.Forms.DialogResult.OK Then
lokalizacjaPlikuGraficznego = open_dialog.FileName
Dim bmp As New Bitmap(lokalizacjaPlikuGraficznego)
Console.WriteLine("Ile ostatnich bitów chcesz wyzerować")
Dim zerowanie As Integer = 0
Do
Dim test As String = Console.ReadLine()
If IsNumeric(test) Then
If test >= 1 And test <= 7 Then
zerowanie = test
Exit Do
Else
Console.WriteLine("możesz wybrać tylko cyfrę od 1 do 7.")
End If
Else
Console.WriteLine("możesz wybrać tylko cyfrę od 1 do 7.")
End If
Loop
zerowanie = 2 ^ zerowanie
Dim R As Integer = 0, G As Integer = 0, B As Integer = 0
Dim xmax As Integer = bmp.Width - 1
Dim ymax As Integer = bmp.Height - 1
Dim pixelElementIndex As Long = 0
For y = 0 To ymax
For x = 0 To xmax
With bmp.GetPixel(x, y)
R = .R - .R Mod zerowanie
G = .G - .G Mod zerowanie
B = .B - .B Mod zerowanie
End With
bmp.SetPixel(x, y, Color.FromArgb(R, G, B))
Next x
Next y
bmp.Save("C:\Users\piotr\Desktop\" + _
IO.Path.GetFileNameWithoutExtension(lokalizacjaPlikuGraficznego).ToString + _
" (wyzerowano " + Math.Log(zerowanie, 2).ToString + " ostatnich bitów).bmp" _
, System.Drawing.Imaging.ImageFormat.Bmp)
Console.WriteLine("Bity zostały wyzerowane!")
Console.ReadLine()
End If
End Sub
End Module
Wykorzystamy sobie cztery ostatnie bity do ukrycia naszego obrazka. Oczywiście nie będzie to takie proste, musimy zachować pewne zasady. Ważną decyzją będzie czy nasz plik graficzny będzie miał obniżoną jakość, czy nie. Jeśli obniżymy mu jakość, będziemy mogli zapisać piksel w pikselu, jeśli postanowimy zachować jakość, ukrytego obrazka wtedy będziemy musieli zapisać jeden piksel ukrytego obrazka w dwóch pikselach obrazka kontenera. Zaczniemy od pierwszego przykładu, zapiszemy obrazek w obrazku, z tym że obrazek ukryty będzie miał obniżoną jakość. Oczywiste będzie, że obrazek ukrywający musi być większej lub równej wielkości niż obrazek ukrywany:
Należy teraz wykorzystać kod powyżej i wpleść algorytm dodania obrazka po zerowaniu bitów. Wyzwaniem zostaje dodanie końca wiersza pikseli. Najprościej będzie wypełnić puste pola zerami, po wydobyciu obrazka ze źródła, będzie on miał taką samą wielkość jak obrazek bazowy. Jak będzie wyglądał taki algorytm:
Jeśli czytaliście poprzednie artykuły, stworzenie takiego algorytmu, nie powinno przysporzyć wam problemów:
Imports System.Windows.Forms
Imports System.Drawing
Module Module1
Dim lokalizacjaPlikuGraficznego As String
Dim lokalizacjaPlikuUkrywanego As String
Sub Main()
Dim open_dialog As New OpenFileDialog()
Dim open_dialog_ukrywany As New OpenFileDialog()
open_dialog.InitialDirectory = "c:\"
open_dialog.Filter = "Image Files (*.jpeg; *.png; *.bmp)|*.jpg; *.png; *.bmp"
open_dialog.Title = "Wybierz obrazek w którym ukryjesz obrazek"
If open_dialog.ShowDialog() = System.Windows.Forms.DialogResult.OK Then
lokalizacjaPlikuGraficznego = open_dialog.FileName
Dim kontener As New Bitmap(lokalizacjaPlikuGraficznego)
open_dialog_ukrywany.InitialDirectory = "c:\"
open_dialog_ukrywany.Filter = "Image Files (*.jpeg; *.png; *.bmp)|*.jpg; *.png; *.bmp"
open_dialog_ukrywany.Title = "Wybierz obrazek który ukryjesz"
If open_dialog_ukrywany.ShowDialog() = System.Windows.Forms.DialogResult.OK Then
lokalizacjaPlikuUkrywanego = open_dialog_ukrywany.FileName
Dim ukryty As New Bitmap(lokalizacjaPlikuUkrywanego)
If kontener.Width < ukryty.Width Or kontener.Height < ukryty.Height Then
Console.WriteLine("Obrazek ukrywany jest większy niż obrazek bazowy, wybierz mniejszy obrazek.")
Console.ReadLine()
Else
Dim R As Integer = 0, G As Integer = 0, B As Integer = 0
Dim xmax As Integer = kontener.Width - 1
Dim ymax As Integer = kontener.Height - 1
Dim pixelElementIndex As Long = 0
For y = 0 To ymax
For x = 0 To xmax
With kontener.GetPixel(x, y)
R = .R - .R Mod (2 ^ 4)
G = .G - .G Mod (2 ^ 4)
B = .B - .B Mod (2 ^ 4)
End With
If x < ukryty.Width And y < ukryty.Height Then
With ukryty.GetPixel(x, y)
R += OdwrocBity(.R - .R Mod (2 ^ 4))
G += OdwrocBity(.G - .G Mod (2 ^ 4))
B += OdwrocBity(.B - .B Mod (2 ^ 4))
End With
Else
'15 rozjaśni, 0 -przyciemni
R += 15
G += 15
B += 15
End If
kontener.SetPixel(x, y, Color.FromArgb(R, G, B))
Next x
Next y
kontener.Save("C:\Users\Piotrek\Desktop\" + _
IO.Path.GetFileNameWithoutExtension(lokalizacjaPlikuGraficznego).ToString + _
".bmp", System.Drawing.Imaging.ImageFormat.Bmp)
Console.WriteLine("Bity zostały ukryte!")
Console.ReadLine()
End If
End If
End If
End Sub
Private Function OdwrocBity(ByVal w As Integer) As Integer
Dim rezultat As Integer = 0
For i As Integer = 0 To 7
rezultat = rezultat * 2 + (w Mod 2)
w = Math.Floor(w / 2)
Next
Return rezultat
End Function
End Module
Efekt działania algorytmu można podziwiać poniżej:
Na pierwszy rzut oka obrazek wygląda jak słabej jakości zdjęcie :P, ale niech to was nie zwiedzie. Po użyciu kodu cofającego nasze zmiany ukaże się nam obrazek ukryty wewnątrz kotka:
Imports System.Windows.Forms
Imports System.Drawing
Module Module1
Dim lokalizacjaPlikuGraficznego As String
Sub Main()
Dim open_dialog As New OpenFileDialog()
open_dialog.InitialDirectory = "c:\"
open_dialog.Filter = "Image Files (*.jpeg; *.png; *.bmp)|*.jpg; *.png; *.bmp"
open_dialog.Title = "Wybierz obrazek w którym ukryto obrazek"
If open_dialog.ShowDialog() = System.Windows.Forms.DialogResult.OK Then
lokalizacjaPlikuGraficznego = open_dialog.FileName
Dim kontener As New Bitmap(lokalizacjaPlikuGraficznego)
Dim ukryty As New Bitmap(kontener.Width, kontener.Height)
Dim R As Integer = 0, G As Integer = 0, B As Integer = 0
Dim xmax As Integer = kontener.Width - 1
Dim ymax As Integer = kontener.Height - 1
Dim pixelElementIndex As Long = 0
For y = 0 To ymax
For x = 0 To xmax
With kontener.GetPixel(x, y)
R = .R Mod (2 ^ 4)
G = .G Mod (2 ^ 4)
B = .B Mod (2 ^ 4)
End With
R = OdwrocBity(R)
G = OdwrocBity(G)
B = OdwrocBity(B)
ukryty.SetPixel(x, y, Color.FromArgb(R, G, B))
Next x
Next y
ukryty.Save("C:\Users\Piotrek\Desktop\" + _
IO.Path.GetFileNameWithoutExtension(lokalizacjaPlikuGraficznego).ToString + _
"(odzyskany).bmp", System.Drawing.Imaging.ImageFormat.Bmp)
Console.WriteLine("Bity zostały odzyskane!")
Console.ReadLine()
End If
End Sub
Private Function OdwrocBity(ByVal w As Integer) As Integer
Dim rezultat As Integer = 0
For i As Integer = 0 To 7
rezultat = rezultat * 2 + (w Mod 2)
w = Math.Floor(w / 2)
Next
Return rezultat
End Function
End Module
Efekt działania algorytmu:
Jeśli zamiast 15 pozostawilibyśmy wyzerowane bity, wtedy przestrzeń większa niż szerokość i wysokość obrazka ukrytego byłaby czarna. Jak działa algorytm:
Oczywiście dużo lepsze efekty otrzymamy po wykorzystaniu tylko dwóch ostatnich bitów. Również natrafimy na ograniczenia, najważniejsze pytanie, jakie musimy sobie zadać to takie czy chcemy obniżać jakość naszego obrazka ukrywanego, sami widzicie, że użycie tylko czterech pierwszych bitów do utworzenia obrazka nie obniża aż tak znacząco jego jakości. Drugim ograniczeniem jest wielkość naszego obrazka, jeśli użyjemy dwóch ostatnich bitów kontenera, wtedy nasz obrazek będzie musiał być co najmniej czterokrotnie większy od obrazka ukrywanego (bez straty jakości). Mamy dwa sposoby, aby ukryć taki piksel. Możemy ukrywać wartości piksela ciągiem, tak jak to miało miejsce w przypadku ukrywaniu tekstu lub ukrywać kolor w kolorze (zielone tylko w zielonych, czerwone w czerwonych itd.)
- Ukrywanie koloru w kolorze:
Imports System.Windows.Forms
Imports System.Drawing
Module Module1
Dim lokalizacjaPlikuGraficznego As String
Dim lokalizacjaPlikuUkrywanego As String
Sub Main()
Dim open_dialog As New OpenFileDialog()
Dim open_dialog_ukrywany As New OpenFileDialog()
open_dialog.InitialDirectory = "c:\"
open_dialog.Filter = "Image Files (*.jpeg; *.png; *.bmp)|*.jpg; *.png; *.bmp"
open_dialog.Title = "Wybierz obrazek w którym ukryjesz obrazek"
If open_dialog.ShowDialog() = System.Windows.Forms.DialogResult.OK Then
lokalizacjaPlikuGraficznego = open_dialog.FileName
Dim kontener As New Bitmap(lokalizacjaPlikuGraficznego)
open_dialog_ukrywany.InitialDirectory = "c:\"
open_dialog_ukrywany.Filter = "Image Files (*.jpeg; *.png; *.bmp)|*.jpg; *.png; *.bmp"
open_dialog_ukrywany.Title = "Wybierz obrazek który ukryjesz"
If open_dialog_ukrywany.ShowDialog() = System.Windows.Forms.DialogResult.OK Then
lokalizacjaPlikuUkrywanego = open_dialog_ukrywany.FileName
Dim ukryty As New Bitmap(lokalizacjaPlikuUkrywanego)
If kontener.Width < 4 * ukryty.Width Or kontener.Height < ukryty.Height Then
Console.WriteLine("Obrazek ukrywany jest większy niż obrazek bazowy, wybierz mniejszy obrazek.")
Console.ReadLine()
Else
Dim R As Integer = 0, G As Integer = 0, B As Integer = 0
Dim R_ukryty As Integer = 0, G_ukryty As Integer = 0, B_ukryty As Integer = 0
Dim x_ukryty As Integer = 0
Dim xmax As Integer = kontener.Width - 1
Dim ymax As Integer = kontener.Height - 1
Dim pixelElementIndex As Long = 0
Dim indexkoloru As Integer = 0
For y = 0 To ymax
For x = 0 To xmax
With kontener.GetPixel(x, y)
'zerowanie ostatnich dwóch bitów kontenera
R = .R - .R Mod (2 ^ 2)
G = .G - .G Mod (2 ^ 2)
B = .B - .B Mod (2 ^ 2)
End With
If x < 4 * ukryty.Width And y < ukryty.Height Then
If (x Mod 4) = 0 Then
'pobiera wartości kolorów obrazka ukrywanego
With ukryty.GetPixel(x_ukryty, y)
R_ukryty = .R
G_ukryty = .G
B_ukryty = .B
End With
x_ukryty += 1
End If
R += OdwrocBity2(R_ukryty Mod 2 ^ 2)
R_ukryty = Math.Floor(R_ukryty / 2 ^ 2)
G += OdwrocBity2(G_ukryty Mod 2 ^ 2)
G_ukryty = Math.Floor(G_ukryty / 2 ^ 2)
B += OdwrocBity2(B_ukryty Mod 2 ^ 2)
B_ukryty = Math.Floor(B_ukryty / 2 ^ 2)
Else
'wartość 0 przyciemni piksele, wartość 3 rozjaśni je (opcjonalne)
R += 3
G += 3
B += 3
End If
kontener.SetPixel(x, y, Color.FromArgb(R, G, B))
Next x
x_ukryty = 0
Next y
kontener.Save("C:\Users\piotr\Desktop\" + _
IO.Path.GetFileNameWithoutExtension(lokalizacjaPlikuGraficznego).ToString + _
" ukryty.bmp", System.Drawing.Imaging.ImageFormat.Bmp)
Console.WriteLine("Bity zostały ukryte!")
Console.ReadLine()
End If
End If
End If
End Sub
Private Function OdwrocBity2(ByVal w As Integer) As Integer
Dim rezultat As Integer = 0
For i As Integer = 0 To 1
rezultat = rezultat * 2 + (w Mod 2)
w = Math.Floor(w / 2)
Next
Return rezultat
End Function
End Module
Odzyskanie takich bitów nie stanowi problemu:
Imports System.Windows.Forms
Imports System.Drawing
Module Module1
Dim lokalizacjaPlikuGraficznego As String
Sub Main()
Dim open_dialog As New OpenFileDialog()
open_dialog.InitialDirectory = "c:\"
open_dialog.Filter = "Image Files (*.jpeg; *.png; *.bmp)|*.jpg; *.png; *.bmp"
open_dialog.Title = "Wybierz obrazek w którym ukryto obrazek"
If open_dialog.ShowDialog() = System.Windows.Forms.DialogResult.OK Then
lokalizacjaPlikuGraficznego = open_dialog.FileName
Dim kontener As New Bitmap(lokalizacjaPlikuGraficznego)
Dim szerokosc As Integer = Math.Floor(kontener.Width / 4)
Dim ukryty As New Bitmap(szerokosc, kontener.Height)
Dim R As Integer = 0, G As Integer = 0, B As Integer = 0
Dim x_ukryty As Integer = 0
Dim xmax As Integer = kontener.Width - 1
Dim ymax As Integer = kontener.Height - 1
Dim pixelElementIndex As Long = 0
For y = 0 To ymax
For X = 0 To xmax
Dim pix As Color = kontener.GetPixel(X, y)
If (X Mod 4) = 0 Then
If X > 0 Then
ukryty.SetPixel(x_ukryty, y, Color.FromArgb(OdwrocBity(R), OdwrocBity(G), OdwrocBity(B)))
R = 0
G = 0
B = 0
x_ukryty += 1
End If
R = R * 4 + (pix.R Mod 4)
G = G * 4 + (pix.G Mod 4)
B = B * 4 + (pix.B Mod 4)
Else
R = R * 4 + (pix.R Mod 4)
G = G * 4 + (pix.G Mod 4)
B = B * 4 + (pix.B Mod 4)
If X = xmax Then
ukryty.SetPixel(x_ukryty, y, Color.FromArgb(OdwrocBity(R), OdwrocBity(G), _
OdwrocBity(B)))
End If
End If
Next X
x_ukryty = 0
Next y
ukryty.Save("C:\Users\piotr\Desktop\" + _
IO.Path.GetFileNameWithoutExtension(lokalizacjaPlikuGraficznego).ToString + _
"(odzyskany).bmp", System.Drawing.Imaging.ImageFormat.Bmp)
Console.WriteLine("Bity zostały odzyskane!")
Console.ReadLine()
End If
End Sub
Private Function OdwrocBity(ByVal w As Integer) As Integer
Dim rezultat As Integer = 0
For i As Integer = 0 To 7
rezultat = rezultat * 2 + (w Mod 2)
w = Math.Floor(w / 2)
Next
Return rezultat
End Function
End Module
Efekty ukrycia kotka w tapecie znalezionej na dysku:
Tapeta + Kotek = Tapeta-ukryty
Po odzyskaniu bitów z tapeta- ukryty otrzymamy obrazek:
2. Drugim sposobem ukrycia naszego piksela jest ukrycie go ciągiem, bardzo dobrze prezentuje to obrazek poniżej:
Imports System.Windows.Forms
Imports System.Drawing
Module Module1
Dim lokalizacjaPlikuGraficznego As String
Dim lokalizacjaPlikuUkrywanego As String
Sub Main()
Dim open_dialog As New OpenFileDialog()
Dim open_dialog_ukrywany As New OpenFileDialog()
open_dialog.InitialDirectory = "c:\"
open_dialog.Filter = "Image Files (*.jpeg; *.png; *.bmp)|*.jpg; *.png; *.bmp"
open_dialog.Title = "Wybierz obrazek w którym ukryjesz obrazek"
If open_dialog.ShowDialog() = System.Windows.Forms.DialogResult.OK Then
lokalizacjaPlikuGraficznego = open_dialog.FileName
Dim kontener As New Bitmap(lokalizacjaPlikuGraficznego)
open_dialog_ukrywany.InitialDirectory = "c:\"
open_dialog_ukrywany.Filter = "Image Files (*.jpeg; *.png; *.bmp)|*.jpg; *.png; *.bmp"
open_dialog_ukrywany.Title = "Wybierz obrazek który ukryjesz"
If open_dialog_ukrywany.ShowDialog() = System.Windows.Forms.DialogResult.OK Then
lokalizacjaPlikuUkrywanego = open_dialog_ukrywany.FileName
Dim ukryty As New Bitmap(lokalizacjaPlikuUkrywanego)
If kontener.Width < 4 * ukryty.Width Or kontener.Height < ukryty.Height Then
Console.WriteLine("Obrazek ukrywany jest większy niż obrazek bazowy, wybierz mniejszy obrazek.")
Console.ReadLine()
Else
Dim R As Integer = 0, G As Integer = 0, B As Integer = 0
Dim ukrywany As Integer = 0
Dim x_ukryty As Integer = 0
Dim xmax As Integer = kontener.Width - 1
Dim ymax As Integer = kontener.Height - 1
Dim pixelElementIndex As Long = 0
Dim pixelUkrywanyIndex As Long = 0
For y = 0 To ymax
For x = 0 To xmax
With kontener.GetPixel(x, y)
'zerowanie ostatnich dwóch bitów kontenera
R = .R - .R Mod (2 ^ 2)
G = .G - .G Mod (2 ^ 2)
B = .B - .B Mod (2 ^ 2)
End With
If x < 4 * ukryty.Width And y < ukryty.Height Then
For i As Integer = 0 To 2
If (pixelElementIndex Mod 4) = 0 Then
With ukryty.GetPixel(x_ukryty, y)
Select Case pixelUkrywanyIndex
Case 0
ukrywany = .R
pixelUkrywanyIndex = 1
Case 1
ukrywany = .G
pixelUkrywanyIndex = 2
Case 2
ukrywany = .B
pixelUkrywanyIndex = 0
x_ukryty += 1
End Select
End With
End If
Select Case i
Case 0
R += OdwrocBity2(ukrywany Mod 2 ^ 2)
ukrywany = Math.Floor(ukrywany / 2 ^ 2)
Case 1
G += OdwrocBity2(ukrywany Mod 2 ^ 2)
ukrywany = Math.Floor(ukrywany / 2 ^ 2)
Case 2
B += OdwrocBity2(ukrywany Mod 2 ^ 2)
ukrywany = Math.Floor(ukrywany / 2 ^ 2)
kontener.SetPixel(x, y, Color.FromArgb(R, G, B))
End Select
pixelElementIndex += 1
Next
Else
'wartość 0 przyciemni piksele, wartość 3 rozjaśni je (opcjonalne)
R += 3
G += 3
B += 3
kontener.SetPixel(x, y, Color.FromArgb(R, G, B))
End If
Next x
x_ukryty = 0
Next y
kontener.Save("C:\Users\piotr\Desktop\" + _
IO.Path.GetFileNameWithoutExtension(lokalizacjaPlikuGraficznego).ToString + _
" ukryty.bmp", System.Drawing.Imaging.ImageFormat.Bmp)
Console.WriteLine("Bity zostały ukryte!")
Console.ReadLine()
End If
End If
End If
End Sub
Private Function OdwrocBity2(ByVal w As Integer) As Integer
Dim rezultat As Integer = 0
For i As Integer = 0 To 1
rezultat = rezultat * 2 + (w Mod 2)
w = Math.Floor(w / 2)
Next
Return rezultat
End Function
End Module
Algorytm odzyskujący bity z ukrytego obrazka zapisanego bez strat w dwóch ostatnich bitach
Imports System.Windows.Forms
Imports System.Drawing
Module Module1
Dim lokalizacjaPlikuGraficznego As String
Sub Main()
Dim open_dialog As New OpenFileDialog()
open_dialog.InitialDirectory = "c:\"
open_dialog.Filter = "Image Files (*.jpeg; *.png; *.bmp)|*.jpg; *.png; *.bmp"
open_dialog.Title = "Wybierz obrazek w którym ukryto obrazek"
If open_dialog.ShowDialog() = System.Windows.Forms.DialogResult.OK Then
lokalizacjaPlikuGraficznego = open_dialog.FileName
Dim kontener As New Bitmap(lokalizacjaPlikuGraficznego)
Dim szerokosc As Integer = Math.Floor(kontener.Width / 4)
Dim ukryty As New Bitmap(szerokosc, kontener.Height)
Dim R As Integer = 0, G As Integer = 0, B As Integer = 0
Dim x_ukryty As Integer = 0
Dim pixelUkrywanyIndex As Long = 0
Dim ukrywany_kolor As Integer = 0
Dim xmax As Integer = kontener.Width - 1
Dim ymax As Integer = kontener.Height - 1
Dim pixelElementIndex As Long = 0
For y = 0 To ymax
For X = 0 To xmax
Dim pix As Color = kontener.GetPixel(X, y)
For i As Integer = 0 To 2
Select Case i
Case 0
ukrywany_kolor = ukrywany_kolor * 4 + (pix.R Mod 4)
Case 1
ukrywany_kolor = ukrywany_kolor * 4 + (pix.G Mod 4)
Case 2
ukrywany_kolor = ukrywany_kolor * 4 + (pix.B Mod 4)
End Select
pixelElementIndex += 1
If (pixelElementIndex Mod 4) = 0 Then
Select Case pixelUkrywanyIndex
Case 0
R = ukrywany_kolor
ukrywany_kolor = 0
pixelUkrywanyIndex = 1
Case 1
G = ukrywany_kolor
ukrywany_kolor = 0
pixelUkrywanyIndex = 2
Case 2
B = ukrywany_kolor
ukrywany_kolor = 0
pixelUkrywanyIndex = 0
ukryty.SetPixel(x_ukryty, y, Color.FromArgb(OdwrocBity(R), OdwrocBity(G), OdwrocBity(B)))
x_ukryty += 1
End Select
End If
Next
Next X
x_ukryty = 0
Next y
ukryty.Save("C:\Users\piotr\Desktop\" + _
IO.Path.GetFileNameWithoutExtension(lokalizacjaPlikuGraficznego).ToString + _
"(odzyskany).bmp", System.Drawing.Imaging.ImageFormat.Bmp)
Console.WriteLine("Bity zostały odzyskane!")
Console.ReadLine()
End If
End Sub
Private Function OdwrocBity(ByVal w As Integer) As Integer
Dim rezultat As Integer = 0
For i As Integer = 0 To 7
rezultat = rezultat * 2 + (w Mod 2)
w = Math.Floor(w / 2)
Next
Return rezultat
End Function
End Module
Jeśli chcecie sprawdzić działanie algorytmu, możecie odzyskać sobie kotka z pliku: tapeta-z-ukrytym-kotkiem-piksele-ukryte-ciagiem.bmp
Można oczywiście zapisać tylko cztery bity obrazka ukrywanego, spowoduje to zmniejszenie jakości obrazka, ale wykorzystamy tylko 2 piksele kontenera do zapisu jednego piksela obrazka ukrywanego. Łącząc metodę z początku artykułu i metodę zapisu pikseli (kolor w kolor lub ciągiem) można stworzyć algorytm ukrywający obrazek ze stratą ostatnich czterech bitów obrazka ukrywanego. Należy po usunięciu ostatnich czterech bitów koloru ukrywanego po prostu je odwrócić:
Przykład programu do ukrywania obrazka ze stratą czterech ostatnich bitów z wykorzystaniem LSB2
Imports System.Windows.Forms
Imports System.Drawing
Module Module1
Dim lokalizacjaPlikuGraficznego As String
Dim lokalizacjaPlikuUkrywanego As String
Sub Main()
Dim open_dialog As New OpenFileDialog()
Dim open_dialog_ukrywany As New OpenFileDialog()
open_dialog.InitialDirectory = "c:\"
open_dialog.Filter = "Image Files (*.jpeg; *.png; *.bmp)|*.jpg; *.png; *.bmp"
open_dialog.Title = "Wybierz obrazek w którym ukryjesz obrazek"
If open_dialog.ShowDialog() = System.Windows.Forms.DialogResult.OK Then
lokalizacjaPlikuGraficznego = open_dialog.FileName
Dim kontener As New Bitmap(lokalizacjaPlikuGraficznego)
open_dialog_ukrywany.InitialDirectory = "c:\"
open_dialog_ukrywany.Filter = "Image Files (*.jpeg; *.png; *.bmp)|*.jpg; *.png; *.bmp"
open_dialog_ukrywany.Title = "Wybierz obrazek który ukryjesz"
If open_dialog_ukrywany.ShowDialog() = System.Windows.Forms.DialogResult.OK Then
lokalizacjaPlikuUkrywanego = open_dialog_ukrywany.FileName
Dim ukryty As New Bitmap(lokalizacjaPlikuUkrywanego)
If kontener.Width < 2 * ukryty.Width Or kontener.Height < ukryty.Height Then
Console.WriteLine("Obrazek ukrywany jest większy niż obrazek bazowy, wybierz mniejszy obrazek.")
Console.ReadLine()
Else
Dim R As Integer = 0, G As Integer = 0, B As Integer = 0
Dim R_ukryty As Integer = 0, G_ukryty As Integer = 0, B_ukryty As Integer = 0
Dim x_ukryty As Integer = 0
Dim xmax As Integer = kontener.Width - 1
Dim ymax As Integer = kontener.Height - 1
Dim pixelElementIndex As Long = 0
Dim indexkoloru As Integer = 0
For y = 0 To ymax
For x = 0 To xmax
With kontener.GetPixel(x, y)
'zerowanie ostatnich dwóch bitów kontenera
R = .R - .R Mod (2 ^ 2)
G = .G - .G Mod (2 ^ 2)
B = .B - .B Mod (2 ^ 2)
End With
If x < 2 * ukryty.Width And y < ukryty.Height Then
If (x Mod 2) = 0 Then
'pobiera wartości kolorów obrazka ukrywanego
With ukryty.GetPixel(x_ukryty, y)
R_ukryty = OdwrocBity(.R - .R Mod (2 ^ 4))
G_ukryty = OdwrocBity(.G - .G Mod (2 ^ 4))
B_ukryty = OdwrocBity(.B - .B Mod (2 ^ 4))
End With
x_ukryty += 1
End If
R += OdwrocBity2(R_ukryty Mod 2 ^ 2)
R_ukryty = Math.Floor(R_ukryty / 2 ^ 2)
G += OdwrocBity2(G_ukryty Mod 2 ^ 2)
G_ukryty = Math.Floor(G_ukryty / 2 ^ 2)
B += OdwrocBity2(B_ukryty Mod 2 ^ 2)
B_ukryty = Math.Floor(B_ukryty / 2 ^ 2)
Else
'wartość 0 przyciemni piksele, wartość 3 rozjaśni je (opcjonalne)
R += 3
G += 3
B += 3
End If
kontener.SetPixel(x, y, Color.FromArgb(R, G, B))
Next x
x_ukryty = 0
Next y
kontener.Save("C:\Users\piotr\Desktop\" + _
IO.Path.GetFileNameWithoutExtension(lokalizacjaPlikuGraficznego).ToString + _
" ukryty.bmp", System.Drawing.Imaging.ImageFormat.Bmp)
Console.WriteLine("Bity zostały ukryte!")
Console.ReadLine()
End If
End If
End If
End Sub
Private Function OdwrocBity2(ByVal w As Integer) As Integer
Dim rezultat As Integer = 0
For i As Integer = 0 To 1
rezultat = rezultat * 2 + (w Mod 2)
w = Math.Floor(w / 2)
Next
Return rezultat
End Function
Private Function OdwrocBity(ByVal w As Integer) As Integer
Dim rezultat As Integer = 0
For i As Integer = 0 To 7
rezultat = rezultat * 2 + (w Mod 2)
w = Math.Floor(w / 2)
Next
Return rezultat
End Function
End Module
Przykład kodu odzyskującego bity ze stratą:
Imports System.Windows.Forms
Imports System.Drawing
Module Module1
Dim lokalizacjaPlikuGraficznego As String
Sub Main()
Dim open_dialog As New OpenFileDialog()
open_dialog.InitialDirectory = "c:\"
open_dialog.Filter = "Image Files (*.jpeg; *.png; *.bmp)|*.jpg; *.png; *.bmp"
open_dialog.Title = "Wybierz obrazek w którym ukryto obrazek"
If open_dialog.ShowDialog() = System.Windows.Forms.DialogResult.OK Then
lokalizacjaPlikuGraficznego = open_dialog.FileName
Dim kontener As New Bitmap(lokalizacjaPlikuGraficznego)
Dim szerokosc As Integer = Math.Floor(kontener.Width / 2)
Dim ukryty As New Bitmap(szerokosc, kontener.Height)
Dim R As Integer = 0, G As Integer = 0, B As Integer = 0
Dim x_ukryty As Integer = 0
Dim xmax As Integer = kontener.Width - 1
Dim ymax As Integer = kontener.Height - 1
Dim pixelElementIndex As Long = 0
For y = 0 To ymax
For X = 0 To xmax
Dim pix As Color = kontener.GetPixel(X, y)
If (X Mod 2) = 0 Then
If X > 0 Then
ukryty.SetPixel(x_ukryty, y, Color.FromArgb(OdwrocBity(R), OdwrocBity(G), OdwrocBity(B)))
R = 0
G = 0
B = 0
x_ukryty += 1
End If
R = R * 4 + (pix.R Mod 4)
G = G * 4 + (pix.G Mod 4)
B = B * 4 + (pix.B Mod 4)
Else
R = Odwroc4Bity(R * 4 + (pix.R Mod 4))
G = Odwroc4Bity(G * 4 + (pix.G Mod 4))
B = Odwroc4Bity(B * 4 + (pix.B Mod 4))
If X = xmax Then
ukryty.SetPixel(x_ukryty, y, Color.FromArgb(OdwrocBity(R), OdwrocBity(G), _
OdwrocBity(B)))
End If
End If
Next X
x_ukryty = 0
Next y
ukryty.Save("C:\Users\piotr\Desktop\" + _
IO.Path.GetFileNameWithoutExtension(lokalizacjaPlikuGraficznego).ToString + _
"(odzyskany).bmp", System.Drawing.Imaging.ImageFormat.Bmp)
Console.WriteLine("Bity zostały odzyskane!")
Console.ReadLine()
End If
End Sub
Private Function OdwrocBity(ByVal w As Integer) As Integer
Dim rezultat As Integer = 0
For i As Integer = 0 To 7
rezultat = rezultat * 2 + (w Mod 2)
w = Math.Floor(w / 2)
Next
Return rezultat
End Function
Private Function Odwroc4Bity(ByVal w As Integer) As Integer
Dim rezultat As Integer = 0
For i As Integer = 0 To 3
rezultat = rezultat * 2 + (w Mod 2)
w = Math.Floor(w / 2)
Next
Return rezultat
End Function
End Module
Kotek ukryty tą metodą do pobrania i sprawdzenia: Matrix ukryty