Centralize JPEG compression to a single function
This commit is contained in:
@@ -108,17 +108,7 @@ fun decodeJpeg(jpegBytes: ByteArray): Mat? {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun encodeJpeg(mat: Mat, jpegQuality: Int): ByteArray? {
|
fun encodeJpeg(mat: Mat, jpegQuality: Int): ByteArray? {
|
||||||
val params = MatOfInt(Imgcodecs.IMWRITE_JPEG_QUALITY, jpegQuality.coerceIn(0, 100))
|
return runCatching {
|
||||||
val encoded = MatOfByte()
|
org.fairscan.imageprocessing.encodeJpeg(mat, jpegQuality)
|
||||||
val ok = Imgcodecs.imencode(".jpg", mat, encoded, params)
|
}.getOrNull()
|
||||||
params.release()
|
|
||||||
|
|
||||||
if (!ok) {
|
|
||||||
encoded.release()
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
|
|
||||||
val result = encoded.toArray()
|
|
||||||
encoded.release()
|
|
||||||
return result
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,45 +14,55 @@
|
|||||||
*/
|
*/
|
||||||
package org.fairscan.app.platform
|
package org.fairscan.app.platform
|
||||||
|
|
||||||
import android.graphics.Bitmap
|
|
||||||
import android.graphics.BitmapFactory
|
|
||||||
import androidx.core.graphics.scale
|
|
||||||
import org.fairscan.app.data.ImageTransformations
|
import org.fairscan.app.data.ImageTransformations
|
||||||
import org.opencv.core.MatOfInt
|
import org.fairscan.imageprocessing.encodeJpeg
|
||||||
|
import org.opencv.core.Mat
|
||||||
|
import org.opencv.core.Size
|
||||||
import org.opencv.imgcodecs.Imgcodecs
|
import org.opencv.imgcodecs.Imgcodecs
|
||||||
|
import org.opencv.imgproc.Imgproc
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import kotlin.math.min
|
import kotlin.math.min
|
||||||
|
|
||||||
class OpenCvTransformations : ImageTransformations {
|
class OpenCvTransformations : ImageTransformations {
|
||||||
|
|
||||||
override fun rotate(
|
override fun rotate(
|
||||||
inputFile: File,
|
inputFile: File,
|
||||||
outputFile: File,
|
outputFile: File,
|
||||||
rotationDegrees: Int,
|
rotationDegrees: Int,
|
||||||
jpegQuality: Int
|
jpegQuality: Int
|
||||||
) {
|
) {
|
||||||
val src = Imgcodecs.imread(inputFile.absolutePath)
|
transform(inputFile, outputFile, jpegQuality) {
|
||||||
require(!src.empty()) { "Could not load image from ${inputFile.absolutePath}" }
|
org.fairscan.imageprocessing.rotate(it, rotationDegrees)
|
||||||
|
|
||||||
val dst = org.fairscan.imageprocessing.rotate(src, rotationDegrees)
|
|
||||||
|
|
||||||
val params = MatOfInt(Imgcodecs.IMWRITE_JPEG_QUALITY, jpegQuality)
|
|
||||||
if (!Imgcodecs.imwrite(outputFile.absolutePath, dst, params)) {
|
|
||||||
throw RuntimeException("Could not write image to ${outputFile.absolutePath}")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
params.release()
|
|
||||||
src.release()
|
|
||||||
dst.release()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun resize(inputFile: File, outputFile: File, maxSize: Int) {
|
override fun resize(inputFile: File, outputFile: File, maxSize: Int) {
|
||||||
val bitmap = BitmapFactory.decodeFile(inputFile.absolutePath)
|
transform(inputFile, outputFile, 85) { src ->
|
||||||
val ratio = min(maxSize.toFloat() / bitmap.width, maxSize.toFloat() / bitmap.height)
|
val ratio = min(maxSize.toFloat() / src.width(), maxSize.toFloat() / src.height())
|
||||||
val newW = (bitmap.width * ratio).toInt()
|
val newW = (src.width() * ratio).toDouble()
|
||||||
val newH = (bitmap.height * ratio).toInt()
|
val newH = (src.height() * ratio).toDouble()
|
||||||
val scaled = bitmap.scale(newW, newH)
|
val scaled = Mat()
|
||||||
outputFile.outputStream().use {
|
Imgproc.resize(src, scaled, Size(newW, newH))
|
||||||
scaled.compress(Bitmap.CompressFormat.JPEG, 85, it)
|
scaled
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun transform(
|
||||||
|
inputFile: File,
|
||||||
|
outputFile: File,
|
||||||
|
jpegQuality: Int,
|
||||||
|
transform: (Mat) -> Mat,
|
||||||
|
) {
|
||||||
|
val input = Imgcodecs.imread(inputFile.absolutePath)
|
||||||
|
var output: Mat? = null
|
||||||
|
try {
|
||||||
|
require(!input.empty()) { "Could not load image from ${inputFile.absolutePath}" }
|
||||||
|
output = transform.invoke(input)
|
||||||
|
val outputBytes = encodeJpeg(output, jpegQuality)
|
||||||
|
outputFile.writeBytes(outputBytes)
|
||||||
|
} finally {
|
||||||
|
input.release()
|
||||||
|
output?.release()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,8 +15,12 @@
|
|||||||
package org.fairscan.imageprocessing
|
package org.fairscan.imageprocessing
|
||||||
|
|
||||||
import org.opencv.core.Mat
|
import org.opencv.core.Mat
|
||||||
|
import org.opencv.core.MatOfByte
|
||||||
|
import org.opencv.core.MatOfInt
|
||||||
import org.opencv.core.Size
|
import org.opencv.core.Size
|
||||||
|
import org.opencv.imgcodecs.Imgcodecs
|
||||||
import org.opencv.imgproc.Imgproc
|
import org.opencv.imgproc.Imgproc
|
||||||
|
import java.io.IOException
|
||||||
import kotlin.math.sqrt
|
import kotlin.math.sqrt
|
||||||
|
|
||||||
fun resizeForMaxPixels(img: Mat, maxPixels: Double): Mat {
|
fun resizeForMaxPixels(img: Mat, maxPixels: Double): Mat {
|
||||||
@@ -30,3 +34,19 @@ fun resizeForMaxPixels(img: Mat, maxPixels: Double): Mat {
|
|||||||
Imgproc.resize(img, resizedImg, size, 0.0, 0.0, Imgproc.INTER_AREA)
|
Imgproc.resize(img, resizedImg, size, 0.0, 0.0, Imgproc.INTER_AREA)
|
||||||
return resizedImg
|
return resizedImg
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun encodeJpeg(mat: Mat, jpegQuality: Int): ByteArray {
|
||||||
|
val params = MatOfInt(Imgcodecs.IMWRITE_JPEG_QUALITY, jpegQuality.coerceIn(0, 100))
|
||||||
|
val encoded = MatOfByte()
|
||||||
|
val ok = Imgcodecs.imencode(".jpg", mat, encoded, params)
|
||||||
|
params.release()
|
||||||
|
|
||||||
|
if (!ok) {
|
||||||
|
encoded.release()
|
||||||
|
throw IOException("Failed to encode JPEG")
|
||||||
|
}
|
||||||
|
|
||||||
|
val result = encoded.toArray()
|
||||||
|
encoded.release()
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user