javascripthtmlimagegetimagedata

JS image pixels change


I have a problem. I am currently working on an project that changes image pixels. I am doing it as a web app using javascript. The problem is that when I change them using getImageData and then downloads the picture with toDataURL and link download it works. But when I then upload the image and read data with getInamgeData some of the pixels change. Where can be the problem and how can I fix it.

const original_canvas = document.getElementById('original_canvas');
const edited_canvas = document.getElementById('edited_canvas');
const orig_ctx = original_canvas.getContext('2d');
const edit_ctx = edited_canvas.getContext('2d');
var start = document.getElementById("start");
var textfield = document.getElementById("textField");
var message = "";
var download = document.getElementById("download");

var loadFile = function(event){                                     //
  var uploaded_image = new Image();
  uploaded_image.src = URL.createObjectURL(event.target.files[0]);  //
  uploaded_image.onload = function (){                              //resizes canvas and image after its loaded
    //original_canvas.width = 400;
    //original_canvas.height = uploaded_image.height/(uploaded_image.width/400);
    //orig_ctx.drawImage(uploaded_image,0,0,original_canvas.width,original_canvas.height);  //draws resized image into canvas
    //toto rozostruje obrazok
    original_canvas.width = uploaded_image.width;
    original_canvas.height = uploaded_image.height;
    orig_ctx.drawImage(uploaded_image,0,0);
  };

};

function toBinary(input){                               //converts text to binary
  var output = "5";
  for(i = 0; i < input.length; i++){
    output += input[i].charCodeAt(0).toString(2) + "2"; //first to ASCI code then to binary
  }
  output += "5";
  return output
}

function toText(input){                                 //converts binary to text
  var output = "";
  var binary = "";
  for(i = 0; i < input.length; i++){
    if(input[i] == "2"){                                //divides string by spaces
      asci = parseInt(binary, 2);                       //binary to ASCI code
      output += String.fromCharCode(asci);              //ASCI code to char
      console.log(asci);
      binary = "";
    }
    else{
      binary += input[i];
    }
  }
  return output;
}

function closest_point(number, change){                 //finds number ending in 1 or 0 and changes it to it
  var result = 0;                                       //this should help reduce the vissible changes
  function up(){
    result = (((number-number%10)/10)+1)*10+change;
    return result;
  }
  function down(){
    result = number-number%10+change;
    return result;
  }
  if(Math.abs(number-up()) < Math.abs(number-down()) && up()<255){
    return up();
  }
  else{
    return down();
  }
}

function encode(){
  message = toBinary(textfield.value);
  console.log(message);
  let imgData = orig_ctx.getImageData(0, 0, original_canvas.width,original_canvas.height);
  if(message.length > imgData.data.length){
    alert("Message is too big.");
  }
  else{
    for (i = 0; i < imgData.data.length; i += 4){
      if(i >= message.length){
        break;
      }
      else{
        imgData.data[i] = closest_point(imgData.data[i],Number(message[i]));        
        imgData.data[i+1] = closest_point(imgData.data[i+1],Number(message[i+1]));        
        imgData.data[i+2] = closest_point(imgData.data[i+2],Number(message[i+2]));        
        imgData.data[i+3] = closest_point(imgData.data[i+3],Number(message[i+3]));
      }       
    }
    console.log(imgData);
    edited_canvas.width = imgData.width;
    edited_canvas.height = imgData.height;
    edit_ctx.putImageData(imgData, 0, 0);
    var jpg = edited_canvas.toDataURL('image/png', 1.0);
    console.log(jpg);
    download.href = jpg;
  }
}

function decode(){
  message="";
  counter = 0;
  let imgData = orig_ctx.getImageData(0, 0, original_canvas.width,original_canvas.height);
  console.log(imgData);
  for(i=0; i < imgData.data.length;i+=4){
    if(imgData.data[i]%10 == 5){
      counter += 1;
    }
    if(counter == 1){
      message += (imgData.data[i]%10).toString();
      message += (imgData.data[i+1]%10).toString();
      message += (imgData.data[i+2]%10).toString();
      message += (imgData.data[i+3]%10).toString();
    }
    if(counter == 2){
      message = message.substring(1);
      if(message.includes("5")==true){
        message = message.substring(0,message.indexOf('5'));
      }
      console.log(message);
      break;
    }
  }
  console.log(toText(message));
}

function main(){
  encode_check = document.getElementById("encode_check");
  decode_check = document.getElementById("decode_check");
  if(decode_check.checked == true){
    decode();
  }
  else{
    if(encode_check.checked == true){
      encode();
    }
    else{
      alert("Fill everyhing");
    }
  }
}
<!DOCTYPE html>
<html lang="en">
  <head>
      <meta charset="UTF-8">
      <meta http-equiv="X-UA-Compatible" content="IE=edge">
      <meta name="viewport" content="width=device-width, initial-scale=1.0">
      <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-1BmE4kWBq78iYhFldvKuhfTAU6auU8tT94WrHftjDbrCEXSU1oBoqyl2QvZ6jIW3" crossorigin="anonymous">
      <title>Steganography</title>
  </head>
  <body>
    <div style="position:relative; left:50px; top:10px;">
      <input type="text" id="textField" placeholder="O polnoci na vrsku">
      <button id="start" onclick="main()">Start</button>
      <a href="" id="download" download>Download</a>
      <input type="file"  accept="image/*" name="image" id="file"  onchange="loadFile(event)">

      <div class="form-check">
        <input class="form-check-input" type="radio" name="flexRadioDefault" id="decode_check">
        <label class="form-check-label" for="flexRadioDefault1">
          Decode
        </label>
      </div>
      <div class="form-check">
        <input class="form-check-input" type="radio" name="flexRadioDefault" id="encode_check">
        <label class="form-check-label" for="flexRadioDefault2">
          Encode
        </label>
      </div>
    </div>


    <canvas id="original_canvas">
      Your browser does not support the HTML5 canvas tag.
    </canvas>
    <canvas id="edited_canvas">
      Your browser does not support the HTML5 canvas tag.
    </canvas>

    

  </body>
</html>


Solution

  • I managed to fix it. The problem was that the pixels changed only when the forth value changed- the alpha channel. I suppose it is caused by gamma correction of the web browser.