From 53b226a4654926e1d98a0e5e3fd6f5740b75f33e Mon Sep 17 00:00:00 2001
From: Pierre-Yves Nicolas <6371790+pynicolas@users.noreply.github.com>
Date: Sat, 4 Apr 2026 18:48:04 +0200
Subject: [PATCH] Centralize image processing for 'app' module
---
.../app/domain/DocumentDetectionTest.kt | 7 +-
.../java/org/fairscan/app/SessionViewModel.kt | 5 +-
.../org/fairscan/app/data/ImageRepository.kt | 6 +-
.../fairscan/app/data/ImageTransformations.kt | 4 +-
.../fairscan/app/domain/ExportPreparation.kt | 32 +---
.../fairscan/app/platform/ImageProcessor.kt | 145 ++++++++++++++++++
.../platform/OpenCvImageTransformations.kt | 93 -----------
.../app/ui/screens/camera/CameraViewModel.kt | 62 +-------
.../fairscan/app/data/ImageRepositoryTest.kt | 14 +-
9 files changed, 167 insertions(+), 201 deletions(-)
create mode 100644 app/src/main/java/org/fairscan/app/platform/ImageProcessor.kt
delete mode 100644 app/src/main/java/org/fairscan/app/platform/OpenCvImageTransformations.kt
diff --git a/app/src/androidTest/java/org/fairscan/app/domain/DocumentDetectionTest.kt b/app/src/androidTest/java/org/fairscan/app/domain/DocumentDetectionTest.kt
index 06f2bf4..0df6ce9 100644
--- a/app/src/androidTest/java/org/fairscan/app/domain/DocumentDetectionTest.kt
+++ b/app/src/androidTest/java/org/fairscan/app/domain/DocumentDetectionTest.kt
@@ -24,11 +24,10 @@ import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.runBlocking
-import org.fairscan.app.ui.screens.camera.extractDocumentFromBitmap
+import org.fairscan.app.platform.extractDocumentFromBitmap
import org.fairscan.app.ui.screens.settings.DefaultColorMode
import org.fairscan.imageprocessing.ImageSize
import org.fairscan.imageprocessing.detectDocumentQuad
-import org.fairscan.imageprocessing.scaledTo
import org.junit.Assert.assertEquals
import org.junit.Assert.fail
import org.junit.Test
@@ -61,10 +60,8 @@ class DocumentDetectionTest {
val mask = segmentationResult.segmentation
val quad = detectDocumentQuad(mask, ImageSize(bitmap.width, bitmap.height),false)
if (quad != null) {
- val resizedQuad =
- quad.scaledTo(mask.width, mask.height, bitmap.width, bitmap.height)
val auto = DefaultColorMode.AUTO
- val page = extractDocumentFromBitmap(bitmap, resizedQuad,0, mask, scope, auto)
+ val page = extractDocumentFromBitmap(bitmap, quad,0, mask, scope, auto)
outputJpeg = page.pageJpeg
val file = File(context.getExternalFilesDir(null), imageFileName)
file.writeBytes(outputJpeg.bytes)
diff --git a/app/src/main/java/org/fairscan/app/SessionViewModel.kt b/app/src/main/java/org/fairscan/app/SessionViewModel.kt
index ef97eb7..514f645 100644
--- a/app/src/main/java/org/fairscan/app/SessionViewModel.kt
+++ b/app/src/main/java/org/fairscan/app/SessionViewModel.kt
@@ -20,7 +20,7 @@ import androidx.lifecycle.AndroidViewModel
import androidx.lifecycle.viewModelScope
import kotlinx.coroutines.CoroutineScope
import org.fairscan.app.data.ImageRepository
-import org.fairscan.app.platform.OpenCvTransformations
+import org.fairscan.app.platform.ImageProcessor
import java.io.File
import java.util.UUID
@@ -66,8 +66,7 @@ class ScanSessionContainer(
val imageRepository = ImageRepository(
scanRootDir,
- OpenCvTransformations(),
- thumbnailSizePx,
+ ImageProcessor(thumbnailSizePx),
scope,
)
}
diff --git a/app/src/main/java/org/fairscan/app/data/ImageRepository.kt b/app/src/main/java/org/fairscan/app/data/ImageRepository.kt
index ab6bd62..189d56d 100644
--- a/app/src/main/java/org/fairscan/app/data/ImageRepository.kt
+++ b/app/src/main/java/org/fairscan/app/data/ImageRepository.kt
@@ -52,7 +52,6 @@ const val THUMBNAIL_DIR_NAME = "thumbnails"
class ImageRepository(
scanRootDir: File,
val transformations: ImageTransformations,
- private val thumbnailSizePx: Int,
private val scope: CoroutineScope,
) {
private val sourceDir = File(scanRootDir, SOURCE_DIR_NAME).apply { mkdirs() }
@@ -236,8 +235,7 @@ class ImageRepository(
} else {
transformations.rotate(
baseJpeg,
- key.rotation.degrees,
- ExportQuality.BALANCED.jpegQuality)
+ key.rotation.degrees)
}
}
@@ -245,7 +243,7 @@ class ImageRepository(
withContext(Dispatchers.IO) {
val processed = getOrCompute(imageCache, key, ::computeProcessedImage)
?: return@withContext null
- transformations.resize(processed, thumbnailSizePx)
+ transformations.resizeToThumbnail(processed)
}
// --- Other operations ---
diff --git a/app/src/main/java/org/fairscan/app/data/ImageTransformations.kt b/app/src/main/java/org/fairscan/app/data/ImageTransformations.kt
index e32ede5..efced7e 100644
--- a/app/src/main/java/org/fairscan/app/data/ImageTransformations.kt
+++ b/app/src/main/java/org/fairscan/app/data/ImageTransformations.kt
@@ -20,9 +20,9 @@ import org.fairscan.imageprocessing.ColorMode
interface ImageTransformations {
- fun rotate(input: Jpeg, rotationDegrees: Int, jpegQuality: Int): Jpeg
+ fun rotate(input: Jpeg, rotationDegrees: Int): Jpeg
- fun resize(input: Jpeg, maxSize: Int): Jpeg
+ fun resizeToThumbnail(input: Jpeg): Jpeg
fun process(source: Jpeg, metadata: PageMetadata, colorMode: ColorMode): Jpeg
diff --git a/app/src/main/java/org/fairscan/app/domain/ExportPreparation.kt b/app/src/main/java/org/fairscan/app/domain/ExportPreparation.kt
index a567365..45b24d5 100644
--- a/app/src/main/java/org/fairscan/app/domain/ExportPreparation.kt
+++ b/app/src/main/java/org/fairscan/app/domain/ExportPreparation.kt
@@ -15,10 +15,8 @@
package org.fairscan.app.domain
import org.fairscan.app.data.ImageRepository
-import org.fairscan.imageprocessing.ColorMode
-import org.fairscan.imageprocessing.extractDocument
+import org.fairscan.app.platform.processedImage
import org.fairscan.imageprocessing.resizeForMaxPixels
-import org.fairscan.imageprocessing.scaledTo
import org.opencv.core.Mat
fun interface JpegProvider {
@@ -52,8 +50,10 @@ suspend fun jpegsForExport(
val metadata = page.metadata
val manualRotation = page.manualRotation
val colorMode = page.colorMode
- if (source != null && metadata != null && colorMode != null)
- prepareJpegForHigh(source, metadata, manualRotation, colorMode, exportQuality)
+ if (source != null && metadata != null && colorMode != null) {
+ val rotation = metadata.baseRotation.add(manualRotation)
+ processedImage(source, metadata, rotation, colorMode, exportQuality)
+ }
else
jpeg(page, imageRepository)
}
@@ -83,25 +83,3 @@ private fun resizeJpegBytesForMaxPixels(
resized?.release()
}
}
-
-private fun prepareJpegForHigh(
- source: Jpeg,
- pageMetadata: PageMetadata,
- manualRotation: Rotation,
- colorMode: ColorMode,
- exportQuality: ExportQuality,
-): Jpeg {
-
- var decoded: Mat? = null
- var page: Mat? = null
- try {
- decoded = source.toMat()
- val quad = pageMetadata.normalizedQuad.scaledTo(1, 1, decoded.width(), decoded.height())
- val rotationDegrees = pageMetadata.baseRotation.add(manualRotation).degrees
- page = extractDocument(decoded, quad, rotationDegrees, colorMode, exportQuality.maxPixels)
- return Jpeg.fromMat(page, exportQuality.jpegQuality)
- } finally {
- decoded?.release()
- page?.release()
- }
-}
diff --git a/app/src/main/java/org/fairscan/app/platform/ImageProcessor.kt b/app/src/main/java/org/fairscan/app/platform/ImageProcessor.kt
new file mode 100644
index 0000000..193a71b
--- /dev/null
+++ b/app/src/main/java/org/fairscan/app/platform/ImageProcessor.kt
@@ -0,0 +1,145 @@
+/*
+ * Copyright 2025-2026 Pierre-Yves Nicolas
+ *
+ * This program is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation, either version 3 of the License, or (at your option)
+ * any later version.
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ * You should have received a copy of the GNU General Public License along with
+ * this program. If not, see .
+ */
+package org.fairscan.app.platform
+
+import android.graphics.Bitmap
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.async
+import org.fairscan.app.data.ImageTransformations
+import org.fairscan.app.domain.CapturedPage
+import org.fairscan.app.domain.ExportQuality
+import org.fairscan.app.domain.Jpeg
+import org.fairscan.app.domain.PageMetadata
+import org.fairscan.app.domain.Rotation
+import org.fairscan.app.ui.screens.settings.DefaultColorMode
+import org.fairscan.imageprocessing.ColorMode
+import org.fairscan.imageprocessing.Mask
+import org.fairscan.imageprocessing.Quad
+import org.fairscan.imageprocessing.autoColorMode
+import org.fairscan.imageprocessing.extractDocument
+import org.fairscan.imageprocessing.scaledTo
+import org.opencv.android.Utils
+import org.opencv.core.Mat
+import org.opencv.core.Size
+import org.opencv.imgproc.Imgproc
+import kotlin.math.min
+
+class ImageProcessor(private val thumbnailSizePx: Int) : ImageTransformations {
+
+ override fun rotate(input: Jpeg, rotationDegrees: Int): Jpeg {
+ return transform(input, ExportQuality.BALANCED.jpegQuality) {
+ org.fairscan.imageprocessing.rotate(it, rotationDegrees)
+ }
+ }
+
+ override fun resizeToThumbnail(input: Jpeg): Jpeg {
+ val maxSize = thumbnailSizePx.toFloat()
+ return transform(input, 85) { src ->
+ val ratio = min(maxSize / src.width(), maxSize / src.height())
+ val newW = (src.width() * ratio).toDouble()
+ val newH = (src.height() * ratio).toDouble()
+ val scaled = Mat()
+ Imgproc.resize(src, scaled, Size(newW, newH))
+ scaled
+ }
+ }
+
+ private fun transform(
+ inJpeg: Jpeg,
+ jpegQuality: Int,
+ transform: (Mat) -> Mat,
+ ): Jpeg {
+ val input = inJpeg.toMat()
+ var output: Mat? = null
+ try {
+ output = transform.invoke(input)
+ return Jpeg.fromMat(output, jpegQuality)
+ } finally {
+ input.release()
+ output?.release()
+ }
+ }
+
+ override fun process(source: Jpeg, metadata: PageMetadata, colorMode: ColorMode): Jpeg {
+ return processedImage(source, metadata, metadata.baseRotation, colorMode, ExportQuality.BALANCED)
+ }
+}
+
+fun processedImage(
+ source: Jpeg,
+ metadata: PageMetadata,
+ rotation: Rotation,
+ colorMode: ColorMode,
+ exportQuality: ExportQuality,
+): Jpeg {
+ val rotationDegrees = rotation.degrees
+ var sourceMat: Mat? = null
+ var page: Mat? = null
+ try {
+ sourceMat = source.toMat()
+ val quad = metadata.normalizedQuad.scaledTo(1, 1, sourceMat.width(), sourceMat.height())
+ page = extractDocument(sourceMat, quad, rotationDegrees, colorMode, exportQuality.maxPixels)
+ return Jpeg.fromMat(page, exportQuality.jpegQuality)
+ } finally {
+ sourceMat?.release()
+ page?.release()
+ }
+}
+
+fun extractDocumentFromBitmap(
+ source: Bitmap,
+ quadInMask: Quad,
+ rotationDegrees: Int,
+ mask: Mask,
+ viewModelScope: CoroutineScope,
+ defaultColorMode: DefaultColorMode = DefaultColorMode.AUTO
+): CapturedPage {
+ val exportQuality = ExportQuality.BALANCED
+ val quad = quadInMask.scaledTo(mask.width, mask.height, source.width, source.height)
+
+ val rgba = Mat()
+ Utils.bitmapToMat(source, rgba)
+ val bgr = Mat()
+ Imgproc.cvtColor(rgba, bgr, Imgproc.COLOR_RGBA2BGR)
+ rgba.release()
+ val autoColorMode = autoColorMode(bgr, mask, quad)
+ val colorMode = defaultColorMode.colorMode ?: autoColorMode
+ val page = extractDocument(bgr, quad, rotationDegrees, colorMode, exportQuality.maxPixels)
+ val pageJpeg = Jpeg.fromMat(page, exportQuality.jpegQuality)
+ bgr.release()
+ page.release()
+
+ val normalizedQuad = quad.scaledTo(source.width, source.height, 1, 1)
+ val baseRotation = Rotation.fromDegrees(rotationDegrees)
+ val metadata = PageMetadata(normalizedQuad, baseRotation, autoColorMode)
+ val sourceJpegDeferred = viewModelScope.async(Dispatchers.IO) {
+ compressSource(source)
+ }
+ return CapturedPage(pageJpeg, sourceJpegDeferred, metadata, colorMode)
+}
+
+private fun compressSource(source: Bitmap): Jpeg {
+ val rgba = Mat()
+ Utils.bitmapToMat(source, rgba)
+ val bgr = Mat()
+ Imgproc.cvtColor(rgba, bgr, Imgproc.COLOR_RGBA2BGR)
+ rgba.release()
+ return try {
+ Jpeg.fromMat(bgr, 90)
+ } finally {
+ bgr.release()
+ }
+}
diff --git a/app/src/main/java/org/fairscan/app/platform/OpenCvImageTransformations.kt b/app/src/main/java/org/fairscan/app/platform/OpenCvImageTransformations.kt
deleted file mode 100644
index 3ed0270..0000000
--- a/app/src/main/java/org/fairscan/app/platform/OpenCvImageTransformations.kt
+++ /dev/null
@@ -1,93 +0,0 @@
-/*
- * Copyright 2025-2026 Pierre-Yves Nicolas
- *
- * This program is free software: you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the Free
- * Software Foundation, either version 3 of the License, or (at your option)
- * any later version.
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
- * You should have received a copy of the GNU General Public License along with
- * this program. If not, see .
- */
-package org.fairscan.app.platform
-
-import org.fairscan.app.data.ImageTransformations
-import org.fairscan.app.domain.ExportQuality
-import org.fairscan.app.domain.Jpeg
-import org.fairscan.app.domain.PageMetadata
-import org.fairscan.imageprocessing.ColorMode
-import org.fairscan.imageprocessing.extractDocument
-import org.fairscan.imageprocessing.scaledTo
-import org.opencv.core.Mat
-import org.opencv.core.Size
-import org.opencv.imgproc.Imgproc
-import kotlin.math.min
-
-class OpenCvTransformations : ImageTransformations {
-
- override fun rotate(
- input: Jpeg,
- rotationDegrees: Int,
- jpegQuality: Int
- ): Jpeg {
- return transform(input, jpegQuality) {
- org.fairscan.imageprocessing.rotate(it, rotationDegrees)
- }
- }
-
- override fun resize(input: Jpeg, maxSize: Int): Jpeg {
- return transform(input, 85) { src ->
- val ratio = min(maxSize.toFloat() / src.width(), maxSize.toFloat() / src.height())
- val newW = (src.width() * ratio).toDouble()
- val newH = (src.height() * ratio).toDouble()
- val scaled = Mat()
- Imgproc.resize(src, scaled, Size(newW, newH))
- scaled
- }
- }
-
- private fun transform(
- inJpeg: Jpeg,
- jpegQuality: Int,
- transform: (Mat) -> Mat,
- ): Jpeg {
- val input = inJpeg.toMat()
- var output: Mat? = null
- try {
- output = transform.invoke(input)
- return Jpeg.fromMat(output, jpegQuality)
- } finally {
- input.release()
- output?.release()
- }
- }
-
- override fun process(source: Jpeg, metadata: PageMetadata, colorMode: ColorMode): Jpeg {
- val exportQuality = ExportQuality.BALANCED
- var sourceMat: Mat? = null
- var page: Mat? = null
- try {
- sourceMat = source.toMat()
- val quad = metadata.normalizedQuad.scaledTo(
- 1,
- 1,
- sourceMat.width(),
- sourceMat.height()
- )
- page = extractDocument(
- sourceMat,
- quad,
- metadata.baseRotation.degrees,
- colorMode,
- exportQuality.maxPixels
- )
- return Jpeg.fromMat(page, exportQuality.jpegQuality)
- } finally {
- sourceMat?.release()
- page?.release()
- }
- }
-}
diff --git a/app/src/main/java/org/fairscan/app/ui/screens/camera/CameraViewModel.kt b/app/src/main/java/org/fairscan/app/ui/screens/camera/CameraViewModel.kt
index 24844dd..fa548a2 100644
--- a/app/src/main/java/org/fairscan/app/ui/screens/camera/CameraViewModel.kt
+++ b/app/src/main/java/org/fairscan/app/ui/screens/camera/CameraViewModel.kt
@@ -19,9 +19,7 @@ import android.graphics.Matrix
import androidx.camera.core.ImageProxy
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
-import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
-import kotlinx.coroutines.async
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
@@ -32,21 +30,9 @@ import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import org.fairscan.app.AppContainer
import org.fairscan.app.domain.CapturedPage
-import org.fairscan.app.domain.ExportQuality
-import org.fairscan.app.domain.Jpeg
-import org.fairscan.app.domain.PageMetadata
-import org.fairscan.app.domain.Rotation
-import org.fairscan.app.ui.screens.settings.DefaultColorMode
+import org.fairscan.app.platform.extractDocumentFromBitmap
import org.fairscan.imageprocessing.ImageSize
-import org.fairscan.imageprocessing.Mask
-import org.fairscan.imageprocessing.Quad
import org.fairscan.imageprocessing.detectDocumentQuad
-import org.fairscan.imageprocessing.extractDocument
-import org.fairscan.imageprocessing.autoColorMode
-import org.fairscan.imageprocessing.scaledTo
-import org.opencv.android.Utils
-import org.opencv.core.Mat
-import org.opencv.imgproc.Imgproc
sealed interface CameraEvent {
data class ImageCaptured(val page: CapturedPage) : CameraEvent
@@ -167,10 +153,9 @@ class CameraViewModel(appContainer: AppContainer): ViewModel() {
val originalSize = ImageSize(source.width, source.height)
val quad = detectDocumentQuad(mask, originalSize, isLiveAnalysis = false)
if (quad != null) {
- val resizedQuad = quad.scaledTo(mask.width, mask.height, source.width, source.height)
val defaultColorMode = settingsRepository.defaultColorMode.first()
result = extractDocumentFromBitmap(
- source, resizedQuad, rotationDegrees, mask, viewModelScope, defaultColorMode)
+ source, quad, rotationDegrees, mask, viewModelScope, defaultColorMode)
}
}
return@withContext result
@@ -201,19 +186,6 @@ class CameraViewModel(appContainer: AppContainer): ViewModel() {
}
}
-private fun compressJpeg(bitmap: Bitmap, quality: Int): Jpeg {
- val rgba = Mat()
- Utils.bitmapToMat(bitmap, rgba)
- val bgr = Mat()
- Imgproc.cvtColor(rgba, bgr, Imgproc.COLOR_RGBA2BGR)
- rgba.release()
- return try {
- Jpeg.fromMat(bgr, quality)
- } finally {
- bgr.release()
- }
-}
-
sealed class CaptureState {
open val frozenImage: Bitmap? = null
@@ -226,36 +198,6 @@ sealed class CaptureState {
) : CaptureState()
}
-fun extractDocumentFromBitmap(
- source: Bitmap,
- quad: Quad,
- rotationDegrees: Int,
- mask: Mask,
- viewModelScope: CoroutineScope,
- defaultColorMode: DefaultColorMode = DefaultColorMode.AUTO
-): CapturedPage {
- val rgba = Mat()
- Utils.bitmapToMat(source, rgba)
- val bgr = Mat()
- Imgproc.cvtColor(rgba, bgr, Imgproc.COLOR_RGBA2BGR) // CV_8UC4 → CV_8UC3
- rgba.release()
- val autoColorMode = autoColorMode(bgr, mask, quad)
- val colorMode = defaultColorMode.colorMode ?: autoColorMode
- val maxPixels = ExportQuality.BALANCED.maxPixels
- val page = extractDocument(bgr, quad, rotationDegrees, colorMode, maxPixels)
- val pageJpeg = Jpeg.fromMat(page, ExportQuality.BALANCED.jpegQuality)
- bgr.release()
- page.release()
-
- val normalizedQuad = quad.scaledTo(source.width, source.height, 1, 1)
- val baseRotation = Rotation.fromDegrees(rotationDegrees)
- val metadata = PageMetadata(normalizedQuad, baseRotation, autoColorMode)
- val sourceJpegDeferred = viewModelScope.async(Dispatchers.IO) {
- compressJpeg(source, 90)
- }
- return CapturedPage(pageJpeg, sourceJpegDeferred, metadata, colorMode)
-}
-
fun rotateBitmap(source: Bitmap, angle: Float): Bitmap {
val matrix = Matrix()
matrix.postRotate(angle)
diff --git a/app/src/test/java/org/fairscan/app/data/ImageRepositoryTest.kt b/app/src/test/java/org/fairscan/app/data/ImageRepositoryTest.kt
index bcc362d..6cc0063 100644
--- a/app/src/test/java/org/fairscan/app/data/ImageRepositoryTest.kt
+++ b/app/src/test/java/org/fairscan/app/data/ImageRepositoryTest.kt
@@ -60,22 +60,22 @@ class ImageRepositoryTest {
}
fun repo(
- rotate: (Jpeg, Int, Int) -> Jpeg = { input, _, _ -> input },
- resize: (Jpeg, Int) -> Jpeg = { input, _ -> jpeg(input.bytes[0]) },
+ rotate: (Jpeg, Int) -> Jpeg = { input, _ -> input },
+ resizeToThumbnail: (Jpeg) -> Jpeg = { input -> jpeg(input.bytes[0]) },
process: (Jpeg, PageMetadata, ColorMode) -> Jpeg = { _, _, _ ->
throw UnsupportedOperationException()
}
): ImageRepository {
val transformations = object : ImageTransformations {
- override fun rotate(input: Jpeg, rotationDegrees: Int, jpegQuality: Int): Jpeg =
- rotate(input, rotationDegrees, jpegQuality)
- override fun resize(input: Jpeg, maxSize: Int): Jpeg =
- resize(input, maxSize)
+ override fun rotate(input: Jpeg, rotationDegrees: Int): Jpeg =
+ rotate(input, rotationDegrees)
+ override fun resizeToThumbnail(input: Jpeg): Jpeg =
+ resizeToThumbnail(input)
override fun process(source: Jpeg, metadata: PageMetadata, colorMode: ColorMode): Jpeg =
process(source, metadata, colorMode)
}
- return ImageRepository(getFilesDir(), transformations, 200, testScope)
+ return ImageRepository(getFilesDir(), transformations, testScope)
}
@Test