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>
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.