From 348802efc5b2c3c2949b2051bc87fd071e3c56a1 Mon Sep 17 00:00:00 2001 From: Ming Ming Date: Sat, 10 Sep 2022 19:24:14 +0800 Subject: [PATCH] Add color pop enhancement --- app/lib/help_utils.dart | 1 + app/lib/l10n/app_en.arb | 8 + app/lib/l10n/untranslated-messages.txt | 22 +++ app/lib/widget/handler/enhance_handler.dart | 77 +++++++++ plugin/android/src/main/cpp/deep_lap_3.cpp | 161 ++++++++++++++---- plugin/android/src/main/cpp/deep_lap_3.h | 5 + .../src/main/cpp/filter/saturation.cpp | 20 +-- .../android/src/main/cpp/filter/saturation.h | 17 ++ .../plugin/ImageProcessorChannelHandler.kt | 29 ++++ .../nc_photos/plugin/ImageProcessorService.kt | 17 ++ .../plugin/image_processor/DeepLab3.kt | 32 ++++ plugin/lib/src/image_processor.dart | 19 +++ 12 files changed, 356 insertions(+), 52 deletions(-) create mode 100644 plugin/android/src/main/cpp/filter/saturation.h diff --git a/app/lib/help_utils.dart b/app/lib/help_utils.dart index 23567dd8..c48ee3a4 100644 --- a/app/lib/help_utils.dart +++ b/app/lib/help_utils.dart @@ -8,4 +8,5 @@ const enhanceZeroDceUrl = "https://bit.ly/3wKJcm9"; const enhanceDeepLabPortraitBlurUrl = "https://bit.ly/3wIuXy6"; const enhanceEsrganUrl = "https://bit.ly/3wO0NJP"; const enhanceStyleTransferUrl = "https://bit.ly/3agpTcF"; +const enhanceDeepLabColorPopUrl = "https://bit.ly/3Rx0YCD"; const editPhotosUrl = "https://bit.ly/3v82oKA"; diff --git a/app/lib/l10n/app_en.arb b/app/lib/l10n/app_en.arb index 4ecefe3f..52a36b71 100644 --- a/app/lib/l10n/app_en.arb +++ b/app/lib/l10n/app_en.arb @@ -1266,6 +1266,14 @@ "@enhanceStyleTransferStyleDialogTitle": { "description": "Pick a reference image for the style transfer algorithm" }, + "enhanceColorPopTitle": "Color pop", + "@enhanceColorPopTitle": { + "description": "Desaturate the background of a photo" + }, + "enhanceGenericParamWeightLabel": "Weight", + "@enhanceGenericParamWeightLabel": { + "description": "This generic parameter sets the weight of the applied effect. The effect will be more obvious when the weight is high." + }, "doubleTapExitNotification": "Tap again to exit", "@doubleTapExitNotification": { "description": "If double tap to exit is enabled in settings, shown when users tap the back button" diff --git a/app/lib/l10n/untranslated-messages.txt b/app/lib/l10n/untranslated-messages.txt index 26639ff9..4f100a89 100644 --- a/app/lib/l10n/untranslated-messages.txt +++ b/app/lib/l10n/untranslated-messages.txt @@ -118,6 +118,8 @@ "enhanceSuperResolution4xTitle", "enhanceStyleTransferTitle", "enhanceStyleTransferStyleDialogTitle", + "enhanceColorPopTitle", + "enhanceGenericParamWeightLabel", "doubleTapExitNotification", "imageEditDiscardDialogTitle", "imageEditDiscardDialogContent", @@ -293,6 +295,8 @@ "enhanceSuperResolution4xTitle", "enhanceStyleTransferTitle", "enhanceStyleTransferStyleDialogTitle", + "enhanceColorPopTitle", + "enhanceGenericParamWeightLabel", "doubleTapExitNotification", "imageEditDiscardDialogTitle", "imageEditDiscardDialogContent", @@ -359,6 +363,8 @@ "shareMethodOriginalFileDescription", "collectionEditedPhotosLabel", "enhanceStyleTransferStyleDialogTitle", + "enhanceColorPopTitle", + "enhanceGenericParamWeightLabel", "doubleTapExitNotification", "imageEditDiscardDialogTitle", "imageEditDiscardDialogContent", @@ -419,6 +425,8 @@ "shareMethodOriginalFileTitle", "shareMethodOriginalFileDescription", "collectionEditedPhotosLabel", + "enhanceColorPopTitle", + "enhanceGenericParamWeightLabel", "imageEditToolbarColorLabel", "imageEditToolbarTransformLabel", "imageEditTransformOrientation", @@ -447,6 +455,8 @@ "shareMethodOriginalFileTitle", "shareMethodOriginalFileDescription", "collectionEditedPhotosLabel", + "enhanceColorPopTitle", + "enhanceGenericParamWeightLabel", "imageEditToolbarColorLabel", "imageEditToolbarTransformLabel", "imageEditTransformOrientation", @@ -501,6 +511,8 @@ "enhanceSuperResolution4xTitle", "enhanceStyleTransferTitle", "enhanceStyleTransferStyleDialogTitle", + "enhanceColorPopTitle", + "enhanceGenericParamWeightLabel", "doubleTapExitNotification", "imageEditDiscardDialogTitle", "imageEditDiscardDialogContent", @@ -603,6 +615,8 @@ "enhanceSuperResolution4xTitle", "enhanceStyleTransferTitle", "enhanceStyleTransferStyleDialogTitle", + "enhanceColorPopTitle", + "enhanceGenericParamWeightLabel", "doubleTapExitNotification", "imageEditDiscardDialogTitle", "imageEditDiscardDialogContent", @@ -684,6 +698,8 @@ "enhanceSuperResolution4xTitle", "enhanceStyleTransferTitle", "enhanceStyleTransferStyleDialogTitle", + "enhanceColorPopTitle", + "enhanceGenericParamWeightLabel", "doubleTapExitNotification", "imageEditDiscardDialogTitle", "imageEditDiscardDialogContent", @@ -765,6 +781,8 @@ "enhanceSuperResolution4xTitle", "enhanceStyleTransferTitle", "enhanceStyleTransferStyleDialogTitle", + "enhanceColorPopTitle", + "enhanceGenericParamWeightLabel", "doubleTapExitNotification", "imageEditDiscardDialogTitle", "imageEditDiscardDialogContent", @@ -846,6 +864,8 @@ "enhanceSuperResolution4xTitle", "enhanceStyleTransferTitle", "enhanceStyleTransferStyleDialogTitle", + "enhanceColorPopTitle", + "enhanceGenericParamWeightLabel", "doubleTapExitNotification", "imageEditDiscardDialogTitle", "imageEditDiscardDialogContent", @@ -927,6 +947,8 @@ "enhanceSuperResolution4xTitle", "enhanceStyleTransferTitle", "enhanceStyleTransferStyleDialogTitle", + "enhanceColorPopTitle", + "enhanceGenericParamWeightLabel", "doubleTapExitNotification", "imageEditDiscardDialogTitle", "imageEditDiscardDialogContent", diff --git a/app/lib/widget/handler/enhance_handler.dart b/app/lib/widget/handler/enhance_handler.dart index 706ed2de..7e4cb050 100644 --- a/app/lib/widget/handler/enhance_handler.dart +++ b/app/lib/widget/handler/enhance_handler.dart @@ -115,6 +115,20 @@ class EnhanceHandler { isSaveToServer: isSaveToServer, ); break; + + case _Algorithm.deepLab3ColorPop: + await ImageProcessor.deepLab3ColorPop( + "${account.url}/${file.path}", + file.filename, + Pref().getEnhanceMaxWidthOr(), + Pref().getEnhanceMaxHeightOr(), + args["weight"], + headers: { + "Authorization": Api.getAuthorizationHeaderValue(account), + }, + isSaveToServer: isSaveToServer, + ); + break; } } @@ -180,6 +194,13 @@ class EnhanceHandler { ); List<_Option> _getOptions() => [ + if (platform_k.isAndroid) + _Option( + title: L10n.global().enhanceColorPopTitle, + subtitle: "DeepLap v3", + link: enhanceDeepLabColorPopUrl, + algorithm: _Algorithm.deepLab3ColorPop, + ), if (platform_k.isAndroid) _Option( title: L10n.global().enhanceLowLightTitle, @@ -223,6 +244,9 @@ class EnhanceHandler { case _Algorithm.arbitraryStyleTransfer: return _getArbitraryStyleTransferArgs(context); + + case _Algorithm.deepLab3ColorPop: + return _getDeepLab3ColorPopArgs(context); } } @@ -347,6 +371,58 @@ class EnhanceHandler { } } + Future?> _getDeepLab3ColorPopArgs( + BuildContext context) async { + var current = 1.0; + final weight = await showDialog( + context: context, + builder: (context) => AppTheme( + child: AlertDialog( + title: Text(L10n.global().enhanceGenericParamWeightLabel), + contentPadding: const EdgeInsets.fromLTRB(24.0, 20.0, 24.0, 0), + content: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisSize: MainAxisSize.min, + children: [ + Row( + mainAxisSize: MainAxisSize.max, + children: [ + Icon( + Icons.water_drop, + size: 20, + color: AppTheme.getSecondaryTextColor(context), + ), + Expanded( + child: StatefulSlider( + initialValue: current, + onChangeEnd: (value) { + current = value; + }, + ), + ), + Icon( + Icons.water_drop_outlined, + color: AppTheme.getSecondaryTextColor(context), + ), + ], + ), + ], + ), + actions: [ + TextButton( + onPressed: () { + Navigator.of(context).pop(current); + }, + child: Text(L10n.global().enhanceButtonLabel), + ), + ], + ), + ), + ); + _log.info("[_getDeepLab3ColorPopArgs] weight: $weight"); + return weight?.run((it) => {"weight": it}); + } + bool isAtLeast4GbRam() { // We can't compare with 4096 directly as some RAM are preserved return AndroidInfo().totalMemMb > 3584; @@ -368,6 +444,7 @@ enum _Algorithm { deepLab3Portrait, esrgan, arbitraryStyleTransfer, + deepLab3ColorPop, } class _Option { diff --git a/plugin/android/src/main/cpp/deep_lap_3.cpp b/plugin/android/src/main/cpp/deep_lap_3.cpp index d33a52de..31906475 100644 --- a/plugin/android/src/main/cpp/deep_lap_3.cpp +++ b/plugin/android/src/main/cpp/deep_lap_3.cpp @@ -13,6 +13,8 @@ #include #include +#include "./filter/saturation.h" + using namespace plugin; using namespace renderscript; using namespace std; @@ -24,6 +26,7 @@ constexpr const char *MODEL = "tf/lite-model_mobilenetv2-dm05-coco_dr_1.tflite"; constexpr size_t WIDTH = 513; constexpr size_t HEIGHT = 513; constexpr unsigned LABEL_COUNT = 21; +constexpr const char *TAG = "deep_lap_3"; enum struct Label { BACKGROUND = 0, @@ -72,18 +75,6 @@ public: const size_t height, const unsigned radius); private: - /** - * Post-process the segment map. - * - * The resulting segment map will: - * 1. Contain only the most significant label (the one with the most pixel) - * 2. The label value set to 255 - * 3. The background set to 0 - * - * @param segmentMap - */ - void postProcessSegmentMap(std::vector *segmentMap); - std::vector enhance(const uint8_t *image, const size_t width, const size_t height, const std::vector &segmentMap, @@ -94,6 +85,36 @@ private: static constexpr const char *TAG = "DeepLab3Portrait"; }; +class DeepLab3ColorPop { +public: + explicit DeepLab3ColorPop(DeepLab3 &&deepLab); + + std::vector infer(const uint8_t *image, const size_t width, + const size_t height, const float weight); + +private: + std::vector enhance(const uint8_t *image, const size_t width, + const size_t height, + const std::vector &segmentMap, + const float weight); + + DeepLab3 deepLab; + + static constexpr const char *TAG = "DeepLab3ColorPop"; +}; + +/** + * Post-process the segment map. + * + * The resulting segment map will: + * 1. Contain only the most significant label (the one with the most pixel) + * 2. The label value set to 255 + * 3. The background set to 0 + * + * @param segmentMap + */ +void postProcessSegmentMap(std::vector *segmentMap); + } // namespace extern "C" JNIEXPORT jbyteArray JNICALL @@ -121,6 +142,31 @@ Java_com_nkming_nc_1photos_plugin_image_1processor_DeepLab3Portrait_inferNative( } } +extern "C" JNIEXPORT jbyteArray JNICALL +Java_com_nkming_nc_1photos_plugin_image_1processor_DeepLab3ColorPop_inferNative( + JNIEnv *env, jobject *thiz, jobject assetManager, jbyteArray image, + jint width, jint height, jfloat weight) { + try { + initOpenMp(); + auto aam = AAssetManager_fromJava(env, assetManager); + DeepLab3ColorPop model(DeepLab3{aam}); + RaiiContainer cImage( + [&]() { return env->GetByteArrayElements(image, nullptr); }, + [&](jbyte *obj) { + env->ReleaseByteArrayElements(image, obj, JNI_ABORT); + }); + const auto result = model.infer(reinterpret_cast(cImage.get()), + width, height, weight); + auto resultAry = env->NewByteArray(result.size()); + env->SetByteArrayRegion(resultAry, 0, result.size(), + reinterpret_cast(result.data())); + return resultAry; + } catch (const exception &e) { + throwJavaException(env, e.what()); + return nullptr; + } +} + namespace { DeepLab3::DeepLab3(AAssetManager *const aam) : model(Asset(aam, MODEL)) {} @@ -168,31 +214,6 @@ vector DeepLab3Portrait::infer(const uint8_t *image, return enhance(image, width, height, segmentMap, radius); } -void DeepLab3Portrait::postProcessSegmentMap(vector *segmentMap) { - // keep only the largest segment - vector &segmentMapRef = *segmentMap; - vector count(LABEL_COUNT); - for (size_t i = 0; i < segmentMapRef.size(); ++i) { - assert(segmentMapRef[i] < LABEL_COUNT); - const auto label = std::min(segmentMapRef[i], LABEL_COUNT); - if (label != static_cast(Label::BACKGROUND)) { - ++count[label]; - } - } - const auto keep = distance( - count.data(), max_element(count.data(), count.data() + count.size())); - LOGI(TAG, "[postProcessSegmentMap] Label to keep: %d", - static_cast(keep)); -#pragma omp parallel for - for (size_t i = 0; i < segmentMapRef.size(); ++i) { - if (segmentMapRef[i] == keep) { - segmentMapRef[i] = 0xFF; - } else { - segmentMapRef[i] = 0; - } - } -} - vector DeepLab3Portrait::enhance(const uint8_t *image, const size_t width, const size_t height, @@ -222,4 +243,70 @@ vector DeepLab3Portrait::enhance(const uint8_t *image, return rgba8ToRgb8(blur.data(), width, height); } +DeepLab3ColorPop::DeepLab3ColorPop(DeepLab3 &&deepLab) + : deepLab(move(deepLab)) {} + +vector DeepLab3ColorPop::infer(const uint8_t *image, + const size_t width, const size_t height, + const float weight) { + auto segmentMap = deepLab.infer(image, width, height); + postProcessSegmentMap(&segmentMap); + return enhance(image, width, height, segmentMap, weight); +} + +vector DeepLab3ColorPop::enhance(const uint8_t *image, + const size_t width, + const size_t height, + const vector &segmentMap, + const float weight) { + LOGI(TAG, "[enhance] Enhancing image"); + // resize alpha to input size + vector alpha(width * height); + base::ResampleImage<1>(segmentMap.data(), WIDTH, HEIGHT, alpha.data(), width, + height, base::KernelTypeLanczos3); + // smoothen the edge + vector alphaFiltered(width * height); + getToolkitInst().blur(alpha.data(), alphaFiltered.data(), width, height, 1, + 4); + alpha.clear(); + + // desaturate input + auto rgba8 = rgb8ToRgba8(image, width, height); + vector desaturate(width * height * 4); + plugin::filter::Saturation saturation; + desaturate = saturation.apply(rgba8.data(), width, height, -1 * weight); + + // draw input on top of blurred image, with alpha map + replaceChannel<4>(rgba8.data(), alphaFiltered.data(), width, height, 3); + alphaFiltered.clear(); + alphaBlend(rgba8.data(), desaturate.data(), width, height); + rgba8.clear(); + return rgba8ToRgb8(desaturate.data(), width, height); +} + +void postProcessSegmentMap(vector *segmentMap) { + // keep only the largest segment + vector &segmentMapRef = *segmentMap; + vector count(LABEL_COUNT); + for (size_t i = 0; i < segmentMapRef.size(); ++i) { + assert(segmentMapRef[i] < LABEL_COUNT); + const auto label = std::min(segmentMapRef[i], LABEL_COUNT); + if (label != static_cast(Label::BACKGROUND)) { + ++count[label]; + } + } + const auto keep = distance( + count.data(), max_element(count.data(), count.data() + count.size())); + LOGI(TAG, "[postProcessSegmentMap] Label to keep: %d", + static_cast(keep)); +#pragma omp parallel for + for (size_t i = 0; i < segmentMapRef.size(); ++i) { + if (segmentMapRef[i] == keep) { + segmentMapRef[i] = 0xFF; + } else { + segmentMapRef[i] = 0; + } + } +} + } // namespace diff --git a/plugin/android/src/main/cpp/deep_lap_3.h b/plugin/android/src/main/cpp/deep_lap_3.h index be94e4e4..81aae761 100644 --- a/plugin/android/src/main/cpp/deep_lap_3.h +++ b/plugin/android/src/main/cpp/deep_lap_3.h @@ -11,6 +11,11 @@ Java_com_nkming_nc_1photos_plugin_image_1processor_DeepLab3Portrait_inferNative( JNIEnv *env, jobject *thiz, jobject assetManager, jbyteArray image, jint width, jint height, jint radius); +JNIEXPORT jbyteArray JNICALL +Java_com_nkming_nc_1photos_plugin_image_1processor_DeepLab3ColorPop_inferNative( + JNIEnv *env, jobject *thiz, jobject assetManager, jbyteArray image, + jint width, jint height, jfloat weight); + #ifdef __cplusplus } #endif diff --git a/plugin/android/src/main/cpp/filter/saturation.cpp b/plugin/android/src/main/cpp/filter/saturation.cpp index 3e95f408..fb60a253 100644 --- a/plugin/android/src/main/cpp/filter/saturation.cpp +++ b/plugin/android/src/main/cpp/filter/saturation.cpp @@ -11,23 +11,11 @@ #include "../math_util.h" #include "../util.h" #include "./hslhsv.h" +#include "./saturation.h" using namespace plugin; using namespace std; -namespace { - -class Saturation { -public: - std::vector apply(const uint8_t *rgba8, const size_t width, - const size_t height, const float weight); - -private: - static constexpr const char *TAG = "Saturation"; -}; - -} // namespace - extern "C" JNIEXPORT jbyteArray JNICALL Java_com_nkming_nc_1photos_plugin_image_1processor_Saturation_applyNative( JNIEnv *env, jobject *thiz, jbyteArray rgba8, jint width, jint height, @@ -39,7 +27,7 @@ Java_com_nkming_nc_1photos_plugin_image_1processor_Saturation_applyNative( [&](jbyte *obj) { env->ReleaseByteArrayElements(rgba8, obj, JNI_ABORT); }); - const auto result = Saturation().apply( + const auto result = filter::Saturation().apply( reinterpret_cast(cRgba8.get()), width, height, value); auto resultAry = env->NewByteArray(result.size()); env->SetByteArrayRegion(resultAry, 0, result.size(), @@ -51,7 +39,8 @@ Java_com_nkming_nc_1photos_plugin_image_1processor_Saturation_applyNative( } } -namespace { +namespace plugin { +namespace filter { vector Saturation::apply(const uint8_t *rgba8, const size_t width, const size_t height, const float weight) { @@ -75,3 +64,4 @@ vector Saturation::apply(const uint8_t *rgba8, const size_t width, } } // namespace +} diff --git a/plugin/android/src/main/cpp/filter/saturation.h b/plugin/android/src/main/cpp/filter/saturation.h new file mode 100644 index 00000000..3530a09b --- /dev/null +++ b/plugin/android/src/main/cpp/filter/saturation.h @@ -0,0 +1,17 @@ +#include +#include + +namespace plugin { +namespace filter { + +class Saturation { +public: + std::vector apply(const uint8_t *rgba8, const size_t width, + const size_t height, const float weight); + +private: + static constexpr const char *TAG = "Saturation"; +}; + +} // namespace filter +} // namespace plugin diff --git a/plugin/android/src/main/kotlin/com/nkming/nc_photos/plugin/ImageProcessorChannelHandler.kt b/plugin/android/src/main/kotlin/com/nkming/nc_photos/plugin/ImageProcessorChannelHandler.kt index d6fd252a..35fa34f5 100644 --- a/plugin/android/src/main/kotlin/com/nkming/nc_photos/plugin/ImageProcessorChannelHandler.kt +++ b/plugin/android/src/main/kotlin/com/nkming/nc_photos/plugin/ImageProcessorChannelHandler.kt @@ -92,6 +92,24 @@ class ImageProcessorChannelHandler(context: Context) : } } + "deepLab3ColorPop" -> { + try { + deepLab3ColorPop( + call.argument("fileUrl")!!, + call.argument("headers"), + call.argument("filename")!!, + call.argument("maxWidth")!!, + call.argument("maxHeight")!!, + call.argument("isSaveToServer")!!, + call.argument("weight")!!, + result + ) + } catch (e: Throwable) { + logE(TAG, "Uncaught exception", e) + result.error("systemException", e.toString(), null) + } + } + "filter" -> { try { filter( @@ -181,6 +199,17 @@ class ImageProcessorChannelHandler(context: Context) : } ) + private fun deepLab3ColorPop( + fileUrl: String, headers: Map?, filename: String, + maxWidth: Int, maxHeight: Int, isSaveToServer: Boolean, weight: Float, + result: MethodChannel.Result + ) = method( + fileUrl, headers, filename, maxWidth, maxHeight, isSaveToServer, + ImageProcessorService.METHOD_DEEP_LAP_COLOR_POP, result, onIntent = { + it.putExtra(ImageProcessorService.EXTRA_WEIGHT, weight) + } + ) + private fun filter( fileUrl: String, headers: Map?, filename: String, maxWidth: Int, maxHeight: Int, isSaveToServer: Boolean, diff --git a/plugin/android/src/main/kotlin/com/nkming/nc_photos/plugin/ImageProcessorService.kt b/plugin/android/src/main/kotlin/com/nkming/nc_photos/plugin/ImageProcessorService.kt index d04dbb22..fc82d5b9 100644 --- a/plugin/android/src/main/kotlin/com/nkming/nc_photos/plugin/ImageProcessorService.kt +++ b/plugin/android/src/main/kotlin/com/nkming/nc_photos/plugin/ImageProcessorService.kt @@ -31,6 +31,7 @@ class ImageProcessorService : Service() { const val METHOD_DEEP_LAP_PORTRAIT = "DeepLab3Portrait" const val METHOD_ESRGAN = "Esrgan" const val METHOD_ARBITRARY_STYLE_TRANSFER = "ArbitraryStyleTransfer" + const val METHOD_DEEP_LAP_COLOR_POP = "DeepLab3ColorPop" const val METHOD_FILTER = "Filter" const val EXTRA_FILE_URL = "fileUrl" const val EXTRA_HEADERS = "headers" @@ -49,6 +50,7 @@ class ImageProcessorService : Service() { METHOD_DEEP_LAP_PORTRAIT, METHOD_ESRGAN, METHOD_ARBITRARY_STYLE_TRANSFER, + METHOD_DEEP_LAP_COLOR_POP, ) val EDIT_METHODS = listOf( METHOD_FILTER, @@ -128,6 +130,9 @@ class ImageProcessorService : Service() { METHOD_ARBITRARY_STYLE_TRANSFER -> onArbitraryStyleTransfer( startId, intent.extras!! ) + METHOD_DEEP_LAP_COLOR_POP -> onDeepLapColorPop( + startId, intent.extras!! + ) METHOD_FILTER -> onFilter(startId, intent.extras!!) else -> { logE(TAG, "Unknown method: $method") @@ -172,6 +177,14 @@ class ImageProcessorService : Service() { ) } + private fun onDeepLapColorPop(startId: Int, extras: Bundle) { + return onMethod( + startId, extras, METHOD_DEEP_LAP_COLOR_POP, args = mapOf( + "weight" to extras.getFloat(EXTRA_WEIGHT) + ) + ) + } + private fun onFilter(startId: Int, extras: Bundle) { val filters = extras.getSerializable(EXTRA_FILTERS)!! .asType>() @@ -465,6 +478,10 @@ private class ImageProcessorEnhanceCommand( args["weight"] as Float ).infer(fileUri) + ImageProcessorService.METHOD_DEEP_LAP_COLOR_POP -> DeepLab3ColorPop( + context, maxWidth, maxHeight, args["weight"] as Float + ).infer(fileUri) + else -> throw IllegalArgumentException("Unknown method: $method") } } diff --git a/plugin/android/src/main/kotlin/com/nkming/nc_photos/plugin/image_processor/DeepLab3.kt b/plugin/android/src/main/kotlin/com/nkming/nc_photos/plugin/image_processor/DeepLab3.kt index 3aca32d3..a0eff4a0 100644 --- a/plugin/android/src/main/kotlin/com/nkming/nc_photos/plugin/image_processor/DeepLab3.kt +++ b/plugin/android/src/main/kotlin/com/nkming/nc_photos/plugin/image_processor/DeepLab3.kt @@ -45,3 +45,35 @@ class DeepLab3Portrait( private val maxHeight = maxHeight private val radius = radius } + +class DeepLab3ColorPop( + context: Context, maxWidth: Int, maxHeight: Int, weight: Float +) { + fun infer(imageUri: Uri): Bitmap { + val width: Int + val height: Int + val rgb8Image = BitmapUtil.loadImage( + context, imageUri, maxWidth, maxHeight, BitmapResizeMethod.FIT, + isAllowSwapSide = true, shouldUpscale = false + ).use { + width = it.width + height = it.height + TfLiteHelper.bitmapToRgb8Array(it) + } + val am = context.assets + + return inferNative(am, rgb8Image, width, height, weight).let { + TfLiteHelper.rgb8ArrayToBitmap(it, width, height) + } + } + + private external fun inferNative( + am: AssetManager, image: ByteArray, width: Int, height: Int, + weight: Float + ): ByteArray + + private val context = context + private val maxWidth = maxWidth + private val maxHeight = maxHeight + private val weight = weight +} diff --git a/plugin/lib/src/image_processor.dart b/plugin/lib/src/image_processor.dart index e6821a86..1106b3c4 100644 --- a/plugin/lib/src/image_processor.dart +++ b/plugin/lib/src/image_processor.dart @@ -151,6 +151,25 @@ class ImageProcessor { "isSaveToServer": isSaveToServer, }); + static Future deepLab3ColorPop( + String fileUrl, + String filename, + int maxWidth, + int maxHeight, + double weight, { + Map? headers, + required bool isSaveToServer, + }) => + _methodChannel.invokeMethod("deepLab3ColorPop", { + "fileUrl": fileUrl, + "headers": headers, + "filename": filename, + "maxWidth": maxWidth, + "maxHeight": maxHeight, + "weight": weight, + "isSaveToServer": isSaveToServer, + }); + static Future filter( String fileUrl, String filename,