Pull to refresh

Реализация стеганографического метода Коха-Жао на Ruby

Reading time 4 min
Views 8.4K
В этой статье будет рассмотрен наиболее предпочтительный стеганографический метод скрытия информации в изображениях, как с точки зрения устойчивости к различным видам атак, так и с точки зрения сохранения приемлимого качества изображения.

Алгоритм Коха-Жао для встраивания информации использует частотную область контейнера и заключается в относительной замене величин коэффициентов дискретного косинусного преобразования (ДКП). Изображение разбивается на блоки размерностью 8×8 пикселей и к каждому блоку применяется ДКП. Каждый блок пригоден для записи одного бита информации.

Итак алгоритм скрытия будет следующим:

— итерируем изображение двойным массивом с шагом в 8
— на каждой итерации создаем временный массив 8x8 пикселей, каждым элементом которого будет набор трех цветов пикселя.
— применяем ДКП к этому массиву, и получаем массив коэффициентов размером 8x8
— выбираем 2 коэффициента и высчитываем их разность по модулю
— если разность меньше или равна 25, то присваиваем первому коээфициенту положительное значение второго + константа, либо тоже самое но со знаком минус(это называется передача бита)
— если разность меньше или равна -25, то те же деяствия только для второго коэффициента.
— далее применяется обратное ДКП чтобы перевести наши коээфициенты обратно в пространственную область.
— после чего копируем новые значения цветов в изображение.

Реализация функции ДКП и ОДКП на языке Ruby выглядит следующим образом:

def dct(dct,arr)
  s=0
  (0..7).each do |i|
    (0..7).each do |j|
      temp = 0.0
      (0..7).each do |x|
        (0..7).each do |y|
          temp = temp + $cos_t[i][x]*$cos_t[j][y]*arr[x][y][:blue]
        end
      end
      dct[i][j] = $e[i][j]*temp
    end
  end
  return dct
end

def idct(dct,arr)
  (0..7).each do |i|
    (0..7).each do |j|
      temp = 0
      (0..7).each do |x|
        (0..7).each do |y|
          temp += dct[x][y]*$cos_t[x][i]*$cos_t[y][j]*$e[x][y]
          arr[i][j][:blue] = (temp > 255) ? 255 : (temp < 0 ) ? 0 : temp.round;
        end
      end
    end
  end
  return arr
end


$cos_t и $e — это глобальные переменные-массивы с уже заполненными значениями.

Необходимо отметить, что метод является достаточно устойчивым к искажению изображения, даже к его существенному изменению, но для скрытия больших объемов данных неприменим.

Полный код скрипта реализующего скрытие\чтение информации:

require 'RMagick'
include Magick

$cos_t = [            [1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0],
                      [0.9807853, 0.8314696, 0.5555702, 0.1950903,
                       -0.1950903,-0.5555702,-0.8314696,-0.9807853],
                       [0.9238795, 0.3826834,-0.3826834,-0.9238795,
                        -0.9238795,-0.3826834, 0.3826834, 0.9238795],
                        [0.8314696,-0.1950903,-0.9807853,-0.5555702,
                         0.5555702, 0.9807853, 0.1950903,-0.8314696],
                         [0.7071068,-0.7071068,-0.7071068, 0.7071068,
                          0.7071068,-0.7071068,-0.7071068, 0.7071068],
                          [0.5555702,-0.9807853, 0.1950903, 0.8314696,
                           -0.8314696,-0.1950903, 0.9807853,-0.5555702],
                           [0.3826834,-0.9238795, 0.9238795,-0.3826834,
                            -0.3826834, 0.9238795,-0.9238795, 0.3826834],
                            [0.1950903,-0.5555702, 0.8314696,-0.9807853,
                             0.9807853,-0.8314696, 0.5555702,-0.1950903] ]

$e = [             [0.125, 0.176777777, 0.176777777, 0.176777777,
                    0.176777777, 0.176777777, 0.176777777, 0.176777777],
                    [0.176777777, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25],
                    [0.176777777, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25],
                    [0.176777777, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25],
                    [0.176777777, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25],
                    [0.176777777, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25],
                    [0.176777777, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25],
                    [0.176777777, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25]];

def insert_message(image,text)
  i = image.copy
  dct = Array.new(8).map!{Array.new(8)}
  k = 0
  l = 0
  s=0
  temp = Array.new(8).map!{Array.new(8)}
  (0..(image.columns - 1)).step(8) do |i|
    (0..(image.rows - 1)).step(8) do |j|
      break if l >= text.length*8
      (0..7).each do |x|
        (0..7).each do |y|
          temp[x][y] = { 
            :red=>image.pixel_color(j+x,i+y).red,
            :green=>image.pixel_color(j+x,i+y).green,
            :blue=>image.pixel_color(j+x,i+y).blue
          }
        end
      end 
      dct = dct(dct,temp)
      k = dct[3][4].abs - dct[4][3].abs
      if get_bit(text,l)
        if k<=25
          dct[3][4] = (dct[3][4]>=0) ? dct[4][3].abs + 150 : -1*(dct[4][3].abs + 150)
        end
      else
        if k>=-25
          dct[4][3] = (dct[4][3]>=0) ? dct[3][4].abs + 150 : -1*(dct[3][4].abs + 150)
        end
      end
      xt  = temp.clone
      temp = idct(dct,temp)
      (0..7).each do |x|
        (0..7).each do |y|
          pixel = Pixel.new(temp[x][y][:red],temp[x][y][:green],temp[x][y][:blue])
          image.pixel_color(j+x,i+y,pixel)
        end
      end
      l=l+1
    end
    break if l >= text.length
  end
  image.write('cr.bmp')
  return image
end

def read_message(image)
  k = 0
  s=0
  out = []
  l=0
  a=''
  p=0
  b=0
  dct = Array.new(8).map!{Array.new(8)}
  temp = Array.new(8).map!{Array.new(8)}
  (0..(image.columns - 1)).step(8) do |i|
    (0..(image.rows - 1)).step(8) do |j|
      (0..7).each do |x|
        (0..7).each do |y|
          temp[x][y] = { 
            :red=>image.pixel_color(j+x,i+y).red,
            :green=>image.pixel_color(j+x,i+y).green,
            :blue=>image.pixel_color(j+x,i+y).blue
          }
        end
      end
      l=l+1
      dct = dct(dct,temp)
      k = dct[3][4].abs-dct[4][3].abs

      if k>=25
        a=1
      elsif k<=-25
        a=0
      else
        a=-1
        break
      end
      b |= a << p
      if p==7
        out.push(b.chr)
        b=0
      end
      p=(p<7) ? p+1 : 0
    end
    if a==-1
      break
    end
  end
  return out.join
end

def dct(dct,arr)
  s=0
  (0..7).each do |i|
    (0..7).each do |j|
      temp = 0.0
      (0..7).each do |x|
        (0..7).each do |y|
          temp = temp + $cos_t[i][x]*$cos_t[j][y]*arr[x][y][:blue]
        end
      end
      dct[i][j] = $e[i][j]*temp
    end
  end
  return dct
end

def idct(dct,arr)
  (0..7).each do |i|
    (0..7).each do |j|
      temp = 0
      (0..7).each do |x|
        (0..7).each do |y|
          temp += dct[x][y]*$cos_t[x][i]*$cos_t[y][j]*$e[x][y]
          arr[i][j][:blue] = (temp > 255) ? 255 : (temp < 0 ) ? 0 : temp.round;
        end
      end
    end
  end
  return arr
end

def get_bit(str,pos)
  return true if  str[pos/8].ord & (1 << pos % 8) > 0
  return false if  str[pos/8].ord & (1 << pos % 8) <= 0
end

image = Magick::Image.read('src.bmp').first
insert_message(image,"qweqwe")


image = Magick::Image.read('dst.bmp').first
puts read_message image
Tags:
Hubs:
0
Comments 0
Comments Leave a comment

Articles