简体   繁体   中英

Javascript Steganography: Hiding an image inside of an image

I have been stuck on this program for a while and I am completely lost as to what is wrong and what I need to fix. I am trying to use steganography to encrypt a secret image inside of a cover image, and then decrypt the secret image. I am given part of the code and I have to implement the encodePixel and decodePixel functions, as well as the "helper functions".

When I run the program, the resulting image is completely black instead of the secret image. I do not know where my error is, and if it is in the encode or decode function, or if it is in the helper functions.

These are the functions that I have implemented:

/*
 * Encodes the given secret pixel into the low bits of the
 * RGB values of the given cover pixel
 * Returns the modified cover pixel
 */
function encodePixel(coverPixel, secretPixel) {

    var redBit = secretPixel[RED];
    var greenBit = secretPixel[GREEN];
    var blueBit = secretPixel[BLUE];


    if(redBit >= 128){
        setLowestBit(coverPixel[RED], 1);
    }else{
        setLowestBit(coverPixel[RED], 0);
    }
    if(blueBit >= 128){
        setLowestBit(coverPixel[BLUE], 1);
    }else{
        setLowestBit(coverPixel[BLUE], 0);
    }
    if(greenBit >= 128){
        setLowestBit(coverPixel[GREEN], 1);
    }else{
        setLowestBit(coverPixel[GREEN], 0);

    return coverPixel;
}


/*
 * Extracts the RGB values for a secret pixel from the low bits
 * of the given cover pixel
 * Returns the resulting secret pixel
 */
function decodePixel(coverPixel) {

    var secretPixel = [0, 0, 0];
    var redBit = getLowestBit(coverPixel[RED]);     
    var greenBit = getLowestBit(coverPixel[GREEN]); 
    var blueBit = getLowestBit(coverPixel[BLUE]);   

    if(redBit == 1){
        setLowestBit(secretPixel[RED], 255);
    }else{
        setLowestBit(secretPixel[RED], 0);
    }
    if(blueBit == 1){
        setLowestBit(secretPixel[BLUE], 255);
    }else{
        setLowestBit(secretPixel[BLUE], 0);
    }
    if(greenBit == 1){
        setLowestBit(secretPixel[GREEN], 255);
    }else{
        setLowestBit(secretPixel[GREEN], 0);
    }

    return secretPixel;
}



//=========HELPER FUNCTIONS==========\\

// Returns true if the given value is even, false otherwise
function isEven(value) {
    return value % 2 == 0;
}


/*
 * Given a number, return the lowest bit in the binary representation
 * of the number.
 * Returns either a 0 or a 1
 */
function getLowestBit(value) {
    if(isEven(value)){
        return 0;
    }else{
        return 1;
    }
}


/*
 * Given a number, return a new number with the same underlying bits
 * except the lowest bit is set to the given bitValue.
 */
function setLowestBit(value, bitValue) {
    if(bitValue == 0) {
        // If value is even, the lowest bit is already 0.
        if(isEven(value)) {
            return value;
        }
        // Otherwise, the lowest bit is 1. We subtract 1 to set it to 0.
        else {
            return value - 1;
        }
    }

    else {
        // If value is odd, the lowest bit is already 1
        if(!isEven(value)) {
            return value;
        }
        // Otherwise, the lowest bit is 0. We add 1 to set it to 1.
        else {
            return value + 1;
        }
    }
}

This is the rest of the code, which I do not have to change.

//===============================CONSTANTS===========================\\

// Constants for the images
var ORIGINAL_URL = "https://codehs.com/static/img/zebra.jpg";
var SECRET_IMAGE_URL = "https://codehs.com/static/img/about/goldengate.jpg";

// Constants for pixel indices
var RED = 0;
var GREEN = 1;
var BLUE = 2;

// Constants for colors
var MAX_COLOR_VALUE = 255;
var MIN_COLOR_VALUE = 0;
var COLOR_THRESHOLD = 128;

//********************STARTER CODE BELOW******************************

// Constants for spacing
var X_GAP = 10;
var Y_GAP = 25;
var TEXT_Y_GAP = 4;
var IMAGE_WIDTH = getWidth() / 2 - X_GAP * 2;
var IMAGE_HEIGHT = getHeight() / 3 - Y_GAP * 1.1;
var IMAGE_X = 10;
var IMAGE_Y = 25;

// Image width cannot be odd, it messes up the math of the encoding
if(IMAGE_WIDTH % 2 == 1) {
    IMAGE_WIDTH -= 1;
}

// We need to wait for the image to load before modifying it
var IMAGE_LOAD_WAIT_TIME = 50;

function start() {
    // Set up original image
    var original = new WebImage(ORIGINAL_URL);
    original.setSize(IMAGE_WIDTH, IMAGE_HEIGHT);
    original.setPosition(IMAGE_X, IMAGE_Y);

    // Set up secret image
    var secret = new WebImage(SECRET_IMAGE_URL);
    secret.setSize(IMAGE_WIDTH, IMAGE_HEIGHT);
    secret.setPosition(IMAGE_X + original.getWidth() + X_GAP, IMAGE_Y);

    // Set up the cover image 
    // (identical to original, but will be modified to encode the secret image)
        var cover = new WebImage(ORIGINAL_URL);
    var coverX = getWidth() / 2 - IMAGE_WIDTH / 2;
    cover.setSize(IMAGE_WIDTH, IMAGE_HEIGHT);
    cover.setPosition(IMAGE_X + IMAGE_WIDTH / 2, IMAGE_Y + Y_GAP + IMAGE_HEIGHT);

    // Add originals
    add(original);
    add(secret);

    // Add the cover image that will be used for encrypting the secret image
    add(cover);

    // Wait for images to load before encrypting and decrypting
    setTimeout(function(){
        encrypt(cover, secret);
        decrypt(cover);
    }, IMAGE_LOAD_WAIT_TIME);

    // Add labels for each image
    var originalLabel = new Text("Original Cover Image", "11pt Arial");
    originalLabel.setPosition(original.getX(), original.getY() - TEXT_Y_GAP);
    add(originalLabel);

    var secretLabel = new Text("Original Secret Image", "11pt Arial");
    secretLabel.setPosition(secret.getX(), secret.getY() - TEXT_Y_GAP);
    add(secretLabel);

    var coverLabel = new Text("Cover Image with Secret Image encoded inside", "11pt Arial");
    coverLabel.setPosition(getWidth() / 2 - coverLabel.getWidth() / 2, cover.getY() - TEXT_Y_GAP);
    add(coverLabel);

    var resultLabel = new Text("Resulting Secret Image decoded from Cover Image", "11pt Arial");
    resultLabel.setPosition(getWidth() / 2 - resultLabel.getWidth() / 2, cover.getY() + IMAGE_HEIGHT + Y_GAP - TEXT_Y_GAP );
    add(resultLabel);
}

/*
 * Encrypts the secret image inside of the cover image.
 * For each pixel in the cover image, the lowest bit of each
 * R, G, and B value is set to a 0 or 1 depending on the amount of
 * R, G, and B in the corresponding secret pixel.
 * If an R, G, or B value in the secret image is between 0 and 127,
 * set a 0, if it is between 128 and 255, set a 1.
 */
function encrypt(cover, secret) {

    // Make sure cover and secret are the same size.
    var width = cover.getWidth();
    var height = cover.getHeight();
    secret.setSize(width, height);

    // Loop over each pixel in the image
    for(var x = 0; x < width; x++) {
        for(var y = 0; y < height; y++) {

            // Get the pixels at this location for both images
            var coverPixel = cover.getPixel(x, y);
            var secretPixel = secret.getPixel(x, y);

            // Modify the cover pixel to encode the secret pixel
            var newCoverPixel = encodePixel(coverPixel, secretPixel);

            // Update this pixel in the cover image to have the
            // secret bit encoded
            cover.setRed(x, y, newCoverPixel[RED]);
            cover.setGreen(x, y, newCoverPixel[GREEN]);
            cover.setBlue(x, y, newCoverPixel[BLUE]);
        }
    }
}

/*
 * Decrypts a secret image from an encoded cover image.
 */
function decrypt(coverImage) {

    // secretImage will start off the same as the cover image.
    // As we loop over the coverImage to discover the secret embedded image,
    // we will update secretImage pixel by pixel
    var secretImage = new WebImage(coverImage.filename);

    // We need to add secretImage to the canvas before we can modify it
    secretImage.setSize(coverImage.getWidth(), coverImage.getHeight());
    secretImage.setPosition(coverImage.getX(), coverImage.getY() + coverImage.getHeight() + Y_GAP);
    add(secretImage);

    // Wait for secretImage to load on the canvas before we start
    // modifying its pixels.
    setTimeout(function(){
        computeSecretImage(coverImage, secretImage);
    }, IMAGE_LOAD_WAIT_TIME);
}

/*
 * Set the pixels in the image secret based on the lowest
 * bit values in the pixels of the image cover.
 */
function computeSecretImage(cover, secret) {
    // Loop over every pixel in the cover image
    for(var x = 0; x < cover.getWidth(); x++) {
        for(var y = 0; y < cover.getHeight(); y++) {
            //Get the current pixel of the cover image
            var coverPixel = cover.getPixel(x, y);

            // Compute the secretPixel from this cover pixel
            var secretPixel = decodePixel(coverPixel);

            // Update the pixel in the secret image to match the computed secret pixel
            secret.setRed(x, y, secretPixel[RED]);
            secret.setGreen(x, y, secretPixel[GREEN]);
            secret.setBlue(x, y, secretPixel[BLUE]);
        }
    }
}

I appreciate any help!

Your issue is that you set the secretPixel to [0,0,0], then it never changes in any function calls. You return values from encodePixel and decodePixel, but the returned values are discarded

What you'd need to do is

function encodePixel(coverPixel, secretPixel) {
    var returnedPixel = [0,0,0];
    var redBit = secretPixel[RED];
    var greenBit = secretPixel[GREEN];
    var blueBit = secretPixel[BLUE];


    if(redBit >= 128){
        returnedPixel[RED] = setLowestBit(coverPixel[RED], 1);
    }else{
        returnedPixel[RED] = setLowestBit(coverPixel[RED], 0);
    }
    // repeat for blue/green
    //
    // then
    return returnedPixel;
}

and

function decodePixel(coverPixel) {

    var secretPixel = [0, 0, 0];
    var redBit = getLowestBit(coverPixel[RED]);     
    var greenBit = getLowestBit(coverPixel[GREEN]); 
    var blueBit = getLowestBit(coverPixel[BLUE]);   

    if(redBit == 1){
        secretPixel[RED] = 255;
    }
    // repeat for green and blue
    //
    // then
    return secretPixel;
}

Bonus: your whole first snippet of code can be simplified as 2 lines:

const encodePixel = (coverPixel, secretPixel) => coverPixel.map((part, index) => (part & 254) | ((secretPixel[index] >> 7) & 1));
const decodePixel = coverPixel => coverPixel.map(part => (part & 1) * 255);

Or if ES6 frightens you, 10 lines:

var encodePixel = function encodePixel(coverPixel, secretPixel) {
    return coverPixel.map(function (part, index) {
        return (part & 254) | ((secretPixel[index] >> 7) & 1);
    });
};
var decodePixel = function decodePixel(coverPixel) {
    return coverPixel.map(function (part) {
        return (part & 1) * 255;
    });
};
/* Encodes the given secret pixel into the low bits of the
 * RGB values of the given cover pixel.
 * Returns the modified cover pixel 
 */

function encodePixel(coverPixel, secretPixel) {
    // Get RGB values from both pixels
    var originalRed = coverPixel[RED];
    var originalGreen = coverPixel[GREEN];
    var originalBlue = coverPixel[BLUE];
    
    var secretRed = secretPixel[RED];
    var secretGreen = secretPixel[GREEN];
    var secretBlue = secretPixel[BLUE];
    
    // Determine what the red green and blue bits should be
    // for this pixel
    var redBit = 0;
    var greenBit = 0;
    var blueBit = 0;
    
    // Color values go from 0 to 255. 
    // If a value is in the lower half of the range, the bit should be 0. 
    // If a value is in the upper half of the range, the bit should be a 1.
    if(secretRed >= COLOR_THRESHOLD) {
        redBit = 1;
    }
    
    if (secretGreen >= COLOR_THRESHOLD) {
        greenBit = 1;
    }
    
    if (secretBlue >= COLOR_THRESHOLD) {
        blueBit = 1;
    }
    
    // Calculate new RGB values for the cover image
    // The lowest bit is set to the corresponding RGB value
    // for the secret image
    var newRed = setLowestBit(originalRed, redBit);
    var newGreen = setLowestBit(originalGreen, greenBit);
    var newBlue = setLowestBit(originalBlue, blueBit);
    
    // Update the cover pixel with the new RGB values
    coverPixel[RED] = newRed;
    coverPixel[GREEN] = newGreen;
    coverPixel[BLUE] = newBlue;
    
    // Return the modified cover pixel
    return coverPixel;
}

/* Extracts the RGB values for a secret pixel from the low bits
 * of the given cover pixel
 * Returns the resulting secret pixel 
 */

function decodePixel(coverPixel) {
    // Get the RGB values from the cover pixel
    var red = coverPixel[RED];
    var green = coverPixel[GREEN];
    var blue = coverPixel[BLUE];
    
    // Initialize a new secret pixel with 0s for R G and B
    var secretPixel = [MIN_COLOR_VALUE, MIN_COLOR_VALUE, MIN_COLOR_VALUE];
    
    // Calculate the RGB values for the secret pixel
    // Based on the lowest bit of each RGB value in the cover pixel!
    
    // Get the lowest bit from each RGB value in the cover pixel
    // If it is not even, the low bit is a 1
    // If it is even, the low bit is a 0
    var redBit = getLowestBit(red);
    var greenBit = getLowestBit(green);
    var blueBit = getLowestBit(blue);
    
    // If the redBit is a 1, max out the red value in the secret pixel
    // Otherwise the secret pixel gets no red
    if(redBit == 1) {
        secretPixel[RED] = MAX_COLOR_VALUE;
    } else {
        secretPixel[RED] = MIN_COLOR_VALUE;
    }
    
    // If the greenBit is a 1, max out the green value in the secret image
    // Otherwise the secret image gets no green in this pixel
    if(greenBit == 1) {
        secretPixel[GREEN] = MAX_COLOR_VALUE;
    } else {
        secretPixel[GREEN] = MIN_COLOR_VALUE;
    }
    
    // If the blueBit is a 1, max out the blue value in the secret image
    // Otherwise the secret image gets no blue in this pixel
    if(blueBit == 1) {
        secretPixel[BLUE] = MAX_COLOR_VALUE;
    } else {
        secretPixel[BLUE] = MIN_COLOR_VALUE;
    }
    
    // Return the calculated secret pixel
    return secretPixel;
}

//=========HELPER FUNCTIONS==========//

/* Returns true if the given value is even, otherwise returns false. */
function isEven(value) {
    return value % 2 == 0;
}


/* Given a number, return the lowest bit in the binary representation
 * of the number.
 * Returns either a 0 or a 1.
 */
function getLowestBit(value) {
    if(isEven(value)) {
        return 0;
    }
    else {
        return 1;
    }
}


/* Given a number, return a new number with the same underlying bits
 * EXCEPT the lowest bit is set to the given bitValue.
 */
function setLowestBit(value, bitValue) {
    if(bitValue == 0) {
        // If value is even, the lowest bit is already 0.
        if(isEven(value)) {
            return value;
        } 
        // Otherwise, the lowest bit is 1. We subtract 1 to set it to 0.
        else {
            return value - 1;
        }
    }
    else {
        // If value is odd, the lowest bit is already 1
        if(!isEven(value)) {
            return value;
        }
        // Otherwise, the lowest bit is 0. We add 1 to set it to 1.
        else {
            return value + 1;
        }
    }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM