I have a byte array obtained from an image using the following code.
String path = "/home/mypc/Desktop/Steganography/image.png";
File file = new File(path);
BufferedImage bfimage = ImageIO.read(file);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ImageIO.write(bfimage, "png", baos);
baos.flush();
byte[] img_in_bytes = baos.toByteArray();
baos.close();
Then I converted these bytes back to png image using the following code.
BufferedImage final_img = ImageIO.read(new ByteArrayInputStream(img_in_bytes));
File output_file = new File("Stegano2.png");
ImageIO.write(final_img, "png", output_file);
It is perfectly fine if i just execute this piece of code. But if i try to modify some of the bytes in between, say like this :
Insert_number_to_image(image_in_bytes, 10);
and my method "Inset_number_to_image" goes like this :
static void Insert_number_to_image(byte[] image, int size){
byte[] size_in_byte = new byte[4];
size_in_byte[0] = (byte)(size >>> 0);
size_in_byte[1] = (byte)(size >>> 8);
size_in_byte[2] = (byte)(size >>> 16);
size_in_byte[3] = (byte)(size >>> 24);
byte temp;
int count = 0;
for(int i=0; i<4; i++)
{
for(int j=0; j<8; j++)
{
temp = size_in_byte[i];
temp = (byte)(temp >>> j);
temp = (byte)(temp & 1);
if(temp == 1)
{
image[count] = (byte)(image[count] | 1);
}
else if(temp == 0)
{
image[count] = (byte)(image[count] & (byte)(~(1)));
}
count++;
}
}
}
then after that, when i save the modified byte array as png image using same code mentioned above, i am getting this error :
Exception in thread "main" java.lang.IllegalArgumentException: image == null!
at javax.imageio.ImageTypeSpecifier.createFromRenderedImage(ImageTypeSpecifier.java:925)
at javax.imageio.ImageIO.getWriter(ImageIO.java:1591)
at javax.imageio.ImageIO.write(ImageIO.java:1520)
at Steganography.main(Steganography.java:211)
What you're using is the raw bytestream of a PNG image. PNG is a compressed format, where the bytestream doesn't reflect any of the pixel values directly and even changing one byte might irreversibly corrupt the file.
What you want instead is to extract the pixel data to a byte array with
byte[] pixels = ((DataBufferByte) img.getRaster().getDataBuffer()).getData();
Now you can modify the pixel values however you want. When you are ready to save that back to a file, convert the pixel byte array to a BufferedImage by putting your pixel array in a DataBufferByte object and passing that to a WriteableRaster, which you then use to create a BufferedImage.
Your method would work for formats where the raw bytestream does directly represent the pixels, such as in BMP. However, even then you'd have to skip the first few bytes to avoid corrupting the header.
Related
I was wondering if it is possible to create a byte array that can be decoded to a Bitmap.
For example, suppose I have a byte array of 100 elements. Could I somehow transform it into an image? I read about headers and whatnots for png, and markers for jpges, but I'm still rather clueless ...
Maybe take 4 bytes at a time and create pixels?
Your question: "create png/jpg from random byte[]"; literally possible though not directly:
You will have to use your own logic first to create a Bitmap Object from that byte[]. But along with the byte[] you will also need to have the bitmaps width, height and bytes per pixel (Bitmap.Config) predefined. You will also have to ensure the size of the byte[] matches the predefined values or accept the blank space in the Bitmap, i.e.:
byte[] b = new byte[width*height*bytesPerPixel];
...so 100 bytes could give you a 5 X 5 pixel bitmap with 4 bytes per pixel. Then you create a Bitmap Object:
Bitmap bitmap = Bitmap.create(width, height, Bitmap.Config.<whatever>)
...where <whatever> will depend on bytes per pixel. Example for 4 bytes per pixel it would be ARGB_8888.
Then run through every pixel in bitmap using two for loops, and pull out bytesPerPixel (assuming 4 now) number of bytes from b, generate a pixel colour and draw the pixel to it:
// Create a Canvas Object;
Canvas c = new Canvas(bitmap);
// Value to index the byte[];
int bI = 0;
// Paint Object for drawing the pixel;
Paint p = new Paint();
// Iterate through the pixel rows;
for(int i = 0; i < height; i++) {
// Iterate through the pixels in the row;
for(int j = 0; j < width; j++) {
// Pull out 4 bytes and generate colour int;
// This entire statement depends on bytesPerPixel;
int colorInt = Color.argb(b[bI++], b[bI++], b[bI++], b[bI++]);
// Set the colour on the Paint;
p.setColor(colorInt);
// Draw the pixel;
c.drawPoint(j, i, p);
}
}
Now your Bitmap will be present in memory so to save it to a file:
try {
// Create a File Object;
File file = new File(<file path>)
// Ensure that the file exists and can be written to;
if(!file.exists()) {
file.createNewFile();
}
// Create a FileOutputStream Object;
FileOutputStream fos = new FileOutputStream(file);
// Write the Bitmap to the File, 100 is max quality but
// it is ignored for PNG since that is lossless;
bitmap.compress(Bitmap.CompressFormat.PNG, 100, fos)
// Clear the output stream;
fos.flush();
// Close the output stream;
fos.close();
} catch (Exception e) {}
Hello I was curious on how data can be downloaded in java, so I looked through few methods and decided to use BufferedInputStream.
Now when I download, I download the file by 1024 bytes burst, and everytime it downloads 1kb I concat the temp array to the main data array.tH
I use this concat method:
public static byte[] concat(byte[] data, byte[] bytes) {
byte[] result = Arrays.copyOf(data, data.length + bytes.length);
System.arraycopy(bytes, 0, result, data.length, bytes.length);
return result;
}
This is my download process:
URL target = new URL ("http://freshhdwall.com/wp-content/uploads/2014/08/Image-Art-Gallery.jpg");
URLConnection t = target.openConnection();
t.setRequestProperty("User-Agent", "NING/1.0");
t.connect();
BufferedInputStream stream = new BufferedInputStream(t.getInputStream());
final byte[] bytes = new byte[1024];
int count;
byte[] data = new byte[0];
while ( (count = stream.read(bytes, 0, bytes.length)) != -1) {
System.out.println(count);
data = concat(data, bytes);
}
Now after downloading, I convert the bytes array to BufferedImage using ByteArrayInputStream:
InputStream s = new ByteArrayInputStream(data);
BufferedImage m = ImageIO.read(s);
And then I display the result:
JFrame j = new JFrame();
j.add(new JLabel(new ImageIcon(m)));
j.pack();
j.setVisible(true);
Now the result image looks like this:
(source: gyazo.com)
As you see, the image looks broken, missing bytes when downloading.
This is the real image:
img http://freshhdwall.com/wp-content/uploads/2014/08/Image-Art-Gallery.jpg
What did I do wrong that it displays the image like that?
On each loop iteration, you potentially read less than bytes.length bytes. As such, you can't use the full length of the array. You need to use exactly that part that was actually read.
One solution is to use
while ((count = stream.read(bytes, 0, bytes.length)) != -1) {
System.out.println(count); // this should hint at this
data = concat(data, bytes, count); // use the count
}
and
public static byte[] concat(byte[] data, byte[] bytes, int count) {
byte[] result = Arrays.copyOf(data, data.length + count);
System.arraycopy(bytes, 0, result, data.length, count);
return result;
}
so as to only copy over the bytes you've actually received.
Consider using some of the solutions here. They are probably more efficient or, at least, more readable.
Bear with me as Im very new with working with audio and I have been googling for days for a solution and not finding any.
So i retrieve the byte array of a .wav file with this (source: Wav file convert to byte array in java)
ByteArrayOutputStream out = new ByteArrayOutputStream();
BufferedInputStream in = new BufferedInputStream(new FileInputStream(WAV_FILE));
int read;
byte[] buff = new byte[1024];
while ((read = in.read(buff)) > 0)
{
out.write(buff, 0, read);
}
out.flush();
byte[] audioBytes = out.toByteArray();
And then i convert the byte array to a float array and normalize it from -1.0 to 1.0. (source: Convert wav audio format byte array to floating point)
ShortBuffer sbuf =
ByteBuffer.wrap(audioBytes).order(ByteOrder.LITTLE_ENDIAN).asShortBuffer();
short[] audioShorts = new short[sbuf.capacity()];
sbuf.get(audioShorts);
float[] audioFloats = new float[audioShorts.length];
for (int i = 0; i < audioShorts.length; i++) {
audioFloats[i] = ((float)audioShorts[i])/0x8000;
}
return audioFloats;
Later i convert this to line drawings which outputs the waveform using java.swing
class Panel2 extends JPanel {
float[] audioFloats;
Dimension d;
public Panel2(Dimension d, float[] audioFloats) {
// set a preferred size for the custom panel.
this.d = d;
setPreferredSize(d);
this.audioFloats = audioFloats;
}
#Override
public void paint(Graphics g) {
//super.paintComponent(g);
super.paint(g);
//shift by 45 because first 44 bytes used for header
for (int i = 45; i<audioFloats.length; i++){
Graphics2D g2 = (Graphics2D) g;
float inc = (i-45)*((float)d.width)/((float)(audioFloats.length-45-1));
Line2D lin = new Line2D.Float(inc, d.height/2, inc, (audioFloats[i]*d.height+d.height/2));
g2.draw(lin);
}
}
}
The waveform only looks right for 16 bit wav files (ive cross checked with goldwave and both my waveform and their waveform look similar for 16 bits).
How do i do this for 8 bit .wav files?
Because this is for homework, my only restriction is read the wav file byte by byte.
I also know the wav files are PCM coded and have the first 44 bytes reserved as the header
You need to adapt this part of the code:
ShortBuffer sbuf =
ByteBuffer.wrap(audioBytes).order(ByteOrder.LITTLE_ENDIAN).asShortBuffer();
short[] audioShorts = new short[sbuf.capacity()];
sbuf.get(audioShorts);
float[] audioFloats = new float[audioShorts.length];
for (int i = 0; i < audioShorts.length; i++) {
audioFloats[i] = ((float)audioShorts[i])/0x8000;
}
You don't need ByteBuffer at all—you already have your byte array. So just convert it to floats:
float[] audioFloats = new float[audioBytes.length];
for (int i = 0; i < audioBytes.length; i++) {
audioFloats[i] = ((float)audioBytes[i])/0x80;
}
Audio streams are usually interleaved with one channel of data then the opposite channel of data. So for example the first 16 bits would be the left channel, then the next 16 bits would be the right channel. Each of these is considered 1 frame of data. I would make sure that your 8 bit stream is only one channel because it looks like the methods are only set up to read one channel.
Also in your example to convert the frames you are grabbing the individual channel as a short then finding a decimal by dividing that by 0x8000 hex or the maximum value of a signed short.
short[] audioShorts = new short[sbuf.capacity()];
sbuf.get(audioShorts);
...
audioFloats[i] = ((float)audioShorts[i])/0x8000;
My guess is that you need to read the 8 byte stream as a type 'byte' instead of a short then divide that by 128 or the maximum value of a signed 8 bit value. This will involve making a whole new method that processes 8 bit streams instead of 16 bit streams. With the following changes.
byte[] audioBytes = new byte[sbuf.capacity()];
sbuf.get(audioBytes);
...
audioFloats[i] = ((float)audioBytes[i])/0x80;
this is what I've gone so far and I can't seem to go further because I don't understand bitwise operations on the RGB
// Read from a file
File file = new File("src/a.gif");
image = ImageO.read(file);
int[] rgbarr = image.getRGB(0, 0, 13, 15, null, 0, 13);// image is 13*15
System.out.println(rgbarr.length);
for (int i : rgbarr)
{
System.out.println(i);
}
Output: was values such as -16777216 and -1 Because I've already made an image black and white just to ease my understanding
But in our case here let's suppose it would be just random image , how do I get from normal RGB image to binary values (0 or 1) for each pixel.
I'm betting that each int in the array is a packed ARGB value;
boolean [] output = new boolean[rgbarr.length];
for ( int i=0; i<rgbarr.length; ++i ) {
int a = (rgbarr[i] >> 24 ) & 0x0FF;
int r = (rgbarr[i] >> 16 ) & 0x0FF;
int g = (rgbarr[i] >> 8 ) & 0x0FF;
int b = (rgbarr[i] ) & 0x0FF;
output[i] = (0.30*r + 0.59*g + 0.11*b) > 127;
}
The output equation I choose was taken from a definition of Luminance from wikipedia. You can change the scales so long as the coefficients add up to 1.
You can do some thing like this,
BufferedImage img = (BufferedImage) image;
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ImageIO.write(img , "jpg", baos);
byte[] previewByte = baos.toByteArray();
Hope this helps!!
You already have RGB values in the int[], that is color of each pixel. You could compare this to a specific background color, like black, to get a 1-bit pixel value. Then maybe set a bit in another appropriately sized byte[] array...
BufferedImage bufferedImage = ImageIO.read(new File("/...icon.jpg"));
// this writes the bufferedImage into a byte array called resultingBytes
ByteArrayOutputStream byteArrayOut = new ByteArrayOutputStream();
ImageIO.write(bufferedImage, "jpg", byteArrayOut);
byte[] resultingBytes = byteArrayOut.toByteArray();
I use the above code to get a JEPG image as a byte array. I want to know what exactly is in this byte array. Does this array contain any file header information or just pixel values? And for example, if I want to reverse this image's color, what is a good way to do so?
Thanks so much!
It's a complete JPEG file, in memory.
EDIT: If you want to manipulate pixel data as an array, you may find Raster more helpful:
E.g.:
Raster raster = bufferedImage.getData();
You can then call one of the Raster.getPixels methods.
The ByteArrayOutputStream contains whatever you wrote to it. Nothing more, nothing less. So your question is really about ImageIO.write(). Which writes out an encoding of an image according to the encoding type you supply. Which was JPEG.
Here is how you read real pixel values. The JPEG information is much harder to do anything with!
public static void main(String... args) throws IOException {
String u = "http://blog.stackoverflow.com/wp-content/uploads/stackoverflow-logo-300.png";
BufferedImage old = ImageIO.read(new URL(u));
BufferedImage inverted = new BufferedImage(old.getWidth(),
old.getHeight(),
BufferedImage.TYPE_INT_RGB);
for (int y = 0; y < old.getHeight(); y++) {
for (int x = 0; x < old.getWidth(); x++) {
Color oldColor = new Color(old.getRGB(x, y));
// reverse all but the alpha channel
Color invertedColor = new Color(255 - oldColor.getRed(),
255 - oldColor.getGreen(),
255 - oldColor.getBlue());
inverted.setRGB(x, y, invertedColor.getRGB());
}
}
ImageIO.write(inverted, "png", new File("test.png"));
}