Here I have some code which creates an image that hides another image in it.
I print out some rgb values to see the change (after implementing function chopToHide and function shift, but I don't understand by what calculation the numbers have changed by). This bit of code is part of a larger program to hide one image inside another (steganography).
This is the environment where I 'm working in: http://www.dukelearntoprogram.com/course1/example/index.php
var start = new SimpleImage("astrachan.jpg");
var hide = new SimpleImage("duvall.jpg");
print (start);
print(hide);
print ("width and height of astrachan picture");
print (start.getHeight(), start.getWidth());
print ("width and height of duvall picture")
print (hide.getHeight(), hide.getWidth());
var cropWidth = start.getWidth();
if (hide.getWidth() < cropWidth) {
cropWidth = hide.getWidth();
}
var cropHeight = start.getHeight();
if (hide.getHeight() < cropHeight) {
cropHeight = hide.getHeight();
}
start = crop(start,cropWidth, cropHeight);
hide = crop(hide,cropWidth, cropHeight);
print(start);
print(hide);
function crop(image, width, height){
var n = new SimpleImage(width,height);
for(var p of image.values()){
var x = p.getX();
var y = p.getY();
if (x < width && y < height){
var np = n.getPixel(x,y);
np.setRed(p.getRed());
np.setBlue(p.getBlue());
np.setGreen(p.getGreen());
}
}
return n;
}
//print (start);
//print(hide);
print ("cropped width and height of astrachan picture");
print (start.getHeight(), start.getWidth());
print ("cropped width and height of duvall picture")
print (hide.getHeight(), hide.getWidth());
function chopToHide(image){
for(var px of image.values()){
px.setRed(pixchange(px.getRed()));
px.setGreen(pixchange(px.getGreen()));
px.setBlue(pixchange(px.getBlue()));
}
return image;
}
function pixchange(pixval){
var x = Math.floor(pixval/16) * 16;
return x;
}
function shift(oldImage){
var newImage = new SimpleImage(oldImage.getWidth(), oldImage.getHeight());
for(var oldPixel of oldImage.values()){
var x = oldPixel.getX();
var y = oldPixel.getY();
var newPixel = newImage.getPixel(x, y);
newPixel.setRed( Math.floor(oldPixel.getRed()/16) );
newPixel.setGreen( Math.floor(oldPixel.getGreen()/16) );
newPixel.setBlue( Math.floor(oldPixel.getBlue()/16) );
}
return newImage;
}
print("before applying the chopToHide function to the image start:");
for(i = 1; i <= 100; i+=20 ){
var pixel = start.getPixel(i, i+5);
print("pixel at (" + pixel.getX() + "," + pixel.getY() + ")-> R= " +
pixel.getRed() + " : G= " + pixel.getGreen() + " : B= " + pixel.getBlue() );
}
//print (start);
start = chopToHide(start);
print("After applying the chopToHide function to the image start:");
for(i = 1; i <= 100; i+=20 ){
var pixel = start.getPixel(i, i+5);
print("pixel at (" + pixel.getX() + "," + pixel.getY() + ")-> R= " +
pixel.getRed() + " : G= " + pixel.getGreen() + " : B= " + pixel.getBlue() );
}
//print (start);
//print (hide);
print("Before applying the shift function to the image hide:");
for(i = 1; i <= 100; i+=20 ){
var pixel = hide.getPixel(i, i+5);
print("pixel at (" + pixel.getX() + "," + pixel.getY() + ")-> R= " +
pixel.getRed() + " : G= " + pixel.getGreen() + " : B= " + pixel.getBlue() );
}
hide = shift(hide);
print("After applying the shift function to the image hide:");
for(i = 1; i <= 100; i+=20 ){
var pixel = hide.getPixel(i, i+5);
print("pixel at (" + pixel.getX() + "," + pixel.getY() + ")-> R= "
+ pixel.getRed() + " : G= " + pixel.getGreen() + " : B= " +
pixel.getBlue() );
}
The idea this code is working towards is to reduce the colour range from 0..255 (8 bits, a "byte") per colour channel to 0..15 (4 bits, a "nybble") per colour channel on both pictures, then combine the two nybbles into one byte. The picture whose colours are in the high nybble will give the perceptible colour change (in increments of 16); the picture hiding in the low nybble will shade the colour of the main picture's pixels imperceptibly.
The formula should be:
steganoImageColour = (overtImageColour & 240) | (covertImageColour >> 4)
or equivalently (but not as fast):
steganoImageColour = (Math.floor(overtImageColour / 16) * 16) +
Math.floor(covertImageColour / 16)
To extract the covert image, you just take the low nybble and promote it to high:
reconstructedCovertImageColour = (steganoImageColour & 15) << 4
or equivalently,
reconstructedCovertImageColour = (steganoImageColour % 16) * 16
For example, if you have a green-yellow pixel in the overt image (173 255 47) and a light sky blue pixel in the covert image (135 206 250), the resulting pixel is (168 252 47), which is still very close to the original green-yellow, but one can reconstruct (128 192 240) from it, which is very close to the original light sky blue.
<div style="background-color: rgb(173,255,47)">overt (green-yellow)</div>
<div style="background-color: rgb(135,206,250)">covert (light sky blue)</div>
<div style="background-color: rgb(168,252,47)">stegano (close enough to green-yellow)</div>
<div style="background-color: rgb(128,192,240)">reconstructed covert (close enough to light sky blue)</div>
Related
I need to calculate an endpoint (E1.x and E1.y) with Javascript but I cannot get it to work properly.
Given are the starting point with S1.x and S1.y coordinates, the distance d and the coordinates of a target point (T1.x and T1.y). T1 determines the direction/line/slope S1-E1 (so E1 is on the line S1-T1, sometimes between S1-T1, sometimes not).
Needed is the calculated endpoint (E1.x and E1/y) that is on a line determined by S1-T1 with a distance d (calculated from S1).
S1.y is always lower than T1.y, but S1.x is sometimes positive/higher and sometimes negative/lower than T1.x.
function calculateEndPoint() {
console.log("pressedDistance: " + pressedDistance + " sourceCoordinates " + sourceCoordinates + " actualDist: " + actualDist + " targetCoordinates " + targetCoordinates + " difference : " + difference);
var S1 = {
x : sourceCoordinates[0],
y : sourceCoordinates[1],
};
var T1 = {
x : targetCoordinates[0],
y : targetCoordinates[1]
};
// slope
var slope = (T1.y-S1.y)/(T1.x-S1.x);
// Find angle of line
var theta = Math.atan(Math.abs(slope));
// the coordinates of the E1 Point
var E1x= Math.abs(T1.x + pressedDistance * Math.cos(theta));
var E1y= Math.abs(T1.y + pressedDistance * Math.sin(theta));
console.log("E1x: " + E1x);
console.log("E1y: " + E1y);
var calcCo = [E1x,E1y];
return calcCo;
}
The answer is illustrated and explained here: https://math.stackexchange.com/a/2109383
function calculateEndPoint() {
var x = sourceCoordinates[0] - (pressedDistance *(sourceCoordinates[0]-targetCoordinates[0]))/actualDist;
var y = sourceCoordinates[1] - (pressedDistance *(sourceCoordinates[1]-targetCoordinates[1]))/actualDist;
console.log("x: " + x + " y: " + y);
var calcCo = [x,y];
return calcCo;
}
I'm working on a simple javascript photo editor, and I'm stuck on this part:
var opacity = document.getElementById("opacity").value;
var contrast = document.getElementById("contrast").value;
var saturate = document.getElementById("saturate").value;
var brightness = document.getElementById("brightness").value;
var color = document.getElementById("color").value;
var sepia = document.getElementById("sepia").value;
function filter() {
document.getElementById("output").style.filter = "hue-rotate(" + color + "deg) sepia(" + sepia + "%) brightness(" + brightness * 3 + "%) saturate(" + saturate + "%) contrast(" + contrast * 2 + "%)";
}
var filters = document.getElementsByClassName("slider");
for (i = 0; i < filters.length; i++) {
filters[i].addEventListener("click", filter);
}
This function works only once. Similar function for opacity:
function opacity() {
var a = document.getElementById("opacity").value;
document.getElementById("output").style.opacity = a / 10;
}
document.getElementById("opacity").addEventListener("change", opacity);
works fine. Any ideas why? I tried doing it this way:
/*
function contrast() {
var b = document.getElementById("contrast").value;
document.getElementById("output").style.filter = "contrast(" + b * 2 + "%)";
}
document.getElementById("contrast").addEventListener("change", contrast);
function saturate() {
var c = document.getElementById("saturate").value;
document.getElementById("output").style.filter = "saturate(" + c + "%)";
}
document.getElementById("saturate").addEventListener("change", saturate);
function brightness() {
var d = document.getElementById("brightness").value;
document.getElementById("output").style.filter = "brightness(" + d * 3 + "%)";
}
document.getElementById("brightness").addEventListener("change", brightness);
function color() {
var e = document.getElementById("color").value;
document.getElementById("output").style.filter = "hue-rotate(" + e + "deg)";
}
document.getElementById("color").addEventListener("change", color);
function sepia() {
var f = document.getElementById("sepia").value;
document.getElementById("output").style.filter = "sepia(" + c + "%)";
}
document.getElementById("sepia").addEventListener("change", sepia);
/*
And everything is ok, but then I'm unable to apply multiple filters. Any help appreciated!
You have to get the value every time you click
var contrast = document.getElementById("contrast");
var saturate = document.getElementById("saturate");
var brightness = document.getElementById("brightness");
var color = document.getElementById("color");
var sepia = document.getElementById("sepia");
function filter() {
//You have to convert to number to do arithmetic
var _brightness = ~~brightness.value;
document.getElementById("output").style.filter = "hue-rotate(" + color.value + "deg) sepia(" + sepia.value + "%) brightness(" + _brightness * 3 + "%) saturate(" + saturate.value + "%) contrast(" + contrast.value * 2 + "%)";
}
and so on
EDIT - I don't think I explained it very well the first time.
I have a lot of data - it's in an Array, with each item in the array being an object. In the system I am working in (a control system for A/V devices, which uses JavaScript as the programming language), I am generating buttons based on the length of the array. I want to be able to position a button, and essentially know the X and Y coordinates for each button in the array - with X and Y being Row/Column. (which I then translate to a X/Y pixel position on my UI.
My initial code, which is below, is within a for loop, and I manually calculated the button position. But this is tedious, as I use this same function to show off different groups/sizes of buttons.
Anywhere there is mirage.log = console.log.
The code below is part of a For Loop
button.element.style.position = 'absolute'; //Do to all Buttons.
if (i == 0) //First Item
{
button.element.style.left = btn_Info.startLeft + 'px'; button.element.style.top = btn_Info.startTop + 'px';
}
else if (i <= btn_Info.numRow-1) //First Column.
{
mirage.log('Setting Position of First Column');
button.element.style.left = btn_Info.startLeft + 'px'; button.element.style.top = (btn_Info.height + btn_Info.vOffset) * i + btn_Info.startTop + 'px';
}
else if (i > btn_Info.numRow - 1 && i <= btn_Info.numRow * 2 - 1)
{
mirage.log('Setting Second column ' + i);
button.element.style.left = btn_Info.startLeft + btn_Info.width + btn_Info.hOffset + 'px'; button.element.style.top = (btn_Info.height + btn_Info.vOffset) * (i-btn_Info.numRow) + btn_Info.startTop + 'px';
}
else
{
mirage.log('Setting Third column ' + i);
button.element.style.left = btn_Info.startLeft + ((btn_Info.width + btn_Info.hOffset)*2) + 'px'; button.element.style.top = (btn_Info.height + btn_Info.vOffset) * (i - (btn_Info.numRow*2)) + btn_Info.startTop + 'px';
}
Thanks in advance for the help - I have grabbed so many answers from this forum over the last year, you guys are awesome!
EDIT -
I was able to get some adjustment if I generate rows first then columns:
I was able to get a little close with the help of a friend, and be able to adjust for a 2 column layout by doing the following:
encoder = {
'buttonVals':{'width':125,'height':50,'numCols':2,'numRows':null;'vOffset':10,'hOffset':10}
var posLeft;
var posTop;
posLeft = (i % encoder.buttonVals.numCols) * (encoder.buttonVals.width + encoder.buttonVals.hOffset) + encoder.buttonVals.startLeft;
posTop = Math.floor(i / encoder.buttonVals.numCols) * (encoder.buttonVals.height + encoder.buttonVals.vOffset) + encoder.buttonVals.startTop;
After working on this for a bit - here is the code that I got to work. This prints out both the row position, and the column position.
testFunction = function(incRow, incCol){
var myFunc = {
'testLength':0,
'numRows':incRow,
'numCols':incCol,
'array':[],
};
myFunc.testLength = incRow * incCol;
for(var c=0, posCol = 0, posRow = 0; c < myFunc.testLength; c++)
{
var whichRow;
posRow = Math.floor(c/myFunc.numRows);
whichRow = Math.floor(c/myFunc.numRows) + c;
if (whichRow > myFunc.numRows)
{
whichRow = whichRow - (myFunc.numRows * posRow) - posRow;
if (whichRow === 0)
{
posCol = posCol + 1;
}
}
console.log(c + ' : ' + whichRow + ' : ' + posCol);
}
};
testFunction(6,4);
Here is the link to what i'm trying to do. I want to flip the blue bars to face to opposite way on the male side then I will fill in the female side as normal. This will create a tornado chart. I've been working on this for hours and I cant figure it out. I'm using Raphael JS.
http://math.mercyhurst.edu/~cmihna/DataViz/Butterfly.html
Just finished up reviewing your website's source code. No need for transforms, or anything of the such. Just some simple math added to your graph generating for-loop.
Your Code below
ind = 0
for (var key in gender) { // loop over all possible gender
for ( var i = 0; i < people.length; i++ ) { // loop over people
if (people[i][key] != 0) {
var barmale = paper.rect((w+leftPadding-rightPadding)/2,topPadding + vs*0 + i*vs, people[i][key],vs)
barmale.attr({'fill': '#0000A0', 'stroke-width':1})
var barfemale = paper.rect((w+leftPadding-rightPadding)/2, topPadding + vs*0 + i*vs, people2[i][key],-vs)
barfemale.attr({'fill': '#FFC0CB', 'stroke-width':1})
barmale.scale(1,-1)
//var dp = paper.circle(leftPadding + ind*hs + 0.5*hs, topPadding + vs*0.5 + i*vs, people[i][key])
//dp.attr({ 'fill': colors[ind] })
barmale.id = people[i][key] + " " + gender[key] + " people in this age range"
barmale.hover(hoverStart, hoverEnd)
barfemale.id = people[i][key] + " " + gender[key] + " people in this age range"
barfemale.hover(hoverStart, hoverEnd)
}
}
ind++
My Code Below
ind = 0
for (var key in gender) { // loop over all possible gender
for ( var i = 0; i < people.length; i++ ) { // loop over people
if (people[i][key] != 0) {
var barmale = paper.rect((w+leftPadding-rightPadding)/2 - people[i][key],topPadding + vs*0 + i*vs, people[i][key],vs)
barmale.attr({'fill': '#0000A0', 'stroke-width':1})
var barfemale = paper.rect((w+leftPadding-rightPadding)/2, topPadding + vs*0 + i*vs, people2[i][key],vs)
barfemale.attr({'fill': '#FFC0CB', 'stroke-width':1})
barmale.scale(1,-1)
//var dp = paper.circle(leftPadding + ind*hs + 0.5*hs, topPadding + vs*0.5 + i*vs, people[i][key])
//dp.attr({ 'fill': colors[ind] })
barmale.id = people[i][key] + " " + gender[key] + " people in this age range"
barmale.hover(hoverStart, hoverEnd)
barfemale.id = people2[i][key] + " " + gender[key] + " people in this age range"
barfemale.hover(hoverStart, hoverEnd)
}
}
ind++
You can see that I am subtracting the value of the Males from the placement on the graph. This causes the offset to "flip". Then, I modified the code a bit more the bring the female graph into the picture and properly label it.
Please let me know if any questions.
Proof below
I am working with some CSS Backgrounds (http://css3pie.com/demos/gradient-patterns/) in an application and want to be able to scale the design with a slider. Here's a JSFiddle. I was able to scale X & Y separately on some like the Stripes and Picnic designs where I just had to play with background-size:50px 50px; like this:
//setup the variables based off the css which was set using dropdown
gridItems = $(document.activeElement).val().split("; ");
for (i = 0; i < gridItems.length -1; i++) {
gridSettings = gridItems[i].split(":");
if (gridSettings[0]=="background-size"){
gridSize = gridSettings[1].split(" ");
gridX = gridSize[0];
gridY = gridSize[1]
}
//on the action of the slide - update value
$('#gridXY-'+key).on("slide", function(slideEvt) {
gridXY = slideEvt.value;
$('.draggable-' + currentLayer).css("background-size", "calc("+ gridX +" * "+ gridXY +") calc("+ gridY +" * "+ gridXY +")");
});
Which can be set either numerically or using but when it gets to something like the Blue Print it has a lot more settings background-size:100px 100px, 100px 100px, 20px 20px, 20px 20px;
I am trying to do this right so that it can take a variable number of those, and can write something that could work, but the method I have in mind is really messy, hoping for some help scaling this that might be a little cleaner than what I would do.
I did find this:http://codepen.io/Erik/pen/JGnsB but he is using LESS to declare variables, if possible I would like to stay away from that.
UPDATE:
Here is a JSFiddle of it: http://jsfiddle.net/4L3d9qh2/
I added a function that should have let me update it, but for some reason the calc() function doesn't seem to be working to update the div style. After processing, it looks like this:
$('.draggable-0').css("background-size", calc(100px - 4) calc(100px - 4), calc(100px - 4) calc(100px - 4), calc(20px - 4) calc(20px - 4), calc(20px - 4) calc(20px - 4));
$.each(gridSizeArray, function( k, v ){
if (gridIncrement==1)
{
gridXY = "calc(" + v +" - " + value + ") ";
}else{
if(isOdd(gridIncrement)){
gridXY = gridXY + "calc(" + v +" - " + value + ") ";
}else{
gridXY = gridXY + "calc(" + v +" - " + value + "), ";
}
}
gridIncrement++
})
Here was the final code that works. Using .each() loop through and if it is even, add a comma, then remove the last comma at the end.
gridItems = $(document.activeElement).val().split("; ");
for (i = 0; i < gridItems.length -1; i++) {
gridSettings = gridItems[i].split(":");
if (gridSettings[0]=="background-size"){
gridSizeString = gridSettings[1].replace(/,/gi, '');
gridSizeArray = gridSizeString.split(" ");
}
$('[data-type="sliderLayer"][data-layer="'+currentLayer+'"][data-slide="'+currentSlide+'"]').css(gridSettings[0], gridSettings[1]);
}
$.each(gridSizeArray, function( k, v ){
if (gridIncrement==1)
{
gridXY = "calc(" + v +" + " + value + "px) ";
}else{
if(isOdd(gridIncrement)){
gridXY = gridXY + " calc(" + v +" + " + value + "px) ";
}else{
gridXY = gridXY + "calc(" + v +" + " + value + "px),";
}
}
gridIncrement++
})
if (isOdd(gridIncrement))
{
gridXY = gridXY.substring(0, gridXY.length - 1);
}