i'm trying to stream a camera feed over the network,
the main problem that i encountered is the ImageIO latency to encode BufferedImage to jpg
Here is my code:
long cameraTimer = 0, cameraTimerFPS = 0, cameraFPS = 0, cameraSize = 0;
while (!Thread.interrupted()) {
cameraTimer = System.currentTimeMillis();
BufferedImage imageBuffered = // Get the image
ImageWriter imageWriter = ImageIO.getImageWritersByFormatName("jpg").next();
ImageWriteParam imageWriterParameter = imageWriter.getDefaultWriteParam();
imageWriterParameter.setCompressionMode(ImageWriteParam.MODE_EXPLICIT);
imageWriterParameter.setCompressionQuality(cameraCompression / 100F);
ByteArrayOutputStream imageBuffer = new ByteArrayOutputStream();
imageWriter.setOutput(new MemoryCacheImageOutputStream(imageBuffer));
IIOImage outputImage = new IIOImage(imageBuffered, null, null);
imageWriter.write(null, outputImage, imageWriterParameter); // ~600ms latency
imageWriter.dispose();
// Send the image
cameraSize += imageBuffer.size();
cameraTimer = System.currentTimeMillis() - cameraTimer;
cameraTimer = (1000 / cameraFramerate) - cameraTimer;
if (cameraTimerFPS - System.currentTimeMillis() < -1000) {
System.out.println(String.format("Avg Image Size: %s", formatBytes(cameraSize / (cameraFPS + 1))));
System.out.println(String.format("Frame per second: %s", cameraFPS));
cameraTimerFPS = System.currentTimeMillis();
cameraSize = 0;
cameraFPS = 0;
}
cameraFPS++;
if (cameraTimer > 0) {
Thread.sleep(cameraTimer);
}
}
So, my question is how can i get rid of the ImageIO api?
i searched on internet and i didn't find any library that can replace the ImageIO.
Related
I'm trying to save a Image type data as a jpg file to SD card, what I plan to do is first converting it to Bitmap, compress bitmap as jpeg format, then use FileOutputStream to save it to SD card.
Here's my code:
File imagefile = new File(sdCardPath, "image.jpg");
Image image = fromFrame.getImage();
ByteBuffer bbuffer = image.getPlanes()[0].getBuffer();
byte[] byts = new byte[bbuffer.capacity()];
bbuffer.get(byts);
Bitmap bitmapImage = BitmapFactory.decodeByteArray(byts, 0, byts.length, null);
try{
FileOutputStream out = new FileOutputStream(imagefile);
bitmapImage.compress(Bitmap.CompressFormat.JPEG, 100, out);
out.flush();
out.close();
} catch (FileNotFoundException e) {
Log.v(TAG, "FileNotFoundExceptionError " + e.toString());
} catch (IOException e) {
Log.v(TAG, "IOExceptionError " + e.toString());
}
It gives error:
java.lang.NullPointerException: Attempt to invoke virtual method 'boolean
android.graphics.Bitmap.compress(android.graphics.Bitmap$CompressFormat,
int, java.io.OutputStream)' on a null object reference
Is there anything I missed or did it wrong?
It turns out that the image file has to be re-organized and as for Depth16 file, every pixel has 16 bits of data, so the code to convert it into a jpg file is:
Image depthimage = fromFrame.getImage();
int imwidth = depthImage.getWidth();
int imheight = depthImage.getHeight();
Image.Plane plane = depthImage.getPlanes()[0];
ShortBuffer shortDepthBuffer = plane.getBuffer().asShortBuffer();
File sdCardFile = Environment.getExternalStorageDirectory();
File file = new File(sdCardFile, "depthImage.jpg");
Bitmap disBitmap = Bitmap.createBitmap(imwidth, imheight, Bitmap.Config.RGB_565);
for (int i = 0; i < imheight; i++) {
for (int j = 0; j < imwidth; j++) {
int index = (i * imwidth + j) ;
shortDepthBuffer.position(index);
short depthSample = shortDepthBuffer.get();
short depthRange = (short) (depthSample & 0x1FFF);
byte value = (byte) depthRange ;
disBitmap.setPixel(j, i, Color.rgb(value, value, value));
}
}
Matrix matrix = new Matrix();
matrix.setRotate(90);
Bitmap rotatedBitmap = Bitmap.createBitmap(disBitmap, 0, 0, imwidth, imheight, matrix, true);
try {
FileOutputStream out = new FileOutputStream(file);
rotatedBitmap.compress(Bitmap.CompressFormat.JPEG, 90, out);
out.flush();
out.close();
MainActivity.num++;
} catch (Exception e) {
e.printStackTrace();
}
} catch (Exception e) {
e.printStackTrace();
}
I also rotate the picture to make it easier to review on mobile devices
I want to compress images that i choose from gallery in android according to there sizes and upload them on to cloud storage. For example if size of an image which i choose is 300kb i don't to reduce it and keep quality 100 but if same is 7Mb i want to reduce it to 10 quality and i want to set max size to 7Mb of the chosen image(original without compression) and similarly puting different conditions on sizes in between.
My code
if (resultCode == RESULT_OK) {
resultUri = result.getUri();
File f = new File(resultUri.getPath());
long sizeUri = f.length()/1024;
try {
bitmap = ImageDecoder.decodeBitmap(ImageDecoder.createSource(getContentResolver(),resultUri));
} catch (IOException e) {
e.printStackTrace();
}
ByteArrayOutputStream baos = new ByteArrayOutputStream();
bitmap.compress(Bitmap.CompressFormat.JPEG, 100, baos);
int baossize= baos.size()/1024;
byte[] uploadbaos = baos.toByteArray();
int lengthbmp = (uploadbaos.length);
int size= lengthbmp/1024;
Log.d(TAG,"baossize: "+baossize+" ByteArray: "+size+" UriSize: "+sizeUri);
// UploadingImage();
}
Just do it:
int quality;
if (sizeUri <= 300)
quality = 90;
else if (sizeUri <= 1000)
quality = 80;
else if (sizeUri <= 2000)
quality = 70;
else if (sizeUri <= 3000)
quality = 50;
else
quality = 30;
bitmap.compress(Bitmap.CompressFormat.JPEG, quality, baos);
Note that JPEG quality 100 is probably too high and below 30 can be very blurry.
Here in the given example you can set the max size , like in your case it could be 7MB.
public static boolean reduceImage(String path, long maxSize) {
File img = new File(path);
boolean result = false;
BitmapFactory.Options options = new BitmapFactory.Options();
Bitmap bitmap = null;
options.inSampleSize=1;
while (img.length()>maxSize) {
options.inSampleSize = options.inSampleSize+1;
bitmap = BitmapFactory.decodeFile(path, options);
img.delete();
try
{
FileOutputStream fos = new FileOutputStream(path);
img.compress(path.toLowerCase().endsWith("png")?
Bitmap.CompressFormat.PNG:
Bitmap.CompressFormat.JPEG, 100, fos);
fos.close();
result = true;
}catch (Exception errVar) {
errVar.printStackTrace();
}
};
return result;
}
I've written a small application for personal use in macOs using java (the only language I know) that reads a directory and compare the images found in it with every other image.
Edit: As user dbush pointed out, here is the code I should've posted:
for (int i = 0; i < files.size(); i++) {
file1 = files.get(i).getAbsolutePath();
for (int j = i + 1; j < files.size(); j++) {
file2 = files.get(j).getAbsolutePath();
System.out.println("Comparing " + files.get(i).getName() + sp + files.get(j).getName());
System.out.println(Arrays.equals(extractBytes(file1), extractBytes(file2)));
}
}
private static byte[] extractBytes(String imageName) throws IOException {
// open image
File imgPath = new File(imageName);
BufferedImage bufferedImage = getSample(imgPath);
// get DataBufferBytes from Raster
WritableRaster raster = bufferedImage.getRaster();
DataBufferByte data = (DataBufferByte) raster.getDataBuffer();
return (data.getData());
}
private static BufferedImage getSample(File f) {
BufferedImage img = null;
Rectangle sourceRegion = new Rectangle(0, 0, 10, 10); // The region you want to extract
try {
ImageInputStream stream1 = ImageIO.createImageInputStream(f); // File or input stream
Iterator<ImageReader> readers = ImageIO.getImageReaders(stream1);
if (readers.hasNext()) {
ImageReader reader = readers.next();
reader.setInput(stream1);
ImageReadParam param = reader.getDefaultReadParam();
param.setSourceRegion(sourceRegion); // Set region
img = reader.read(0, param); // Will read only the region specified
}
} catch (IOException e) {
System.out.println(e);
}
return img;
}
It involves reading two images in a byte array and comparing both. The problem is that it takes a long time to do this, even if I sample a small portion of each image.
Should I re-write the same application in C or swift for better performance, considering I'll be running it on macOs from the terminal? Or it will do minimal difference.
Thanks a lot in advance!
ImagesService imagesService = ImagesServiceFactory.getImagesService();
BlobKey bk = currentProfile.getProfileBlobKey();
Image oldImage = ImagesServiceFactory.makeImageFromBlob(bk);
Transform resize = ImagesServiceFactory.makeResize(500, 500);
Image newImage = imagesService.applyTransform(resize, oldImage);
int imageWidth = newImage.getWidth();
int imageHeight = newImage.getHeight();
This code doesn't seem to work since ImagesServiceFactory.makeImageFromBlob(bk); doesn't return a real Image. Does anyone know a workaround for this? This seems to be the expected behavior. This issue is discussed here but they don't have a solution for getting the height and width just the byte[]
Try this:
FileService fileService = FileServiceFactory.getFileService();
AppEngineFile blobFile = fileService.getBlobFile(blobKey);
FileReadChannel readChannel = fileService.openReadChannel(blobFile, false);
byte[] imageData = getBytes(Channels.newInputStream(readChannel));
Image oldImage = ImagesServiceFactory.makeImage(imageData);
// now you have the real Image
and this little snippet for reading an input stream:
public static byte[] getBytes(InputStream is) throws IOException {
ByteArrayOutputStream buffer = new ByteArrayOutputStream();
int len;
byte[] data = new byte[10000];
while ((len = is.read(data, 0, data.length)) != -1) {
buffer.write(data, 0, len);
}
buffer.flush();
return buffer.toByteArray();
}
have you tried the ImageReader Class?
I am using Xuggler to convert images captured from the java Robot class and sound read from TargetDataLine class and encoding this into a video. I am then attempting to http stream this video data (after writing my header) to a flash client via http (Socket OutputStream) but it plays and stutters (never just playing smoothly) no matter what buffer value I use on the client side.
I am asking for help and showing my java code because I suspect it might be to do with how I am encoding the video or something about sending data via http socket which i am not getting..
ByteArrayURLHandler ba = new ByteArrayURLHandler();
final IRational FRAME_RATE = IRational.make(30);
final int SECONDS_TO_RUN_FOR = 20;
final Robot robot = new Robot();
final Toolkit toolkit = Toolkit.getDefaultToolkit();
final Rectangle screenBounds = new Rectangle(toolkit.getScreenSize());
IMediaWriter writer;
writer = ToolFactory.makeWriter(
XugglerIO.map(
XugglerIO.generateUniqueName(out, ".flv"),
out
));
writer.addListener(new MediaListenerAdapter() {
public void onAddStream(IAddStreamEvent event) {
event.getSource().getContainer().setInputBufferLength(1000);
IStreamCoder coder = event.getSource().getContainer().getStream(event.getStreamIndex()).getStreamCoder();
if (coder.getCodecType() == ICodec.Type.CODEC_TYPE_AUDIO) {
coder.setFlag(IStreamCoder.Flags.FLAG_QSCALE, false);
coder.setBitRate(32000);
System.out.println("onaddstream"+ coder.getPropertyNames().toString());
}
if (coder.getCodecType() == ICodec.Type.CODEC_TYPE_VIDEO) {
// coder.setBitRate(64000);
// coder.setBitRateTolerance(64000);
}
}
});
writer.addVideoStream(videoStreamIndex, videoStreamId, 1024, 768);
final int channelCount = 1;
int audionumber = writer.addAudioStream(audioStreamIndex, audioStreamId,1, 44100);
int bufferSize = (int)audioFormat.getSampleRate() *audioFormat.getFrameSize();//*6;///6;
byte[] audioBuf;// = new byte[bufferSize];
int i = 0;
final int audioStreamIndex = 1;
final int audioStreamId = 1;
BufferedImage screen, bgrScreen;
long startTime = System.nanoTime();
while(keepGoing)
{
audioBuf = new byte[bufferSize];
i++;
screen = robot.createScreenCapture(screenBounds);
bgrScreen = convertToType(screen, BufferedImage.TYPE_3BYTE_BGR);
long nanoTs = System.nanoTime()-startTime;
writer.encodeVideo(0, bgrScreen, nanoTs, TimeUnit.NANOSECONDS);
audioBuf = new byte[line.available()];
int nBytesRead = line.read(audioBuf, 0, audioBuf.length);
IBuffer iBuf = IBuffer.make(null, audioBuf, 0, nBytesRead);
IAudioSamples smp = IAudioSamples.make(iBuf,1,IAudioSamples.Format.FMT_S16);
if (smp == null) {
return;
}
long numSample = audioBuf.length / smp.getSampleSize();
smp.setComplete(true, numSample,(int)
audioFormat.getSampleRate(), audioFormat.getChannels(),
IAudioSamples.Format.FMT_S16, nanoTs/1000);
writer.encodeAudio(1, smp);
writer.flush();
}