I've just imported the Picasso library in my project and I'm using it like described in this other SO question (I need to download & save some images from web server). The whole thing is working correctly except for a thing: the resulting image in my emulator is bigger (in terms of weight) than the original on server. For example, a JPG image of 52Kb (370 x 505 pixels), result in a JPG image of same resolution (370 x 505) but 132Kb of weight. This is the code I'm using (saw in a lot of SO questions):
if (target == null){
target = new Target() {
#Override
public void onBitmapLoaded(final Bitmap bitmap, Picasso.LoadedFrom from) {
new Thread(new Runnable() {
#Override
public void run() {
File file = new File(getActivity().getFilesDir() + "/saved.jpg");
try {
file.createNewFile();
FileOutputStream ostream = new FileOutputStream(file);
bitmap.compress(Bitmap.CompressFormat.JPEG, 100, ostream);
ostream.flush();
ostream.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}).start();
}
#Override
public void onBitmapFailed(Drawable errorDrawable) {}
#Override
public void onPrepareLoad(Drawable placeHolderDrawable) {}
};
}
Picasso.with(getActivity()).load(sourceValue).into(target);
Thanks you all in advance for your time and your attention.
UPDATE:
Checking the memory of my emulator, I've noticed that the cached version of the image that Picasso create, has the correct size. So, basically, if I use the same code I've posted, but I comment all the try-catch block, the file is just downloaded and cached (not saved in another location) but has the correct size. So it is compression's fault?
Related
I'm trying to save the frame of the image from the live stream video. So, I'm able to show live stream video from my android application as well as saving it in my local storage. Now, I want to use some sort of delay in my saveImage function so that images get saved after some specific time. I have used both Handler and TimerTask. The image is getting saved in my local directory after some delay but the length of the image that I get is sometimes very small, the other time normal. I want my saved image to exactly the length of the video stream that I am getting in my application.
I hope the question I asked is easy to understand. I am a beginner in both android and stack overflow.
P.S - I used mjpeg library for showing the video. The video I am getting is from an IP Camera.
Code for saving the images
public void saveImage(){
try {
photo =
new File(Environment.getExternalStorageDirectory(),
"Download/photos/photo" + Instant.now().getEpochSecond() + ".jpg");
if (photo.exists()) {
photo.delete();
}
System.out.println("Photo " + photo);
FileOutputStream fos = new FileOutputStream(photo.getPath());
System.out.println("Image_length" +image.length);
fos.write(image);
fos.close();
} catch (IOException e) {
Log.e("PictureDemo", "Exception in photoCallback", e);
}
}
Code where saveImage is called
final Bitmap outputImg = BitmapFactory.decodeByteArray(image, 0, image.length);
if (outputImg != null) {
if (run) {
newFrame(outputImg);
// Saving frames in internal Storage
new Timer().schedule(
new TimerTask() {
#Override
public void run() {
saveImage();
}
},5000
);
}
} else {
Log.e(tag, "Read image error");
}
#Override
public void onBindViewHolder(final ViewHolder holder ,int position) {
Glide.with(c)
.load(images.get(position))
.placeholder(R.mipmap.ic_launcher)
.into(holder.img);
holder.img.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
try{
String fileName = "bitmap.png";
FileOutputStream stream = c.openFileOutput(fileName,Context.MODE_PRIVATE);
Intent showBigPicture = new Intent(c,showBigPicture.class);
Bitmap bitmapImage = BitmapFactory.decodeFile(images.get(position));
bitmapImage.compress(Bitmap.CompressFormat.PNG,100,stream);
stream.close();
bitmapImage.recycle();
showBigPicture.putExtra("image",fileName);
c.startActivity(showBigPicture);
}catch (Exception e){
e.printStackTrace();
}
}
});
}
this is showing in logCat " Unable to decode stream: java.io.FileNotFoundException: android.support.v7.widget.AppCompatImageView{e22d977 V.ED..C. ...P.... 0,0-540,890 #7f0b0061 app:id/img}: open failed: ENOENT (No such file or directory)"
I believe you want to follow this answer on saving Bitmap images. I believe the reason you're getting a FileNotFoundException is because you're providing the URI to a file that doesn't exist yet to the decodeFile function that's quite possibly a URL from what I can tell. In short, to save a bitmap:
Create a new File(filename)
Decode file using getName on the File from step 1
Create FileOutputStream from File
Compress the bitmap image into the FileOutputStream
From what I can surmise from your question, it looks as though you're showing a images in a RecyclerView and when an image is clicked, you want to open another activity which shows a version of the full image. If that's close to your use-case, and you're using Glide, I would recommend taking advantage of its built-in automatic caching feature to reduce network calls instead of manually saving the file.
By default, disk and memory-based caching is enabled in Glide as long as the same filename, path, or URL are used to obtain the image on each Glide.load(...). If you'd like to manipulate how the caching occurs, use the DiskCacheStrategy enum to control that every time you load the image:
Glide.with(c)
.load(images.get(position))
.diskCacheStrategy(DiskCacheStrategy.SOURCE) # Will cache the source downloaded image before any transformations are applied
.placeholder(R.mipmap.ic_launcher)
.into(holder.img);
If you still want to save the file for other reasons, use a SimpleTarget instead of loading directly into your ImageView like so:
Glide.with(c)
.load(images.get(position))
.diskCacheStrategy(DiskCacheStrategy.SOURCE) # Will cache the source downloaded image before any transformations are applied
.placeholder(R.mipmap.ic_launcher)
.asBitmap()
.into(new SimpleTarget<GlideDrawable>() {
#Override
public void onResourceReady(Bitmap bitmap, GlideAnimation anim) {
holder.img.setImageDrawable(new BitmapDrawable(bitmap));
saveImage(bitmap); # This being an encapsulation of the steps outlined earlier
}
});
I have to share GIF images from URL to some other applications using intent, as per my knowledge for sharing them from url I have to save them first in my phone's memory.
I have used GLIDE lib to show them, how could I store them to share? My code so far (not working): It saves only one image from the set of frames of GIF image.
if (mGIFArrayList != null) {
// imageUri = getLocalBitmapUri(imageViewSimple);
// shareWithAppChooser(imageUri,"");
Glide
.with(mContext)
.load(mGIFArrayList.get(getPosition()).getStrUrl())
.asGif()
.toBytes()
.into(new SimpleTarget<byte[]>() {
#Override public void onResourceReady(final byte[] resource, GlideAnimation<? super byte[]> glideAnimation) {
new AsyncTask<Void, Void, Void>() {
#Override protected Void doInBackground(Void... params) {
// File sdcard = Environment.getExternalStorageDirectory();
File file = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS), "shared_gif_" + System.currentTimeMillis() + ".gif");
File dir = file.getParentFile();
try {
if (!dir.mkdirs() && (!dir.exists() || !dir.isDirectory())) {
throw new IOException("Cannot ensure parent directory for file " + file);
}
BufferedOutputStream s = new BufferedOutputStream(new FileOutputStream(file));
s.write(resource);
s.flush();
s.close();
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
}.execute();
}
})
;
}
on the following two lines you determine that the drawable is a GlideBitmapDrawable
Drawable drawable = imageView.getDrawable();
if (drawable instanceof GlideBitmapDrawable) {
and then on the following like you cast it to GifDrawable:
GifDrawable gifDrawable = ((GifDrawable) imageView.getDrawable());
I'm sure it's throwing a ClassCastException as GifDrawable and GlideBitmapDrawable are not related.
Unfortunately I don't think you can extract the file from the GifDrawable, because it doesn't work like this.
Probably your best workaround it, is to download the gif file from the link to the device storage, and then sharing the file.
I am currently using Picasso to load image from server side and save it in Internal storage in Android.
I am using the following code to load images from server side:
Handler uiHandler = new Handler(Looper.getMainLooper());
uiHandler.post(new Runnable() {
#Override
public void run() {
System.out.println("start run.....");
Picasso.with(context)
.load(url)
.resize(10, 10)
.into(new Target() {
#Override
public void onBitmapLoaded(Bitmap bitmap, Picasso.LoadedFrom from) {
System.out.println("start picasso....");
if (bitmap != null) {
// save image in internal memory
String directory = saveToInternalStorage(bitmap, name);
System.out.println(directory);
} else
System.out.println("image return is null.....");
}
#Override
public void onBitmapFailed(Drawable errorDrawable) {
System.out.println("Failure in loading photo from server: " + name);
}
#Override
public void onPrepareLoad(Drawable placeHolderDrawable) {
}
});
and the following code to save image in memory:
private String saveToInternalStorage(Bitmap bitmapImage, String imageName){
System.out.println("start saving image......");
ContextWrapper cw = new ContextWrapper(context);
File directory = cw.getDir("imageDir", Context.MODE_PRIVATE);
// Create imageDir
File mypath=new File(directory,imageName);
FileOutputStream fos = null;
try {
fos = new FileOutputStream(mypath);
// Use the compress method on the BitMap object to write image to the OutputStream
boolean save=bitmapImage.compress(Bitmap.CompressFormat.PNG, 2, fos);
System.out.println(save);
fos.flush();
fos.close();
} catch (Exception e) {
System.out.println("Error in saving photo "+e.toString());
}
System.out.println("Image is successfully saved..."+directory.getAbsolutePath());
return directory.getAbsolutePath();
}
However, my problem is with Garbage Collection. Picasso does not start at all, even I do not get any failure message from Picasso, and I face with following error :
D/dalvikvm: GC_CONCURRENT freed 434K, 11% free 12767K/14215K, paused 14ms+25ms, total 118ms
I would be thankful, if anyone suggest me any solution to avoid this error.
this is not error instead this is information from GC saying GC_CONCURRENT has freed memory which is 434K and took 25ms. There is Google IO talk I would recommend you watch it
"Picasso does not start at all". Are you not able to load image at all ?
I am not sure if you are facing any problem regarding loading of the image or saving to your internal storage. As much I can guess you are able to do it and the message which you think is error is your problem.
If I am not getting it right. I would pledge you to draft you question in more specified way.
hope this will help!
My application captures video footage and saves it as .mp4 file. I would like to extract one frame from this video and also save it to file. Since I haven't found nothing better, I've decided to use MediaMetadataRetriever.getFrameAtTime() for that. It happens inside the class that inherits from AsyncTask. Here is how my code looks like my doInBackground():
Bitmap bitmap1 = null;
MediaMetadataRetriever retriever = new MediaMetadataRetriever();
try {
retriever.setDataSource(src);
bitmap1 = retriever.getFrameAtTime(timeUs, MediaMetadataRetriever.OPTION_CLOSEST_SYNC);
if (Utils.saveBitmap(bitmap1, dst)) {
Log.d(TAG, "doInBackground Image export OK");
} else {
Log.d(TAG, "doInBackground Image export FAILED");
}
} catch (RuntimeException ex) {
Log.d(TAG, "doInBackground Image export FAILED");
} finally {
retriever.release();
if (bitmap1 != null) {
bitmap1.recycle();
}
}
And the saveBitmap() method:
File file = new File(filepath);
boolean result;
try {
result = file.createNewFile();
if (!result) {
return false;
}
FileOutputStream ostream = new FileOutputStream(file);
bitmap.compress(Bitmap.CompressFormat.PNG, 100, ostream);
ostream.close();
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
Now, the problem is that the quality of the exported image is noticeably worse then the video quality. I don't think that this should happen with PNG output format. You can see the difference below:
The first image was extracted from the video using ffmpeg on my desktop. The second one was extracted using the code above on Samsung Galaxy S6. The result looks pretty much the same on every Android device I was using.
Can someone tell how can I improve the quality of the exported picture?
I found other solution for the issue. You can use bigflake's example to build mechanism for extracting video frame. The one thing you will have to add is seeking mechanism. This works well, keeps the exact quality and does not require any third-party libraries. Only downside I've noticed so far is that it will result in longer execution time than the original idea.