Metoda procentowa dotyczy obrazów monochromatycznych, czy takich, których typ MIME to TYPE_BYTE_BINARY. Dzięki temu barwa piksela może być przedstawiona tylko jako wartość 0 – czarna lub 1 – biała. Poniższy tekst przedstawia moją propozycję zastosowania metody procentowej na obrazie.

Obraz powinien zostać podzielony na obszary. Np. o określonej wielkości, definiowanej na podstawie wysokości i szerokości obrazu. Takie rozwiązanie ogranicza dostosowanie obszarów do ukrywanej wiadomości i niesie ze sobą ryzyko, że nośnik będzie zawierał za mało pól nadających się do ukrycia wiadomości. Z drugiej strony, na podstawie samego pliku z szyfrogramem możliwe jest wyznaczenie obszarów i odczytanie ukrytej wiadomości.

Obraz wejściowy potencjalnie daje możliwość ukrycia dokładnie tylu bitów danych, na ile obszarów zostanie podzielony. Jednak trzeba wziąć pod uwagę, że część z obszarów nie będzie nadawała się do ukrycia wiadomości i zostanie oznaczona jako nieużywana. Sytuacja taka wystąpi, gdy zmiana ogólnej wartości danego pola, wymagałaby zmiany liczby pikseli przekraczającej próg bezpieczeństwa. Próg bezpieczeństwa określa maksymalną liczbę pikseli w obszarze która może zostać zmieniona. Im wyższy próg tym powstałe zaburzenia w pliku będą większe. Z tego powodu proces zarówno osadzania wiadomości w nośniku, jak i jej wyodrębniania rozpoczyna się od zdefiniowania listy obszarów nadających się do użycia.

Przyjmijmy, że wielkość pojedynczego obszaru to 1% szerokości całkowitej obrazu oraz 1% wysokości, a próg bezpieczeństwa, który określa maksymalną liczbę pikseli w obszarze wynosi 25% ilości pikseli pojedynczego obszaru. Oznacza to, że przetwarzany obraz musi mieć co najmniej 200×200 pikseli. Wtedy rozmiar pojedynczego obszaru ma 2×2 pixele, a maksymalna liczba pikseli, które mogą ulec modyfikacji w obszarze to 1.

Obszar został przedstawiony poprzez klasę Field, która zawiera współrzędne x, y punktu rozpoczynającego obszar w obrazie, ogólną wartość pola jako boolean value, liczbę pól stanowiących większość – majority oraz ilość pikseli – pixelsT oChange, które należy zmienić w przypadku konieczności modyfikacji przeważającego koloru obszaru.

Algorytm zlicza ilość czarnych pikseli blackPixels w każdym pełnym obszarze, czyli takim, który składa się z ilości pól określonej jako pixelsInFields = 0.01 ∗ width ∗ 0.01 ∗ height, gdzie width i height to odpowiednio szerokość i wysokość obrazu stanowiącego kontener. Na podstawie procentowej zawartości pól danego koloru obliczana jest ogólna wartość obszaru.

Jeśli blackPixels/pixelsInFields >= 0.5, to value = false, w przecinym razie value = true.

W takim wypadku, aby zmienić przewagę kolorów w polu oznaczonym jako false, należy doprowadzić do sytuacji, aby 51% piseli stanowiły te w kolorze białym. Dla zmiany na kolor czarny minimalną wartością jest 50%. Zmianie ulega zawsze jak najmniejsza potrzebna liczba pikseli, w celu ograniczenia ilości wprowadzonych zakłóceń do obrazu.

Do obliczenia najmniejszej liczby pikseli, które muszą ulec zmianie, korzystamy z proporcji:

(minority + pixelsToChange)/pixelsInF ields = percent
pixelsT oChange = percent ∗ pixelsInF ields − minority,

gdzie minority to liczba pikseli występujących w mniejszości w danym obszarze, pixelsToChange to szukana najmniejsza liczba pikseli, które muszą ulec zmianie, a percent to współczynnik wynoszący odpowiednio 0,5 lub 0,51 w zależności od wartości value.

Należy również pamiętać, że liczba pikseli jest liczbą całkowitą, jeśli więc wyliczona wartość zawiera część ułamkową, należy dodać 1.

Jeśli pixelsT oChange <= 0.25 ∗ pixelsInF ields, to pole zostaje dodane do listy jako użyteczne. Obszary, które wymagałyby zmiany większej liczby pikseli niż ustalony próg nie są brane pod uwagę przy ukrywaniu wiadomości.

Tworzenie listy obszarów nadających się do ukrycia informacji.

private static void findUsefulFields(WritableRaster imageRaster) {
      for(int i=0; i<height; i+=y){
            for(int j=0; j<width; j+=x){
                  int y1 = i+y;
                  int x1 = j+x;
                  if(y1<height && x1<width){
                        int blackPixels = countBlackPixelInField(i, j, imageRaster, y1, x1);
                        double pixelToChangeFromProportion = 0;
                        double percent = 0;
                        Field field = new Field();
                        field.setX(j);
                        field.setY(i);
                        if(blackPixels / (double) pixelsInFields >= 0.5){
                             field.setValue(false);
                             field.setMajority(blackPixels);
                             percent = 0.51;
                        }
                       else{
                             field.setValue(true);
                             field.setMajority(pixelsInFields - blackPixels);
                             percent = 0.5;
                       }
                       int minority = pixelsInFields - field.getMajority();
                       pixelToChangeFromProportion = percent*pixelsInFields - minority;
                       int pixelToChangeFromProportionInt = (int)
                       pixelToChangeFromProportion;
	               if(pixelToChangeFromProportion > pixelToChangeFromProportionInt){
		              pixelToChangeFromProportionInt = pixelToChangeFromProportionInt + 1;
	                }
			
	               field.setPixelsToChange(pixelToChangeFromProportionInt);

	               if(pixelToChangeFromProportionInt <= maxPixelToChange){
		              fieldsList.add(field);
	               }
		
	        }
           }
      }
}

Funkcja do tworzenia listy używanych obszarów jest tworzona zarówno w przypadku ukrywania, jak i dekodowania wiadomości. W każdym obszarze można ukryć jeden bit danych. Początkowe 32 bity będą tak jak w poprzednich algorytmach zawsze przeznaczone na określenie długości wiadomości. Szyfrogram jest ukrywany w kolejnych obszarach z listy. Jeśli pole ma inną wartość niż oczekiwana składowa wiadomości, zostaje zmieniona określona wcześniej liczba pikseli, wybierana w losowy sposób w obrębie danego obszaru.

Poniższy obraz o wymiarach 16x16 pikseli został podzielony na 16 obszarów o rozmiarze 4x4 piksele. Każdy obszar składa się z 16 pikseli. Jeśli margines bezpieczeństwa zostanie ustalony na 25%, to w każdym obszarze można zmodyfikować maksymalnie 4 piksele, aby zmienić przewagę danego koloru w obszarze. Jeśli tyle nie wystarczy, obszar zostanie oznaczony jako nieużywany i będzie pominięty w algorytmie. Przyjmijmy, że jeżeli liczba pikseli czarnych, czyli P (0) >= 50%X, to obszar przyjmuje wartość 0, w przeciwnym razie – P (1) > 50%X obszar przyjmuje 1.

Do każdego piksela obrazu można odwołać się poprzez jego współrzędne. Umiejscowienie punktu (0,0) może różnić się w zależności od użytego języka programowania lub biblioteki. W stworzonym projekcie punkt (0,0) jest zawsze umiejscowiony w lewym górnym rogu obrazu. Kolejne obszary w obrazie zostały ponumerowane według tej reguły.

Przykładowy obraz monochromatyczny i jego podział na obszary

Przykładowy obraz monochromatyczny i jego podział na obszary

Przed ukryciem danych następuje sprawdzenie, czy obszar jest użyteczny, to znaczy czy modyfikacja liczby pikseli oznaczonych jako próg bezpieczeństwa wystarczy na zmianę wartości pola. Obszar oznaczony jako 0 zawiera 9 pól białych i 7 czarnych; przewaga pól białych powoduje przyjęcie przez pole umówionej wartości 1. Jeśli jednak w tym polu miałaby być ukryta wartość 0, wystarczy zmienić 1 piksel na czarny. Liczba pikseli, które muszą ulec zmianie, nie przekracza ustalonego progu bezpieczeństwa, czyli 4 pikseli, więc obszar można uznać za użyteczny. W przedstawionym przykładzie wszystkie pola można uznać za użyteczne, ponieważ rozkład białych i czarnych pikseli jest dość równomierny. Oznacza to, że możliwe jest ukrycie 16 bitów wiadomości. Ukryjmy binarny ciąg: 0110 1000 0110 1001 w kolejności odczytywania pól jak na powyższym schemacie. W każdym polu, jeśli ogólna wartość obszaru jest niezgodna z kolejną ukrywaną wartością, zmieniana jest minimalna ilość pikseli, która wpływa na zmianę wartości ogólnej. Zmieniane piksele są wybierane losowo.

Ukrycie wiadomości w kolejnych obszarach i obraz wyjściowy

Ukrycie wiadomości w kolejnych obszarach i obraz wyjściowy

Ogólny algorytm postępowania w przykładowej aplikacji:
1. Sprawdzamy, czy wymiary obrazu są większe lub równe 200x200 pikseli.
2. Wyznaczenie 1% wysokości i 1% szerokości obrazy wejściowego i zaokrąglenie wyników do liczb całkowitych.
3. Wyznaczenie listy obszarów użytecznych, czyli takich, w których modyfikacja liczby pikseli, nieprzekraczającej ustalonej wartości 25% na przeciwny kolor powoduje zmianę przeważającego koloru w polu.
4. Ukrycie długości wiadomości oraz samej wiadomości zapisanych jako ciągi binarne w obszarach określonych jako użyteczne.
5. Jeśli dany obszar ma przewagę białych pikseli, to jego wartość określana jest jako 1 (true), w przeciwnym razie 0 (false). Jeśli wartość danego obszaru zgadza się z kolejną ukrywaną wartością, to algorytm przechodzi do ukrywania kolejnej wartości w kolejnym obszarze. Jeśli obszar ma wartość przeciwną modyfikacji zostaje poddana minimalna liczba pikseli, tak aby 50% danego obszaru stanowiły piksele czarne, jeśli ukrywamy 0 lub 51% danego obszaru piksele białe dla wartości 1. Piksele są wybierane losowo z danego obszaru. Jeśli wartość danego piksela można zmienić, to licznik zmienionych pikseli zostaje zwiększony, w przeciwnym razie losowane są nowe współrzędne.

Po lewej obraz oryginalny, po prawej obraz z ukrytą wiadomością

Po lewej obraz oryginalny, po prawej obraz z ukrytą wiadomością