I am struggling with the transfer of a simple jpeg file inside an ID3v2 tag from c++ over a TCP socket to java (Android). The library "taglib" offers to extract this file and I am able to save the jpeg as a new file.
The send function looks like this
char *parameter_full = new char[f3->picture().size()+2];
sprintf(parameter_full,"%s\n\0",f3->picture().data());
// send
result = send(c,parameter_full,strlen(parameter_full),0);
delete[] parameter_full;
where
f3->picture().data() returns a pointer to the internal data structure (it returns char*) and
f3->picture().size() returns the size of the array.
Then Android receives it with
String imageString = inFromServer.readLine();
byte[] imageBytes = imageString.getBytes();
Bitmap cover = BitmapFactory.decodeByteArray(imageBytes,0,imageBytes.length);
But somehow decodeByteArray always returns null. My idea is that Java doesn't receive the image correctly because imageString only consists of 4 characters...while the extracted jpeg file has a size of 12.7 KB.
But what has gone wrong?
Martin
You shouldn't use string functions on byte data because 0 values are taken as string terminators. Try looking into memcpy on the C++ side if you need to copy the char* and also the byte[] read functions for InputStream on the Java side.
Related
I'm planing to use SheetJS with rhino. And sheetjs takes a binary object(BLOB if i'm correct) as it's input. So i need to read a file from the system using stranded java I/O methods and store it into a blob before passing it to sheetjs. eg :-
var XLDataWorkBook = XLSX.read(blobInput, {type : "binary"});
So how can i create a BLOB(or appropriate type) from a binary file in java in order to pass it in.
i guess i cant pass streams because i guess XLSX needs a completely created object to process.
I found the answer to this by myself. i was able to get it done this way.
Read the file with InputStream and then write it to a ByteArrayOutputStream. like below.
ByteArrayOutputStream buffer = new ByteArrayOutputStream();
...
buffer.write(bytes, 0, len);
Then create a byte array from it.
byte[] byteArray = buffer.toByteArray();
Finally i did convert it to a Base64 String (which is also applicable in my case) using the "Base64.encodeBase64String()" method in apache.commons.codec.binary package. So i can pass Base64 String as a method parameter.
If you further need there are lot of libraries(3rd-party and default) available for Base64 to Blob conversion as well.
My C++ server sends a video stream from a computer's usb to a Java client.
In order to send an image(Mat object) from C++ I'm using FileStorage class to store the image in a string:
cv::FileStorage fs(".xml", cv::FileStorage::WRITE + cv::FileStorage::MEMORY);
fs << "mymatrix" << frame;
string buff = fs.releaseAndGetString(); //buff has all the Mat object info.
The client receives buff ,creates XML file and reads it using : Java: How to read and write xml files?
The image datatype is "3u" (=CV_8UC3), so I created a byte array to store the matrix data:
//data is a string that contains the matrix data
byte[] Bdata = data.getBytes();
and used this code to create the Mat object ( taken from https://groups.google.com/forum/#!topic/android-opencv/QEwhgO88ZwM ):
//Irows = number of rows
//Icols = number of columns
Mat mat = new Mat(Irows,Icols,CvType.CV_8UC3);
mat.put(0,0,Bdata);
but "mat.put.." is generating this error:
Exception in thread "main" java.lang.UnsupportedOperationException: Provided data element number (3742097) should be multiple of the Mat channels count (3)
I don't understand what is the problem with the byte array?
Any idea would be appreciated.
Edit : Though I'm not near my computer, so I can't confirm this, it appears that the large number in the error is the length of the array and it doesn't divide by 3 - the number of channels. I've no idea why.
honestly, streaming uncompressed bytes (in xml even) over a network sounds like a horrible idea to me.
here's an alternative:
on the server side, encode to jpeg:
std::vector<uchar>outbuf;
std::vector<int> params;
params.push_back(cv::IMWRITE_JPEG_QUALITY);
params.push_back(100);
cv::imencode(".jpg", frame, outbuf, params);
int outlen = outbuf.size();
// send outlen bytes to client
on the client side, unpack:
// read into a MatOfBytes, until the jpeg end sequence ff d9 is discovered.
Mat img = Highgui.imdecode(bytes_buf, IMREAD_UNCHANGED)
I create a PIL image string on a python server:
frame = cv.CaptureFromCAM(0)
image = Image.fromstring('RGB', cv.GetSize(frame), frame.tostring(), 'raw', 'BGR')
buffer = cStringIO.stringIO()
image.save(buffer,'JPEG')
udptransmit(buffer.getvalue())
I have a java client trying to read the transmitted image string and reform the jpeg. This however doesn't seem to be working. I created a python client just to check, and I can reform the jpeg correctly using a call to pygame's load method.
The string being sent from python, contains characters 6:10 = JFIF, which is the correct format (also recognised by python's imghdr module.
In java, I ahve tried
simply writing the byte contents of the string received into a file and naming it with a .jpeg extension. The file isn't a valid jpeg.
Using ImageIO to read the bytes from the string. This produces a null image.
Tried to fetch ImageReaderByFormat('JPEG') and parse the bytes with this. This gives me an error stating 'Image is not a JPEG, starts with 0x...'
I really can't see why python recognises the string as a valid jpeg and java doesn't. Do the two use different jpeg decoders? Even if they do, shouldn't both either validate or reject the string?
Just found a solution to the problem
The problem was with the charset used in java while converting the string sent from my python server into bytes in java.
Here's the simple modification that was required in my java client code:
Charset charset = Charset.forName("ISO-8859-1");
Byte[] bytes: Array[Byte] = cam_data.getBytes(charset)
File f = new File("image.jpeg")
FileImageOutputStream fios = new FileImageOutputStream(f)
BufferedImage bim = ImageIO.read(new ByteArrayInputStream(bytes))
ImageIO.write(bim,"jpeg",fios)
The helpful link that lead me to the answer was http://www.java-forums.org/advanced-java/50516-reading-image-files-into-strings.html
I am writing an applet to wrap a proprietary .dll that can be used in the browser. To achieve this, I am using JNA. The .dll connects to a check scanner peripheral, and can pull images from the devices memory.
I have to make a Windows API call in Java, using JNA, to get the image:
// DEVICE is the JNA Library interface
HANDLEByReference img = new HANDLEByReference();
File outfile = new File("my_image.bmp");
DEVICE.saveImage(img.getValue(), outfile.getName().getBytes());
When the code saves the image, I get one named something like:
C:\Users\user\workspace\JavaProject\bin\my_image.bmpó_¯=Pá
note the gibberish at the end
Does Java return a NULL terminated byte[] array when calling getBytes() on a String?
No, String.getBytes() just returns the bytes in the encoded form of the string.
Note that it also uses the platform default encoding unless you specify the encoding, and that default may not be what you want.
If you want an array with a "0" byte at the end, you could use:
byte[] data = outfile.getName().getBytes(encoding);
byte[] padded = new byte[data.length + 1];
System.arraycopy(data, 0, padded, 0, data.length);
I am porting a Python application to Android and, at some point, this application has to communicate with a Web Service, sending it compressed data.
In order to do that it uses the next method:
def stuff(self, data):
"Convert into UTF-8 and compress."
return zlib.compress(simplejson.dumps(data))
I am using the next method to try to emulate this behavior in Android:
private String compressString(String stringToCompress)
{
Log.i(TAG, "Compressing String " + stringToCompress);
byte[] input = stringToCompress.getBytes();
// Create the compressor with highest level of compression
Deflater compressor = new Deflater();
//compressor.setLevel(Deflater.BEST_COMPRESSION);
// Give the compressor the data to compress
compressor.setInput(input);
compressor.finish();
// Create an expandable byte array to hold the compressed data.
// You cannot use an array that's the same size as the orginal because
// there is no guarantee that the compressed data will be smaller than
// the uncompressed data.
ByteArrayOutputStream bos = new ByteArrayOutputStream(input.length);
// Compress the data
byte[] buf = new byte[1024];
while (!compressor.finished())
{
int count = compressor.deflate(buf);
bos.write(buf, 0, count);
}
try {
bos.close();
} catch (IOException e)
{
}
// Get the compressed data
byte[] compressedData = bos.toByteArray();
Log.i(TAG, "Finished to compress string " + stringToCompress);
return new String(compressedData);
}
But the HTTP response from the server is not correct and I guess it is because the result of the compression in Java is not the same as the one in Python.
I ran a little test compressing "a" both with zlib.compress and deflate.
Python, zlib.compress() -> x%9CSJT%02%00%01M%00%A6
Android, Deflater.deflate -> H%EF%BF%BDK%04%00%00b%00b
How should I compress the data in Android to obtain the same value of zlib.compress() in Python?
Any help, guidance or pointer is greatly appreciated!
compress and deflate are different compression algorithms so the answer is they will not be compatible. As an example of the difference here is 'a' compressed using the two algorithms via Tcl:
% binary encode hex [zlib compress a]
789c4b040000620062
% binary encode hex [zlib deflate a]
4b0400
Your python code is indeed doing compress. And the android code is doing deflate, however you are also getting the UTF-8 byte order mark prepended to the android version (\xef\xbf\xbf)
You can emit deflate data using python:
def deflate(data):
zobj = zlib.compressobj(6,zlib.DEFLATED,-zlib.MAX_WBITS,zlib.DEF_MEM_LEVEL,0)
zdata = zobj.compress(data)
zdata += zobj.flush()
return zdata
>>> deflate("a")
'K\x04\x00'
Although they are not exactly the same algorithms, it seems that they are totally compatible (meaning that if you compress, for example, an String using Deflater.deflate you can correctly uncompress it using zlib).
What caused my problem was that all form variables in a POST need to be percent escaped, and the Android application was not doing that. Encoding the data to Base64 before sending it, and modifying the server to decode it using Base64 before uncompressing it using zlib solved the problem.
Does byte[] input = stringToCompress.getBytes("utf-8"); help? In case your platform's default encoding is not UTF-8, this will force the encoding String -> bytes to use UTF-8. Also, the same goes for the last line of your code where you create a new String - you may want to explicitly specify UTF-8 as the decoding Charset.