I have a situation where I would like to do some very light image file obfustication. My application ships with a bunch of .png files and I'd like it if they weren't so readily editable.
I'm not looking for a 'secure' solution (I don't believe one really exists), I'd just like Joe Public to be unable to edit the files.
I am currently using;
ImageIO.read(new File("/images/imagefile.png"));
I'd rather not have to use Serialisation, as the ImageIO system is pretty deeply ingrained in the code, each image needs also to remain as its own file on disk.
I was hoping I could just change the file extension eg;
ImageIO.read(new File("/images/imagefile.dat"));
But ImageIO seems to use it to identify the file. Can I tell ImageIO that it is a PNG despite its extension?
Encrypt all the files on disk.
Then in the program, decrypt a file, load it in memory and go rocking.
Java image I/O uses the Service Provider Interface to support new image formats1. I believe it might be possible to add a new decoder using a file extension. If that is the case, there is the route to providing an easily pluggable reader for a custom image format.
Note that you will probably need to change the file extension in the source. That might be the job for an advanced IDE, or a one-time search and replace using grep.
As to the format, one extremely simple way make media files unreadable in common readers is to write the bytes of the image in reverse order. Then flip them back after read, put them in a ByteArrayInputStream, and pass them to ImageIO.read(InputStream).
After you have written the service provider and Jar'd it properly (using a manifest with attributes to identify the file/content type it handles, and the corresponding encoder/decoder), add it to the run-time class-path of the app., and it should be able to read the custom image format.
...or keep all images in a single file and seek() to the start position of each image as you load. You can do this by pre-seeking against a FileInputStream, or conversely by creating a ByteArrayInputStream for ImageIO.read(InputStream).
You could try this:
Iterator rs = ImageIO.getImageReadersByFormatName("png");
ImageReader ir = (ImageReader) rs.next();
File srcFile = new File("/images/imagefile.dat");
ImageInputStream iis = ImageIO.createImageInputStream(srcFile);
ir.setInput(iis);
Related
I'm a bit of a noob when it comes to java, and I've been trying to do my best to learn as much about the language as possible. Recently I've started learning the logic behind analyzing images pixel by pixel for their RGB data. Whilst doing this I stumbled upon svs files which are these extremely high quality files that are basically multilayered tiffs.I've explored several open source projects that decode and display .svs images, but couldn't find the algorithms or code in which they decoded the .svs files. Could someone direct me to what file(s) inside of the open source project that would contain the algorithm to decode an svs file, as I'm deeply interested in how one would go about decoding such a large and complex image file, or could someone help me with an algorithm to decode a .svs file in Java. Thanks in advance!
links:
https://github.com/openslide/openslide-java
https://github.com/imagej/imagej
SVS files are produced by Aperio scanners, so you need the Aperio decoder from openslide:
https://github.com/openslide/openslide/blob/master/src/openslide-vendor-aperio.c
openslide have some docs on the format here:
http://openslide.org/formats/aperio/
It's interesting to understand the details, but perhaps not very useful. If you want to read it yourself from Java, you can use the excellent openslide binding, or a libtiff binding plus a lot of extra code.
As mentioned, the .svs files created by Aperio, are just TIFF files (with some limitations, and some minor extensions).
It is not clear from the question whether you just want to read images from such files using an existing library, or if you want to develop such a solution for yourself (for educational purposes or otherwise).
If the latter is the case, you really should read the TIFF 6.0 specification along with the Adobe Tech Notes specifying "new" JPEG compression and and what you can find documented about the Aperio SVS format. You can also look at the source code of existing libraries. Describing the steps necessary to implement a TIFF/SVS reader/decoder from scratch is way beyond the scope of a StackOverflow answer.
If, on the other hand, you just want to open such a file in Java, you should be able to open most of them simply by using ImageIO and a TIFF plugin*.
Code could be as simple as:
BufferedImage image = ImageIO.read(new File("path/to/your.svs"));
This will read the first, and according to what I understand from the specification, the full resolution image in the file.
To read specific image (or, all images if you add a loop) in the file, the code becomes a little more verbose:
// Create input stream
File file = new File("path/to/your.svs");
try (ImageInputStream input = ImageIO.createImageInputStream(file)) {
// Get the reader
Iterator<ImageReader> readers = ImageIO.getImageReaders(input);
if (!readers.hasNext()) {
throw new IllegalArgumentException("No reader for: " + file);
}
ImageReader reader = readers.next();
try {
reader.setInput(input);
// Optionally, listen for read warnings, progress, etc.
reader.addIIOReadWarningListener(...);
reader.addIIOReadProgressListener(...);
// Use reader.getNumImages(true) to get the number of images
// in the file, and optionally add a loop to read all the images.
ImageReadParam param = reader.getDefaultReadParam();
// Optionally, control read settings like subsampling, source region or destination etc.
param.setSourceSubsampling(...);
param.setSourceRegion(...);
param.setDestination(...);
// ...
// Finally read the image, using settings from param
BufferedImage image = reader.read(0, param);
// Optionally, read thumbnails, etc...
int numThumbs = reader.getNumThumbnails(0);
// ...
// Optionally, get the image metadata (ie. to get the custom Aperio
// values from the ImageDescription tag for further processing)
IIOMetadata metadata = reader.getImageMetadata(0);
// ...
}
finally {
// Dispose reader in finally block to avoid memory leaks
reader.dispose();
}
}
This will allow you to skip images that are too large to display in Java (.svs files may contain images that are too large for a BufferedImage...) or have a custom compression not supported by the reader (.svs files may contain images compressed both in baseline JPEG and custom JPEG 2000).
You probably need to read up on the specification to see the order of images, what image is a "thumbnail", a "macro" and a "label" image. I think the "thumbnail" is always a JPEG stream.
*) TIFF plugins for ImageIO:
The most common TIFF plugin for ImageIO is JAI ImageIO (jai_imageio.jar), but it is no longer in development, and I have not tested it with .svs files.
My own project, TwelveMonkeys ImageIO, is actively developed and contains a TIFF plugin that aims to be compatible with the one from JAI, but fixing bugs and adding missing features. I have tested this plugin with some sample .svs files, and it can read them, except the ones having a non-standard (as in "not in the TIFF specification") JPEG 2000 compression.
There are TIFF plugins that I know of, but I haven't tried any of these.
There might also be special purpose Aperio SVS plugins available, that I don't know of.
The use case I'm investigating is to read a an input image (tif), modify it and write it to an output image (tif) .
I'm looking for a convenient way to ensure that the writer uses the same compression settings etc as the original image. I haven't found a convenient way to achieve this with ImageIO (with JAI plugin) .
I tried probing the imageReader.getDefaultReadParam() but it doesn't contain anything of note. The imageReader.getImageMetadata(...) contains info deep inside but the data structure organization is hopeless.
My fallback approach at this time is to use Apache Commons to detect the compression (eg. Imaging.getImageInfo(inputFile).getCompressionAlgorithm()) and logically set the writer compression parameter in ImageIO.
Is there a more elegant way to accomplish this without having to jump through this hoop ?
As an aside, I can't use Apache Commons Imaging exclusively for our image processing task because it doesn't yet completely support some other formats I need eg. writing jpg
Thanks
It seems like the method you are looking for is:
ImageIO.getImageWriter(ImageReader)
From the API doc:
This mechanism may be used to obtain an ImageWriter that will understand the internal structure of non-pixel metadata (as encoded by IIOMetadata objects) generated by the ImageReader. By obtaining this data from the ImageReader and passing it on to the ImageWriter obtained with this method, a client program can read an image, modify it in some way, and write it back out preserving all metadata, without having to understand anything about the structure of the metadata, or even about the image format.
From the documentation, it seems you still have to pass the metadata from the reader to the writer, but at least, you don't have to care about getting the compression (or other) settings from the metadata.
The easiest way to achieve this is probably to do something like:
ImageReader reader;
reader.setInput(input);
IIOImage image = reader.readAll(0, null); // Read image and metadata in one go
doStuffWithImage(image.getRenderedImage()); // Most likely safe to cast to BufferedImage
ImageWriter writer = ImageIO.getImageWriter(reader);
writer.setOutput(output);
ImageWriteParam param = writer.getDefaultWriteParam();
// According to the API doc, the default compressionMode is
// MODE_COPY_FROM_METADATA which is what we want :-)
writer.write(null, image, param); // Don't need stream metadata for TIFF
Lets say you have a bunch of images which you want to host on S3 and they are available in various formats: png, jpg, jpeg, gif ... etc.
Writing or using an image-processing service to normalize all image formats, down to a single one, is one approach ... but I'm wondering if its possible to use a shortcut where you can remove the extension name from a filename (after upload) because the file properties now hold the appropriate mime-type anyway?
So after I upload 1.png, 2.jpg, 3.jpeg and 4.gif ... why not programmatically change all filenames to remove the extensions and access the images as:
/my-bucket/1
/my-bucket/2
/my-bucket/3
/my-bucket/4
So, how can someone programmatically change filenames on S3 to remove filename extensions?
I would love to hack on this using substitutions to remove extensions .<ext> from filenames but I think that programmatically its only available for setting up a job for transferring data from devices that you will actually ship to Amazon.
It's not pretty, but it can be done by calling copyObject() for /myBucket/myFile.jpg and setting the new key to be /myBucket/myFile. After the copy is complete, delete the original. At this time I'm not aware of a proper "rename" method available.
I have multiple images and each image has a resolution of around 2560x10000, I want to join all these images to make one single image. I cannot use the BufferedImage method as the final image and the image I have to join will be in the memory at the same time causing OutOfMemory. So I tried a the below approach:
public static void joinJpegFiles(File infile, File outfile, float compQuality,int i) {
try {
RenderedImage renderedImage = ImageIO.read(infile);
ImageWriter Iwriter = null;
Iterator iter = ImageIO.getImageWritersByFormatName("jpeg");
if (iter.hasNext()) {
Iwriter = (ImageWriter)iter.next();
}
ImageOutputStream IOStream = ImageIO.createImageOutputStream(outfile);
Iwriter.setOutput(IOStream);
IOStream.seek( IOStream.length());
JPEGImageWriteParam JIWP=new JPEGImageWriteParam(Locale.getDefault());
JIWP.setCompressionMode(ImageWriteParam.MODE_EXPLICIT) ;
JIWP.setCompressionQuality(compQuality);
Iwriter.write(null, new IIOImage(renderedImage,null,null), JIWP);
IOStream.flush();
IOStream.close();
Iwriter.dispose();
} catch (IOException e) {
System.out.println("write error: " + e.getMessage());
}
}
This method is called for each image I want to join.
The issue with this approach is that the size of the final image is increasing and equals the sum of sizes of all images I joined, but only the first image is visible when I open the final image.
I still cant figure out what I am doing wrong and I also couldnt find any sample code to join jpegs other than the BufferedImage and ImageIO.write approach. I read at a news group that it works for tiff format but I need this to work for jpeg/png formats.
I assume you have already solved this, or worked around it somehow, but.. In case anyone else need to solve a similar problem:
It's a little unclear what you are trying to achieve here. Do you really want to create one large image, or create a single file containing multiple images?
Multiple images in single file:
Your code seems to append multiple standalone JPEG files into one file. The JPEG (JFIF) format does not support this, and most software will probably just see your file as the first JPEG with loads of junk bytes appended to it at the end. PNG does not allow storing multiple images in one file AFAIK. A format like TIFF does allow multiple images (it even allows you to store them as JPEG streams), which is probably why TIFF was brought up.
However, the JPEG standard has a concept called Abbreviated Streams, that is very much like how JPEG is usually stored in pyramidal TIFF. The ImageIO JPEGImageWriter does support this feature:
"Abbreviated streams are written using the sequence methods of ImageWriter. Stream metadata is used to write a tables-only image at the beginning of the stream, and the tables are set up for use, using ImageWriter.prepareWriteSequence. If no stream metadata is supplied to ImageWriter.prepareWriteSequence, then no tables-only image is written. If stream metadata containing no tables is supplied to ImageWriter.prepareWriteSequence, then a tables-only image containing default visually lossless tables is written."
I'm not sure how other software will interpret these kinds of files, and according to the libjpeg docs it probably won't even work:
"While abbreviated datastreams
can be useful in a closed environment, their use is strongly discouraged in
any situation where data exchange with other applications might be needed.
Caveat designer."
So.. It may or may not be appropriate for your use case.
Compose multiple images into one large image:
If on the other hand, you really want to compose multiple images into one large image (and later store as a single JPEG), you could have a look at some code I wrote long ago, to allow working with large images, without using heap memory.
It uses memory-mapped byte buffers, and might be painfully slow if you don't have enough memory to store the data in RAM. Also, the resulting BufferedImage will always be of TYPE_CUSTOM, so it will miss most potentially hardware or native acceleration you'll normally benefit from, and some operations may not work on it. However, at least you are not limited by either heap size, nor physical RAM.
In my program i want the user to be able to take some images from a directory, and save them under a single file, that can be transferred to another computer possibly, and actually read and displayed(using the same program).
How would i go about doing this, especially if i want to save other data along with it, perhaps objects and such. I know you can use the ObjectOutputStream class, but im not sure how to integrate it with images.
So overall, i want the program to be able to read/write data, objects, and images to/from a single file.
Thanks in Advance.
[EDIT - From Responses + Comment regarding Zip Files]
A zip might be able to get the job done.
But i want it to be read only be the program. ( You think making it a zip, changing the file extension would work, then when reading it just chaing it back and reading as a zip?? ) I dont want users to be able to see the contents directly.
Ill elaborate a bit more saying its a game, and users can create their own content using xml files, images and such. But when a user creates something i dont want other users to be able to see exactly how they created it, or what they used, only the end result.
You can programatically create a zip file, and read a zip file from Java, no need to expose it as a regular .zip file.
See: java.io.zip pacakge for more information, and these others for code samples on how to read/write zip using java.
Now if you want to prevent the users from unzipping this file, but you don't want to complicate your life by encrypting the content, or creating a complex format, you can emulate a simple internet message format, similar to the one used for e-mails to attach files.
You can read more about the internet message format here
This would be a custom file format only used by your application so you can do it as simple as you want. You just have to define your format.
It could be:
Header with the names ( and number ) of files in that bundle.
Followed by a list of separators ( for instance limit.a.txt=yadayada some identifier to know you have finished with that content )
Actual content
So, you create the bundle with something like the following:
public void createBundle() {
ZipOutputStream out = ....
writeHeader( out );
writeLimits( out yourFiles );
for( File f : youFiles ) {
writeFileTo( f, out );
}
out.close();
}
Sort of...
And the result would be a zipped file with something like:
filenames =a.jpg, b.xml, c.ser, d.properties, e.txt
limits.a.jpg =poiurqpoiurqpoeiruqeoiruqproi
limits.b.xml =faklsdjfñaljsdfñalksjdfa
limit.s.ser =sdf09asdf0as9dfasd09fasdfasdflkajsdfñlk
limit.d.properties =adfa0sd98fasdf90asdfaposdifasdfklasdfkñm
limit.e.txt =asdf9asdfaoisdfapsdfñlj
attachments=
<include binary data from a.jpg here>
--poiurqpoiurqpoeiruqeoiruqproi
<include binary data from b.xml here>
--faklsdjfñaljsdfñalksjdfa
etc
Since is your file format you can keep it as simple as possible or complicate your life at infinitum.
If you manage to include a MIME library in your app, that could save you a lot of time.
Finally if you want to add extra security, you have to encrypt the file, which is not that hard after all, the problems is, if you ship the encrypting code too, your users could get curious about it and decompile them to find out. But a good encrypting mechanism would prevent this.
So, depending on your needs you can go from a simple zip, a zip with a custom format, a zip with a complicated customformat or a zip with a custom complicated encrypted format.
Since that's to broad you may ask about specific parts here: https://stackoverflow.com/questions/ask
In your case I would use a ZIP library to package all the images in a ZIP file. For the metadata you want to save along with these, use XML files. XML and ZIP are quite a de-facto standard today, simple to handle and though flexible if you want to add new files or metadata. There are also serializing tools to serialize your objects into XML. (I don't know them exactly in Java, but I'm sure there are.)
Yep, just pack/unpack them with java.util.zip.* which is pretty straightforward to go. Every Windows Version since XP has built in zip support, so your good to go. There are many good (and faster) free zip libraries for java/c#, too.
I know you can use the ObjectOutputStream class, but im not sure how to integrate it with images.
Images are binary data, so reading it into a byte[] and writing the byte[] to ObjectOutputStream should work. It's however only memory hogging since every byte eats at least one byte of JVM's memory. You'll need to take this into account.