JMagick - How to convert a picture from CMYK to RGB? - java

I know there exists another post dealing with that problem How to convert colorspace using JMagick? but but there is something I do not understand:
String baseName = "Pictures/";
String fileName = "dragon.gif";
MagickImage imageCMYK;
try {
ImageInfo info = new ImageInfo( baseName + fileName);
info.setColorspace(ColorspaceType.CMYKColorspace);
System.out.println("ColorSpace BEFORE => " + info.getColorspace());
imageCMYK = new MagickImage( info );
System.out.println("ColorSpace AFTER => " +
imageCMYK.getColorspace());
When I create the new MagickImage, the CMYKColorSpace is not kept as I obtain :
ColorSpace BEFORE => 12 (CMYK)
How to correctly convert a picture from CMYK to RGB ?
Thanks.
ColorSpace AFTER => 1 (RGB)

Update: You are using GIF images. They don't support "CMYK" so the transform won't work for you (see this forum post at imagemagick's web site)!
Use MagicImage.rgbTransformImage(ColorspaceType.CMYKColorspace). From the API:
public boolean rgbTransformImage(int colorspace) throws MagickException
Converts the reference image from RGB to an alternate colorspace. The transformation matrices are not the standard ones: the weights are rescaled to normalized the range of the transformed values to be [0..MaxRGB].
Example:
try {
MagickImage image = new MagickImage(new ImageInfo(baseName + fileName));
if (!image.rgbTransformImage(ColorspaceType.CMYKColorspace))
throw new Exception("Couldn't convert image color space");
...
} catch (MagickException e) {
...
}

This still won't work for other image formats such as PNG.

Related

Is there a way to get Java to read a PNG as TYPE_INT_RGB / TYPE_INT_ARGB?

When I read a PNG image in Java using javax.imageio.ImageIO.read(), the resulting BufferedImage is of TYPE_3BYTE_BGR or TYPE_4BYTE_ABGR depending on transparency.
I'm processing very large images (64+ megapixels), and need them in TYPE_INT_RGB / TYPE_INT_ARGB format, which requires an expensive and very-large-chunk-of-memory hogging repainting of the image onto a new image in the correct format, which is causing OOMs.
It would be much better if I could somehow persuade ImageIO to read the image in the desired format from the get-go - is there any way of doing that? Thanks!
Yes, it is possible to read into a predefined type of BufferedImage, given that the type is supported by and compatible with the reader plugin. Most often the TYPE_#BYTE_* types are compatible with the TYPE_INT_* types, and this is the case for the standard PNGImageReader.
To make it work, you need access to the ImageReader directly, and use the read method that takes an ImageReadParam to control the type of image. It's possible to read into a pre-allocated image by using the ImageReadParam.setDestination(..) method, or to just specify the type of image and let the reader plugin allocate it for you by using ImageReadParam.setDestinationType(..) like I will show below.
Here's a short stand-alone code sample that shows how to read into a specific image type:
public static void main(String[] args) throws IOException {
File input = new File(args[0]);
try (ImageInputStream stream = ImageIO.createImageInputStream(input)) {
// Find a suitable reader
Iterator<ImageReader> readers = ImageIO.getImageReaders(stream);
if (!readers.hasNext()) {
throw new IIOException("No reader for " + input);
}
ImageReader reader = readers.next();
try {
reader.setInput(stream);
// Query the reader for types and select the best match
ImageTypeSpecifier intPackedType = getIntPackedType(reader);
System.out.println("intPackedType = " + intPackedType);
// Pass the type to the reader using read param
ImageReadParam param = reader.getDefaultReadParam();
param.setDestinationType(intPackedType);
// Finally read the image
BufferedImage image = reader.read(0, param);
System.out.println("image = " + image);
}
finally {
reader.dispose();
}
}
}
private static ImageTypeSpecifier getIntPackedType(ImageReader reader) throws IOException {
Iterator<ImageTypeSpecifier> types = reader.getImageTypes(0);
while (types.hasNext()) {
ImageTypeSpecifier spec = types.next();
switch (spec.getBufferedImageType()) {
case BufferedImage.TYPE_INT_RGB:
case BufferedImage.TYPE_INT_ARGB:
return spec;
default:
// continue searching
}
}
return null;
}
Sample output from one of my runs using a PNG as input:
intPackedType = javax.imageio.ImageTypeSpecifier$Packed#707084ba
image = BufferedImage#45ff54e6: type = 2 DirectColorModel: rmask=ff0000 gmask=ff00 bmask=ff amask=ff000000 IntegerInterleavedRaster: width = 100 height = 100 #Bands = 4 xOff = 0 yOff = 0 dataOffset[0] 0
Where type = 2 means BufferedImage.TYPE_INT_ARGB.

How to encode RGB values for a P6 ppm?

I'm trying to create a ppm image file using the P6 encoding. I'm using this code to create the file:
private static void writeImage(String image, String outputPath) {
try (PrintWriter out = new PrintWriter(outputPath + "output.pbm")) {
out.println(image);
} catch (FileNotFoundException e) {
System.out.println(e.getMessage());
}
}
Now all I need to do is build the text that represents the image in the P6 format. Building the header is easy, but despite experimenting and searching, I can't seem to figure out how to convert the RGB values for each pixel into a string I can add to the file.
My question is this:
How do I take an RGB value (for example(red=255, blue=192, green=0)) and get a String representation that will be correctly recognized in an image in the P6 format?
Solution:
Credit to Solomon Slow's comment for helping me figure this out. This is the solution I came up with for those who want details. I now use this function to create and output the file:
private static void writeImage(String header, List<Byte> image, String filepath) {
try (FileOutputStream out = new FileOutputStream(filepath)) {
out.write(header.getBytes(StandardCharsets.UTF_8));
for(byte b:image) {
out.write(b);
}
} catch (IOException e) {
System.out.println(e.getMessage());
throw new TerminateProgram();
}
}
The header I pass in is defined like this in another function:
String header = "P6" + "\n" +
width + " " +
height + "\n" +
"255" + "\n";
Finally, I build a list of byte values using an ArrayList, adding each pixel like so:
List<Byte> image = new ArrayList<>();
// red, green, blue already defined as ints from 0 to 255
image.add((byte)(red));
image.add((byte)(green));
image.add((byte)(blue));
From, http://netpbm.sourceforge.net/doc/ppm.html
Each pixel is a triplet of red, green, and blue samples, in that order. Each sample is represented in pure binary by either 1 or 2 bytes. The most significant byte is first.
That means, the ppm file is not a text file. You probably should be using a FileOutputStream instead of a PrintWriter.
It's going to be a little tricky because Java's byte data type is signed. You'll need to have int values in the range [0..255] for the red, green, and blue levels, and then cast those to byte. Maybe see java unsigned byte to stream
As for the text header of the file, the approach I would use would be to build a String representation of the header, and then call header.getBytes() to turn it into a byte array that you can write to the FileOutputStream.

CanĀ“t get absoluteX and absoluteY from image using itext

I try to get a image (jpg format) from url to put in my pdf using itext 5.0.5 with this simple code below:
Image imageToShow = null;
imageToShow = Image.getInstance(new URL("any image url here"));
imageToShow.scaleAbsolute(size[0], size[1]);
I get the image but the value from absoluteX and absoluteY is always a 'NaN' value and this problem prevents me to change this values (third line), what I'm doing wrong?
If you are in a situation where you can switch to another iText version, try out iText7.
The equivalent code would be:
// src url
String somePath = "https://www.pdfa.org/wp-content/uploads/2016/08/ITSC-Logo-Horizontal-RGB-300dpi.png";
// fetch image data
ImageData imageData = ImageDataFactory.create(new URL(somePath));
// turn into image object
Image image = new Image(imageData);
// perform scaling operation
image = image.scaleAbsolute(120f, 120f);
// debug
System.out.println(image.getImageWidth() + "x" + image.getImageHeight());

Convert PNG to bitonal TIFF

-Edit-
FYI.. I am converting b&w documents scanned in as greyscale or color.
1)The first solution worked, it just reversed black & white (black background, white text). It also took nearly 10 minutes.
2)The JAI solution in the 2nd answer didn't work for me. I tried it before posting here.
Has anyone worked with other libraries free or pay that handle image manipulation well?
-Original-
I am trying to convert an PNG to a bitonal TIFF using Java ImageIO. Has anyone had any luck doing this? I have got it to convert from PNG to TIFF. I am not sure if I need to convert the BufferedImage (PNG) that I read in or convert on the TIFF as I write it. I have searched and searched but nothing seems to work? Does anyone have an suggestions where to look?
Here is the code that converts...
public static void test() throws IOException {
String fileName = "4848970_1";
// String fileName = "color";
String inFileType = ".PNG";
String outFileType = ".TIFF";
File fInputFile = new File("I:/HPF/UU/" + fileName + inFileType);
InputStream fis = new BufferedInputStream(new FileInputStream(fInputFile));
ImageReaderSpi spi = new PNGImageReaderSpi();
ImageReader reader = spi.createReaderInstance();
ImageInputStream iis = ImageIO.createImageInputStream(fis);
reader.setInput(iis, true);
BufferedImage bi = reader.read(0);
int[] xi = bi.getSampleModel().getSampleSize();
for (int i : xi) {
System.out.println("bitsize " + i);
}
ImageWriterSpi tiffspi = new TIFFImageWriterSpi();
TIFFImageWriter writer = (TIFFImageWriter) tiffspi.createWriterInstance();
// TIFFImageWriteParam param = (TIFFImageWriteParam) writer.getDefaultWriteParam();
TIFFImageWriteParam param = new TIFFImageWriteParam(Locale.US);
String[] strings = param.getCompressionTypes();
for (String string : strings) {
System.out.println(string);
}
param.setCompressionMode(ImageWriteParam.MODE_EXPLICIT);
param.setCompressionType("LZW");
File fOutputFile = new File("I:\\HPF\\UU\\" + fileName + outFileType);
OutputStream fos = new BufferedOutputStream(new FileOutputStream(fOutputFile));
ImageOutputStream ios = ImageIO.createImageOutputStream(fos);
writer.setOutput(ios);
writer.write(null, new IIOImage(bi, null, null), param);
ios.flush();
writer.dispose();
ios.close();
}
I have tried changing the compression to type "CCITT T.6" as that appears to be what I want, but I get an error " Bits per sample must be 1 for T6 compression! " Any suggestion would be appreciated.
Most likely, you want something like this to convert to 1 bit before you save to TIFF with CCITT compression.
To expound a little bit - be aware that converting from other bit depths to 1 bit is non-trivial. You are doing a data reduction operation and there are dozens of domain specific solutions which vary greatly in output quality (blind threshold, adaptive threshold, dithering, local threshold, global threshold and so on). None of them are particularly good at all image types (adaptive threshold is pretty good for documents, but lousy for photographs, for example).
As plinth said, you have to do the conversion, Java won't do it magically for you...
If the PNG image is already black & white (as it seems, looking at your comment), using a threshold is probably the best solution.
Somebody seems to have the same problem: HELP: how to compress the tiff. A solution is given on the thread (untested!).

How do I convert images between CMYK and RGB in ColdFusion (Java)?

I have a need to convert images from CMYK to RGB - not necessarily back again, but hey, if it can be done...
With the release of ColdFusion 8, we got the CFImage tag, but it doesn't support this conversion; and nor does Image.cfc, or Alagad's Image Component.
However, it should be possible in Java; which we can leverage through CF. For example, here's how you might create a Java thread to sleep a process:
<cfset jthread = createObject("java", "java.lang.Thread")/>
<cfset jthread.sleep(5000)/>
I would guess a similar method could be used to leverage java to do this image conversion, but not being a Java developer, I don't have a clue where to start. Can anyone lend a hand here?
A very simple formula for converting from CMYK to RGB ignoring all color profiles is:
R = ( (255-C)*(255-K) ) / 255;
G = ( (255-M)*(255-K) ) / 255;
B = ( (255-Y)*(255-K) ) / 255;
This code requires CMYK values to be in rage of 0-255. If you have 0 to 100 or 0.0 to 1.0 you'll have to convert the values.
Hope this will get you started.
As for the java and ColdFusion interfacing, I'm sorry, but I have no idea how to do that.
I use the Java ImageIO libraries (https://jai-imageio.dev.java.net). They aren't perfect, but can be simple and get the job done. As far as converting from CMYK to RGB, here is the best I have been able to come up with.
Download and install the ImageIO JARs and native libraries for your platform. The native libraries are essential. Without them the ImageIO JAR files will not be able to detect the CMYK images. Originally, I was under the impression that the native libraries would improve performance but was not required for any functionality. I was wrong.
The only other thing that I noticed is that the converted RGB images are sometimes much lighter than the CMYK images. If anyone knows how to solve that problem, I would be appreciative.
Below is some code to convert a CMYK image into an RGB image of any supported format.
Thank you,
Randy Stegbauer
package cmyk;
import java.awt.color.ColorSpace;
import java.awt.image.BufferedImage;
import java.awt.image.ColorConvertOp;
import java.io.File;
import java.io.IOException;
import javax.imageio.ImageIO;
import org.apache.commons.lang.StringUtils;
public class Main
{
/**
* Creates new RGB images from all the CMYK images passed
* in on the command line.
* The new filename generated is, for example "GIF_original_filename.gif".
*
*/
public static void main(String[] args)
{
for (int ii = 0; ii < args.length; ii++)
{
String filename = args[ii];
boolean cmyk = isCMYK(filename);
System.out.println(cmyk + ": " + filename);
if (cmyk)
{
try
{
String rgbFile = cmyk2rgb(filename);
System.out.println(isCMYK(rgbFile) + ": " + rgbFile);
}
catch (IOException e)
{
System.out.println(e.getMessage());
}
}
}
}
/**
* If 'filename' is a CMYK file, then convert the image into RGB,
* store it into a JPEG file, and return the new filename.
*
* #param filename
*/
private static String cmyk2rgb(String filename) throws IOException
{
// Change this format into any ImageIO supported format.
String format = "gif";
File imageFile = new File(filename);
String rgbFilename = filename;
BufferedImage image = ImageIO.read(imageFile);
if (image != null)
{
int colorSpaceType = image.getColorModel().getColorSpace().getType();
if (colorSpaceType == ColorSpace.TYPE_CMYK)
{
BufferedImage rgbImage =
new BufferedImage(
image.getWidth(), image.getHeight(), BufferedImage.TYPE_3BYTE_BGR);
ColorConvertOp op = new ColorConvertOp(null);
op.filter(image, rgbImage);
rgbFilename = changeExtension(imageFile.getName(), format);
rgbFilename = new File(imageFile.getParent(), format + "_" + rgbFilename).getPath();
ImageIO.write(rgbImage, format, new File(rgbFilename));
}
}
return rgbFilename;
}
/**
* Change the extension of 'filename' to 'newExtension'.
*
* #param filename
* #param newExtension
* #return filename with new extension
*/
private static String changeExtension(String filename, String newExtension)
{
String result = filename;
if (filename != null && newExtension != null && newExtension.length() != 0);
{
int dot = filename.lastIndexOf('.');
if (dot != -1)
{
result = filename.substring(0, dot) + '.' + newExtension;
}
}
return result;
}
private static boolean isCMYK(String filename)
{
boolean result = false;
BufferedImage img = null;
try
{
img = ImageIO.read(new File(filename));
}
catch (IOException e)
{
System.out.println(e.getMessage() + ": " + filename);
}
if (img != null)
{
int colorSpaceType = img.getColorModel().getColorSpace().getType();
result = colorSpaceType == ColorSpace.TYPE_CMYK;
}
return result;
}
}
The tag cfx_image may be of use to you. I haven't used it in a while but I remember it had a ton of features.
Alternatively, you might be able to script a windows app such as Irfanview (via commandline using cfexecute) to process images.
Hope that helps
I know that this question is old, but I still encounter problems with CMYK images & ColdFusion. However, I just read a CMYK JPEG image using ColdFusion 10 and resaved it. The saved image was able to to be read using ColdFusion 9 (which is only capable of reading RGB JPEGs.) I'm not sure if this conversion is intentional or not and I don't currently have any way of identifying whether the source image's color profile is CMYK or not as the saved color profile still appears to be the same.
<cfset imgData = ImageRead(expandPath("./CMYK_image.jpg"))>
<cfset ImageWrite(imgData, expandPath("./Saved_image.jpg"))>

Categories