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.
Related
I'm reading a serial port from a money counter machine and I'm expecting to get a serial number of a banknote sent from the money counter.
I read the byte array from the machine and converted it to a binaryString using the code below:
public void serialEvent(SerialPortEvent serialPortEvent) {
ArrayList<String> list = new ArrayList<>();
String s1 = new String();
if (serialPortEvent.getEventType() != SerialPort.LISTENING_EVENT_DATA_AVAILABLE) {
return;
}
byte[] readBuffer = new byte[comPort.bytesAvailable()];
int numRead = comPort.readBytes(readBuffer, readBuffer.length);
//System.out.println("Read " + numRead + " bytes.");
for (Byte b : readBuffer) {
//image is more than 500 bytes, all other data is less than 500
if (numRead <= 500) {
break;
}
s1 = String.format("%8s", Integer.toBinaryString(b & 0xFF)).replace(' ', '0');
//new line byte, everytime it shows up I add a new line
if (s1.equals("01000101")) {
System.out.println();
continue;
}
System.out.print(s1);
The picture below is the screenshot from s1 String that I got from System.out and as you can see there is the serial number represented by 1s, and the background is represented by 0s.
My question now is how to convert this String to an image file?
I'm guessing I need to make an 1d or 2d array from this String and make an image where 0s represent white pixels and 1s represent black pixels, but I'm not sure how to do this and I need some help?
Thanks in advance.
EDIT:
I can get the ASCII output using this code:
public void serialEvent(SerialPortEvent serialPortEvent) {
InputStream in = comPort.getInputStream();
Reader in2 = new InputStreamReader(in, StandardCharsets.US_ASCII);
try
{
for (int j = 0; j < 10000; ++j) {
System.out.print((char)in2.read());
in.close();
}
} catch (Exception e) { e.printStackTrace(); }
comPort.closePort();
}
});
Text Output
This is the text output of the serial port.
EDIT 2:
Data that I got from the manufacturer of the machine.
Data from the manufacturer
EDIT 3:
Here is a drawn picture using Graphics Java library.
PBM File Format
You can almost directly write your ASCII art string into a PBM file.
Check the file format here: https://netpbm.sourceforge.net/doc/pbm.html
Other file format
If you prefer to create BMP | GIF | JPEG | PNG | TIFF | WBMP (the formats supported by ImageIO), follow this procedure:
First create a BufferedImage using it's constructor.
Retrieve the Graphics context from the BufferedImage.
Draw your pixels into the Graphics context.
Initialize the background using setColor(Color.white), fillRect.
For each 1 draw a black rectangle: setColor, fillRect
You just have to count your bits so you know the coordinates of the rectagle.
Finally save the BufferedImage to file using ImageIO.write()
Converting from your ascii strings could look like this:
Graphics g = ...
// assuming your string values are stored in the array strings
for(int y=0; y<strings.length, y++) {
String line = strings[y];
for(int x=0; x<line.length(); x++) {
if (line.charAt(x)=='1') {
g.setColor(Color.black);
g.fillRect(x, y, 1, 1);
}
}
}
I want to save the depth info from the arcore to the storage.
Here is the example from the developer guide.
public int getMillimetersDepth(Image depthImage, int x, int y) {
// The depth image has a single plane, which stores depth for each
// pixel as 16-bit unsigned integers.
Image.Plane plane = depthImage.getPlanes()[0];
int byteIndex = x * plane.getPixelStride() + y * plane.getRowStride();
ByteBuffer buffer = plane.getBuffer().order(ByteOrder.nativeOrder());
short depthSample = buffer.getShort(byteIndex);
return depthSample;
}
So I want to save this bytebuffer into a local file, but my output txt file is not readable. How could I fix this?
Here is what I have
Image depthImage = frame.acquireDepthImage();
Image.Plane plane = depthImage.getPlanes()[0];
int format = depthImage.getFormat();
ByteBuffer buffer = plane.getBuffer().order(ByteOrder.nativeOrder());
byte[] data = new byte[buffer.remaining()];
buffer.get(data);
File mypath=new File(super.getExternalFilesDir("depDir"),Long.toString(lastPointCloudTimestamp)+".txt");
FileChannel fc = new FileOutputStream(mypath).getChannel();
fc.write(buffer);
fc.close();
depthImage.close();
I tried to decode them with
String s = new String(data, "UTF-8");
System.out.println(StandardCharsets.UTF-8.decode(buffer).toString());
but the output is still strange like this
.03579:<=>#ABCDFGHJKMNOPRQSUWY]_b
In order to obtain the depth data provided by the ARCore session, you need to write bytes into your local file. A Buffer object is a container, it countains a finite sequence of elements of a specific primitive type (here bytes for a ByteBuffer). So what you need to write inside your file is your data variable that corresponds to the information previously stored in the buffer (according to buffer.get(data)).
It works fine for me, I managed to draw the provided depth map within a python code (but the idea behind the following code can be easily adapted to a java code):
depthData = np.fromfile('depthdata.txt', dtype = np.uint16)
H = 120
W = 160
def extractDepth(x):
depthConfidence = (x >> 13) & 0x7
if (depthConfidence > 6): return 0
return x & 0x1FFF
depthMap = np.array([extractDepth(x) for x in depthData]).reshape(H,W)
depthMap = cv.rotate(depthMap, cv.ROTATE_90_CLOCKWISE)
For further details, read the information concerning the format of the depthmap (DEPTH16) here: https://developer.android.com/reference/android/graphics/ImageFormat#DEPTH16
You must also be aware that the depthmap resolution is set to 160x120 pixels and is oriented according to a landscape format.
Make also sure to surround your code by a try/catch code bloc in case of a IOException error.
I want to compress a Bitmap (JPEG image) to quality 64, the result with be stored in a byte array. Previously I can achieve this with Bitmap#compress() method:
public static byte[] compressJPEG(Bitmap bmp, int quality) {
if (quality <= 0)
return new byte[0];
byte[] array = new byte[0];
try {
ByteArrayOutputStream bmpStream = new ByteArrayOutputStream();
bmp.compress(Bitmap.CompressFormat.JPEG, quality, bmpStream);
array = bmpStream.toByteArray();
bmpStream.close();
} catch (Exception e) {
e.printStackTrace();
}
return array;
}
But the result byte array (with the same input image file) is different when I run this on different devices. (May be because it use nativeCompress() internally)
After checking, I find out the different is in Huffman Table(s), SOS & image data parts, see this:
Diff check between 2 image photos
So how can I compress JPEG image to specific quality level without using Bitmap#compress() method? Because I really want the result byte array to be similar in structure.
There is no concept of "quality level" in JPEG. It is a shorthand that some encoders use to select quantization tables. There are a number of variables in JPEG compression, including the type and breakdown of scans, samples, huffman table selection, and quantization table selection. If any of those are different in the encoder you use, you are going to get different output. It's the nature of the beast.
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.
-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!).