How to convert a Binary String into Gray scale image? - java

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);
}
}
}

Related

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.

Converting byte array to png

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.

create png/jpg from random byte[]

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) {}

Is there a equivalent of Android's BitmapFactory.Options isDecodeBounds for TIFF in Java/JAI?

I am trying to improve the performance of our system (a Java app running in Tomcat) and now the bottleneck is in one operation, we need to read and return dimension of tiff images, so we use JAI's ImageDecoder and use
ImageDecoder decoder = ImageCodec.createImageDecoder("TIFF", input, param);
RenderedImage r = decoder.decodeAsRenderedImage();
int width = r.getWidth();
int height = r.getHeight();
From sampling data, a lot of time is spent in createImageDecoder. My assumption (without going to source code of ImageCodec) is it's probably trying to decode the input stream.
Coming from Android land, I am hoping there is a similar solution to just decode bounds like setting BitmapFactory.Options.inJustDecodeBounds = true but so far no luck in finding any other library like that. (I am aware that tiff support on Android is missing in AOSP, but that's topic for another day.)
Anyone know a library that does this? Or is there a way to achieve similar goal using JAI/ImageIO?
It looks like the tiff file format groups this information together in a header, so you could just read the data from the file yourself:
private static Dimension getTiffDimensions(InputStream tiffFile) throws IOException {
ReadableByteChannel channel = Channels.newChannel(tiffFile);
ByteBuffer buffer = ByteBuffer.allocate(12);
forceRead(channel, buffer, 8);
byte endian = buffer.get();
if(endian != buffer.get() || (endian != 'I' && endian != 'M')) {
throw new IOException("Not a tiff file.");
}
buffer.order(endian == 'I' ? ByteOrder.LITTLE_ENDIAN : ByteOrder.BIG_ENDIAN);
if(buffer.getShort() != 42) {
throw new IOException("Not a tiff file.");
}
// Jump to the first image directory. Note that we've already read 8 bytes.
tiffFile.skip(buffer.getInt() - 8);
int width = -1;
int height = -1;
// The first two bytes of the IFD are the number of fields.
forceRead(channel, buffer, 2);
for(int fieldCount = buffer.getShort(); fieldCount > 0 && (width < 0 || height < 0); --fieldCount) {
forceRead(channel, buffer, 12);
switch(buffer.getShort()) {
case 0x0100: // Image width
width = readField(buffer);
break;
case 0x0101: // Image "length", i.e. height
height = readField(buffer);
break;
}
}
return new Dimension(width, height);
}
private static void forceRead(ReadableByteChannel channel, ByteBuffer buffer, int n) throws IOException {
buffer.position(0);
buffer.limit(n);
while(buffer.hasRemaining()) {
channel.read(buffer);
}
buffer.flip();
}
private static int readField(ByteBuffer buffer) {
int type = buffer.getShort();
int count = buffer.getInt();
if(count != 1) {
throw new RuntimeException("Expected a count of 1 for the given field.");
}
switch(type) {
case 3: // word
return buffer.getShort();
case 4: // int
return buffer.getInt();
default: // char (not used here)
return buffer.get() & 0xFF;
}
}
I've tested this with a few different tiff files (run length encoded black & white, color with transparency) and it seems to work fine. Depending on the layout of your tiff file it may have to read a lot of the stream before it finds the size (one of the files I tested, saved by Apple's Preview, had this data at the end of the file).

How to Insert a Linefeed with PDFBox drawString

I have to make a PDF with a Table. So far it work fine, but now I want to add a wrapping feature. So I need to insert a Linefeed.
contentStream.beginText();
contentStream.moveTextPositionByAmount(x, y);
contentStream.drawString("Some text to insert into a table.");
contentStream.endText();
I want to add a "\n" before "insert". I tried "\u000A" which is the hex value for linefeed, but Eclipse shows me an error.
Is it possible to add linefeed with drawString?
The PDF format allows line breaks, but PDFBox has no build in feature for line breaks.
To use line breaks in PDF you have to define the leading you want to use with the TL-operator. The T*-operator makes a line break. The '-operator writes the given text into the next line. (See PDF-spec for more details, chapter "Text". It´s not that much.)
Here are two code snippets. Both do the same, but the first snippet uses ' and the second snippet uses T*.
private void printMultipleLines(
PDPageContentStream contentStream,
List<String> lines,
float x,
float y) throws IOException {
if (lines.size() == 0) {
return;
}
final int numberOfLines = lines.size();
final float fontHeight = getFontHeight();
contentStream.beginText();
contentStream.appendRawCommands(fontHeight + " TL\n");
contentStream.moveTextPositionByAmount(x, y);
contentStream.drawString(lines.get(0));
for (int i = 1; i < numberOfLines; i++) {
contentStream.appendRawCommands(escapeString(lines.get(i)));
contentStream.appendRawCommands(" \'\n");
}
contentStream.endText();
}
private String escapeString(String text) throws IOException {
try {
COSString string = new COSString(text);
ByteArrayOutputStream buffer = new ByteArrayOutputStream();
string.writePDF(buffer);
return new String(buffer.toByteArray(), "ISO-8859-1");
} catch (UnsupportedEncodingException e) {
// every JVM must know ISO-8859-1
throw new RuntimeException(e);
}
}
Use T* for line break:
private void printMultipleLines(
PDPageContentStream contentStream,
List<String> lines,
float x,
float y) throws IOException {
if (lines.size() == 0) {
return;
}
final int numberOfLines = lines.size();
final float fontHeight = getFontHeight();
contentStream.beginText();
contentStream.appendRawCommands(fontHeight + " TL\n");
contentStream.moveTextPositionByAmount( x, y);
for (int i = 0; i < numberOfLines; i++) {
contentStream.drawString(lines.get(i));
if (i < numberOfLines - 1) {
contentStream.appendRawCommands("T*\n");
}
}
contentStream.endText();
}
To get the height of the font you can use this command:
fontHeight = font.getFontDescriptor().getFontBoundingBox().getHeight() / 1000 * fontSize;
You might want to multiply it whit some line pitch factor.
The pdf format doesn't know line breaks. You have to split the string and move the text position to the next line, using moveTextPositionByAmount.
This is not a special "pdfbox-feature", it is due to the pdf format definition; so there is no way for drawString and there are also no other methods to be called that support linefeeds.
Because the PDF model often isn't the best model for the task at hand, it often makes sense to write a wrapper for it that adds support for whatever's "missing" in your case. This is true for both reading and writing.

Categories