Skip to content

Compression (Images) - Best Libraries in 2023

A look at the best android image compression libraries and how to use them.

zetbaitsu/Compressor

Compressor is a lightweight and powerful android image compression library.

Compressor will allow you to compress large photos into smaller sized photos with very less or negligible loss in quality of the image.

Follow the following steps:

Step 1: Install it:

dependencies {
    implementation 'id.zelory:compressor:3.0.1'
}

Step 2: Compresss

To Compress Image File:

val compressedImageFile = Compressor.compress(context, actualImageFile)

To Compress Image File to specific destination:

val compressedImageFile = Compressor.compress(context, actualImageFile) {
    default()
    destination(myFile)
}

Customize Compressor

Using default constraint and custom partial of it:

val compressedImageFile = Compressor.compress(context, actualImageFile) {
    default(width = 640, format = Bitmap.CompressFormat.WEBP)
}

Full custom constraint

val compressedImageFile = Compressor.compress(context, actualImageFile) {
    resolution(1280, 720)
    quality(80)
    format(Bitmap.CompressFormat.WEBP)
    size(2_097_152) // 2 MB
}

Using your own custom constraint:

class MyLowerCaseNameConstraint: Constraint {
    override fun isSatisfied(imageFile: File): Boolean {
        return imageFile.name.all { it.isLowerCase() }
    }

    override fun satisfy(imageFile: File): File {
        val destination = File(imageFile.parent, imageFile.name.toLowerCase())
        imageFile.renameTo(destination)
        return destination
    }
}

val compressedImageFile = Compressor.compress(context, actualImageFile) {
    constraint(MyLowerCaseNameConstraint()) // your own constraint
    quality(80) // combine with compressor constraint
    format(Bitmap.CompressFormat.WEBP)
}

Compressor now is using Kotlin coroutines!

Calling Compressor should be done from coroutines scope:

// e.g calling from activity lifecycle scope
lifecycleScope.launch {
    val compressedImageFile = Compressor.compress(context, actualImageFile)
}

// calling from global scope
GlobalScope.launch {
    val compressedImageFile = Compressor.compress(context, actualImageFile)
}

To Run Compressor in main thread:

val compressedImageFile = Compressor.compress(context, actualImageFile, Dispatchers.Main)

Reference

Read more here.

AbedElazizShe/LightCompressor

A powerful and easy-to-use video compression library for android uses MediaCodec API.

This library generates a compressed MP4 video with a modified width, height, and bitrate (the number of bits per seconds that determines the video and audio files’ size and quality). It is based on Telegram for Android project.

The general idea of how the library works is that, extreme high bitrate is reduced while maintaining a good video quality resulting in a smaller size.

I would like to mention that the set attributes for size and quality worked just great in my projects and met the expectations. It may or may not meet yours. I’d appreciate your feedback so I can enhance the compression process.

How it works

When the video file is called to be compressed, the library checks if the user wants to set a min bitrate to avoid compressing low resolution videos. This becomes handy if you don’t want the video to be compressed every time it is to be processed to avoid having very bad quality after multiple rounds of compression. The minimum is; * Bitrate: 2mbps

You can pass one of a 5 video qualities; VERY_HIGH, HIGH, MEDIUM, LOW, OR VERY_LOW and the library will handle generating the right bitrate value for the output video

return when (quality) {
    VideoQuality.VERY_LOW -> (bitrate * 0.1).roundToInt()
    VideoQuality.LOW -> (bitrate * 0.2).roundToInt()
    VideoQuality.MEDIUM -> (bitrate * 0.3).roundToInt()
    VideoQuality.HIGH -> (bitrate * 0.4).roundToInt()
    VideoQuality.VERY_HIGH -> (bitrate * 0.6).roundToInt()
}

when {
   width >= 1920 || height >= 1920 -> {
      newWidth = (((width * 0.5) / 16).roundToInt() * 16)
      newHeight = (((height * 0.5) / 16f).roundToInt() * 16)
   }
   width >= 1280 || height >= 1280 -> {
      newWidth = (((width * 0.75) / 16).roundToInt() * 16)
      newHeight = (((height * 0.75) / 16).roundToInt() * 16)
   }
   width >= 960 || height >= 960 -> {
      newWidth = (((width * 0.95) / 16).roundToInt() * 16)
      newHeight = (((height * 0.95) / 16).roundToInt() * 16)
   }
   else -> {
      newWidth = (((width * 0.9) / 16).roundToInt() * 16)
      newHeight = (((height * 0.9) / 16).roundToInt() * 16)
   }
}

You can as well pass custom videoHeight, videoWidth, frameRate, and videoBitrate values if you don't want the library to auto-generate the values for you. The compression will fail if height or width is specified without the other, so ensure you pass both values.

These values were tested on a huge set of videos and worked fine and fast with them. They might be changed based on the project needs and expectations.

Step 1: Install it

Compatibility

Minimum Android SDK: LightCompressor requires a minimum API level of 21.

How to add to your project?

Ensure Kotlin version is 1.6.0.

Include this in your Project-level build.gradle file:

allprojects {
    repositories {
        .
        .
        .
        maven { url 'https://jitpack.io' }
    }
}

Include this in your Module-level build.gradle file:

implementation 'com.github.AbedElazizShe:LightCompressor:1.1.1'

And import the following dependencies to use kotlin coroutines:

implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:${Version.coroutines}"
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:${Version.coroutines}"

If you're facing problems with the setup, edit settings.gradle by adding this at the beginning of the file:

dependencyResolutionManagement {
    repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
    repositories {
        google()
        mavenCentral()
        maven { url 'https://jitpack.io' }
    }
}

Step 2: Compress

To use this library, you must add the following permission to allow read and write to external storage. Refer to the sample app for a reference on how to start compression with the right setup.

API < 29

<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"
android:maxSdkVersion="28"/>

API >= 29

<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>

Then just call VideoCompressor.start() and pass context, uris, and saveAt directory name.

The method has a callback for 5 functions; 1) OnStart - called when compression started 2) OnSuccess - called when compression completed with no errors/exceptions 3) OnFailure - called when an exception occurred or video bitrate and size are below the minimum required for compression. 4) OnProgress - called with progress new value 5) OnCancelled - called when the job is cancelled

Important Notes:

  • All the callback functions returns an index for the video being compressed in the same order of the urls passed to the library. You can use this index to update the UI or retrieve information about the original uri/file.
  • The source video must be provided as a list of content uris.
  • If you want an output video that is optimised to be streamed, ensure you pass [isStreamable] flag is true.

Configuration values

  • VideoQuality: VERY_HIGH (original-bitrate * 0.6) , HIGH (original-bitrate * 0.4), MEDIUM (original-bitrate * 0.3), LOW (original-bitrate * 0.2), OR VERY_LOW (original-bitrate * 0.1)

  • isMinBitrateCheckEnabled: this means, don't compress if bitrate is less than 2mbps

  • frameRate: any fps value

  • videoBitrate: any custom bitrate value

  • disableAudio: true/false to generate a video without audio. False by default.

  • keepOriginalResolution: true/false to tell the library not to change the resolution.

  • videoWidth: custom video width.

  • videoHeight: custom video height.

To cancel the compression job, just call [VideoCompressor.cancel()]

Examples

Kotlin:

VideoCompressor.start(
   context = applicationContext, // => This is required
   uris = List<Uri>, // => Source can be provided as content uris
   isStreamable = true,
   saveAt = Environment.DIRECTORY_MOVIES, // => the directory to save the compressed video(s)
   listener = object : CompressionListener {
       override fun onProgress(index: Int, percent: Float) {
          // Update UI with progress value
          runOnUiThread {
          }
       }

       override fun onStart(index: Int) {
          // Compression start
       }

       override fun onSuccess(index: Int, size: Long, path: String?) {
         // On Compression success
       }

       override fun onFailure(index: Int, failureMessage: String) {
         // On Failure
       }

       override fun onCancelled(index: Int) {
         // On Cancelled
       }

   },
   configureWith = Configuration(
      quality = VideoQuality.MEDIUM,
      frameRate = 24, /*Int, ignore, or null*/
      isMinBitrateCheckEnabled = true,
      videoBitrate = 3677198, /*Int, ignore, or null*/
      disableAudio = false, /*Boolean, or ignore*/
      keepOriginalResolution = false, /*Boolean, or ignore*/
      videoWidth = 360.0, /*Double, ignore, or null*/
      videoHeight = 480.0 /*Double, ignore, or null*/
   )
)

Java:

 VideoCompressor.start(
    applicationContext, // => This is required
    new ArrayList<Uri>(), // => Source can be provided as content uris
    false, // => isStreamable
    Environment.DIRECTORY_DOWNLOADS, // => the directory to save the compressed video(s)
    new CompressionListener() {
       @Override
       public void onStart(int index, long size) {
         // Compression start
       }

       @Override
       public void onSuccess(int index, @Nullable String path) {
         // On Compression success
       }

       @Override
       public void onFailure(int index, String failureMessage) {
         // On Failure
       }

       @Override
       public void onProgress(int index, float progressPercent) {
         // Update UI with progress value
         runOnUiThread(new Runnable() {
            public void run() {
           }
         });
       }

       @Override
       public void onCancelled(int index) {
         // On Cancelled
       }
    }, new Configuration(
        VideoQuality.MEDIUM,
        24, /*frameRate: int, or null*/
        false, /*isMinBitrateCheckEnabled*/
        null, /*videoBitrate: int, or null*/
        false, /*disableAudio: Boolean, or null*/
        false, /*keepOriginalResolution: Boolean, or null*/
        360.0, /*videoWidth: Double, or null*/
        480.0 /*videoHeight: Double, or null*/
    )
);

Reference

Read more here.

airatlovesmusic/Kompressor

Unfortunately, most of compression libs still work with filesystem and don't support contentUris. It can cause problems with new SDK versions, no matter if you have a android:requestLegacyExternalStorage=”true" in manifest or not. Remember, contentUriscan point not only to local files, but also to "virtual" files on the cloud. Therefore, this lib is built on input and output streams and it doesn't work with files.

Step 1: Install it

Install it as shown below:

dependencies {
    implementation 'com.github.airatlovesmusic:Kompressor:0.2.0'
}

Step 2: Compress Image

Compress your image as shown below:

val inputStream = context.contentResolver.openInputStream(contentUri)
val compressedData = imageCompressor.compress(
        inputStream = context.contentResolver.openInputStream(contentUri),
        format = Bitmap.CompressFormat.JPEG,
        quality = 70
)
val compressedBitmap = BitmapFactory.decodeStream(compressedData)

Do whatever you want with received OutputStream, for example you can make Retrofit RequestBody with bytes from stream.

Configuration!

Let's compress something, but with configuration!

val inputStream = context.contentResolver.openInputStream(contentUri)
val compressedData = imageCompressor.compress(
        inputStream = context.contentResolver.openInputStream(contentUri),
        format = Bitmap.CompressFormat.PNG,
        quality = 70,
        steps = resolutionCompression(outHeight = 720, outWidth = 1024) + sizeCompression(FILE_MAX_SIZE)
)
val compressedBitmap = BitmapFactory.decodeStream(compressedData)

This library has multiple compression constraints you can combine: - resolutionCompression (set up required height and width of result image), - sizeCompression (set up max file size of image), - rotationCompression (rotate image) -

Custom Configuration

Let's compress something, but with custom configuration!

val inputStream = context.contentResolver.openInputStream(contentUri)
val compressedData = imageCompressor.compress(
        inputStream = context.contentResolver.openInputStream(contentUri),
        format = Bitmap.CompressFormat.PNG,
        quality = 70,
        steps = resolutionCompression(outHeight = 720, outWidth = 1024) + { bm: Bitmap -> bm }
)
val compressedBitmap = BitmapFactory.decodeStream(compressedData)

You can also easily add your own custom compression steps by passing function which takes Bitmap and returns Bitmap.

Reference

Read more here

SimformSolutionsPvtLtd/SSffmpegVideoOperation

FFmpeg compiled for Android.

Execute FFmpeg commands with ease in your Android app.

Getting Started

This project is provide in-build FFmpeg operation queries:

Video operation ffmpeg queries like

  • Cut video using time
  • Convert image to video
  • Add water mark on video
  • Add text on video
  • Combine image image and video
  • Combine images
  • Combine videos
  • Compress a video
  • Extract frames from video
  • Fast/Slow motion video
  • Reverse video
  • video fade in / fade out
  • Compress video to GIF
  • Rotate and Flip video (Mirroring)
  • Remove audio from video
  • Update aspect ratio of video

Other extra operation FFmpeg queries like

  • Merge GIFs
  • Merge Audios
  • Update audio volume
  • Fast/Slow audio
  • Crop audio using time
  • Compress Audio

Architectures

FFmpeg Android runs on the following architectures: - arm-v7a, arm-v7a-neon, arm64-v8a, x86 and x86_64

Features

  • Enabled network capabilities
  • Multi-threading
  • Supports zlib and Media-codec system libraries
  • Camera access on supported devices
  • Supports API Level 24+

Support target sdk

  • 30

Dependency

Step 1: Install it

Add it in your root build.gradle at the end of repositories:

    allprojects {
        repositories {
        ...
        maven { url 'https://jitpack.io' }
        }
    }

Add the dependency in your app's build.gradle file:

    dependencies {
        implementation 'com.github.SimformSolutionsPvtLtd:SSffmpegVideoOperation:1.0.8'
    }

This is all you have to do to load the FFmpeg library.

Step 2: Run FFmpeg command

In this sample code we will run the FFmpeg -version command in background call.

  val query:Array<String> = "-i, input,....,...., outout"
        CallBackOfQuery().callQuery(query, object : FFmpegCallBack {
            override fun statisticsProcess(statistics: Statistics) {
                Log.i("FFMPEG LOG : ", statistics.videoFrameNumber)
            }

            override fun process(logMessage: LogMessage) {
                Log.i("FFMPEG LOG : ", logMessage.text)
            }

            override fun success() {
            }

            override fun cancel() {
            }

            override fun failed() {
            }
        })

In-build query example

val startTimeString = "00:01:00" (HH:MM:SS)
val endTimeString = "00:02:00" (HH:MM:SS)
val query:Array<String> = FFmpegQueryExtension().cutVideo(inputPath, startTimeString, endTimeString, outputPath)
CallBackOfQuery().callQuery(query, object : FFmpegCallBack {
            override fun statisticsProcess(statistics: Statistics) {
                Log.i("FFMPEG LOG : ", statistics.videoFrameNumber)
            }

            override fun process(logMessage: LogMessage) {
                Log.i("FFMPEG LOG : ", logMessage.text)
            }

            override fun success() {
                //Output = outputPath
            }

            override fun cancel() {
            }

            override fun failed() {
            }
        })
same for other queries. And you can apply your query also.

Reference

Read more here

jeziellago/image-minifier

A Minifier is a lightweight (21KB) android library for image resizing, format changing and quality focusing in reduce file size.

Step 1: Install it

Add to Project-level build.gradle:

allprojects {
    repositories {
        ...
        maven { url 'https://jitpack.io' }
    }
}
Add to Module-level build.gradle:

dependencies {
    implementation 'com.github.jeziellago:image-minifier:0.1.1'
}

Step 2: Compress Image

With an image file, apply one or multiples transformations:

MinifierFactory.create(context)
    .withImage(originalFile)
    .addTransformations {
        resize(1200, 720)
        convertTo(CompressFormat.JPEG)
    }
    .minify {
        onSuccess { minified -> /* success */ }
        onFailure { error -> /* failure */ }
    }
or use coroutines:

val minifiedFile: File = MinifierFactory.create(context)
    .withImage(originalFile)
    .addTransformations {
        resize(1200, 720)
        convertTo(CompressFormat.JPEG)
    }
    .minify(Dispatchers.IO)

Step 3: Apply Transformations

Resize:

resize(1200, 720)

Format:

convertTo(CompressFormat.JPEG)
Gray scale:

colorGrayScale()
Quality ```kotlin:

quality(80) ```

Reference

Read more here.