Skip to content

Commit

Permalink
[Feature] Add Neural Style Transfer to apply to image(s)
Browse files Browse the repository at this point in the history
  • Loading branch information
rh-id committed Jul 26, 2023
1 parent e6e8a4f commit 4734d80
Show file tree
Hide file tree
Showing 31 changed files with 635 additions and 20 deletions.
10 changes: 9 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,17 @@ The power of AI in the palm of my hands!

This is AI utility app for Android device, it basically uses AI model directly on-device to help on performing some tasks.

## Features
### Face Detection & Recognition
<ul>
<li>Detect and add blur effect on people face and export the result to gallery</li>
<li>Detect and select which face to be blurred and export the result to gallery</li>
</ul>

### Neural Style Transfer
Current features:
<ul>
<li>For image files: Detect and add blur effect on people face and export the image to gallery</li>
<li>Select neural style transfer theme to apply to the image(s) and export the result to gallery</li>
</ul>


Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
import m.co.rh.id.a_jarwis.app.ui.page.HomePage;
import m.co.rh.id.a_jarwis.app.ui.page.SplashPage;
import m.co.rh.id.a_jarwis.app.ui.page.common.SelectFaceImagePage;
import m.co.rh.id.a_jarwis.app.ui.page.common.SelectNSTThemePage;
import m.co.rh.id.a_jarwis.app.ui.page.common.ShowMessagePage;
import m.co.rh.id.a_jarwis.base.constants.Routes;
import m.co.rh.id.a_jarwis.settings.ui.page.SettingsPage;
Expand Down Expand Up @@ -51,12 +52,13 @@ public INavigator getNavigator(Activity activity) {

@SuppressLint("InflateParams")
@SuppressWarnings("unchecked")
private Navigator setupMainActivityNavigator() {
private void setupMainActivityNavigator() {
Map<String, StatefulViewFactory> navMap = new ArrayMap<>();
navMap.put(Routes.SPLASH_PAGE, (args, activity) -> new SplashPage(Routes.HOME_PAGE));
navMap.put(Routes.HOME_PAGE, (args, activity) -> new HomePage());
navMap.put(Routes.SHOW_MESSAGE_PAGE, (args, activity) -> new ShowMessagePage());
navMap.put(Routes.SELECT_FACE_IMAGE_PAGE, (args, activity) -> new SelectFaceImagePage());
navMap.put(Routes.SELECT_NST_THEME_PAGE, (args, activity) -> new SelectNSTThemePage());
navMap.put(Routes.SETTINGS_PAGE, (args, activity) -> new SettingsPage());
navMap.put(Routes.DONATIONS_PAGE, (args, activity) -> new DonationsPage());
navMap.putAll(mNavExtDialogConfig.getNavMap());
Expand All @@ -71,7 +73,6 @@ private Navigator setupMainActivityNavigator() {
mActivityNavigatorMap.put(MainActivity.class, navigator);
mApplication.registerActivityLifecycleCallbacks(navigator);
mApplication.registerComponentCallbacks(navigator);
return navigator;
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,5 +8,6 @@ public class CommandProviderModule implements ProviderModule {
@Override
public void provides(ProviderRegistry providerRegistry, Provider provider) {
providerRegistry.registerLazy(BlurFaceCommand.class, () -> new BlurFaceCommand(provider));
providerRegistry.registerLazy(NSTApplyCommand.class, () -> new NSTApplyCommand(provider));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package m.co.rh.id.a_jarwis.app.provider.command;

import android.net.Uri;

import java.io.File;
import java.util.concurrent.ExecutorService;

import io.reactivex.rxjava3.core.Single;
import m.co.rh.id.a_jarwis.base.provider.component.helper.FileHelper;
import m.co.rh.id.a_jarwis.ml_engine.provider.component.NSTEngine;
import m.co.rh.id.aprovider.Provider;

public class NSTApplyCommand {
private final ExecutorService mExecutorService;
private final FileHelper mFileHelper;
private final NSTEngine mNSTEngine;

public NSTApplyCommand(Provider provider) {
mExecutorService = provider.get(ExecutorService.class);
mFileHelper = provider.get(FileHelper.class);
mNSTEngine = provider.get(NSTEngine.class);
}

public Single<File> execute(Uri uriFile, int theme) {
return Single.fromFuture(mExecutorService.submit(() -> {
String fullPath = uriFile.getPath();
int cut = fullPath.lastIndexOf("/");
File result = mFileHelper
.createImageTempFile(fullPath.substring(cut + 1), uriFile);
mNSTEngine.enqueueNsT(result, theme);
return result;
}));
}
}
86 changes: 86 additions & 0 deletions app/src/main/java/m/co/rh/id/a_jarwis/app/ui/page/HomePage.java
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,11 @@
import io.reactivex.rxjava3.schedulers.Schedulers;
import m.co.rh.id.a_jarwis.R;
import m.co.rh.id.a_jarwis.app.provider.command.BlurFaceCommand;
import m.co.rh.id.a_jarwis.app.provider.command.NSTApplyCommand;
import m.co.rh.id.a_jarwis.app.ui.page.nav.param.FileList;
import m.co.rh.id.a_jarwis.app.ui.page.nav.param.MessageText;
import m.co.rh.id.a_jarwis.app.ui.page.nav.param.SelectedChoice;
import m.co.rh.id.a_jarwis.app.ui.page.nav.param.SelectedTheme;
import m.co.rh.id.a_jarwis.base.constants.Routes;
import m.co.rh.id.a_jarwis.base.provider.IStatefulViewProvider;
import m.co.rh.id.a_jarwis.base.rx.RxDisposer;
Expand All @@ -50,6 +52,7 @@ public class HomePage extends StatefulView<Activity> implements RequireComponent
private static final int REQUEST_CODE_IMAGE_AUTO_BLUR_FACE = 1;
private static final int REQUEST_CODE_IMAGE_EXCLUDE_BLUR_FACE = 2;
private static final int REQUEST_CODE_IMAGE_SELECTIVE_BLUR_FACE = 3;
private static final int REQUEST_CODE_IMAGE_NST_APPLY_PICTURE = 4;

@NavInject
private transient INavigator mNavigator;
Expand All @@ -64,12 +67,14 @@ public class HomePage extends StatefulView<Activity> implements RequireComponent
private transient ExecutorService mExecutorService;
private transient RxDisposer mRxDisposer;
private transient BlurFaceCommand mBlurFaceCommand;
private transient NSTApplyCommand mNSTApplyCommand;

// View related
private transient DrawerLayout mDrawerLayout;
private transient View.OnClickListener mOnNavigationClicked;

private ArrayList<File> mfacesList;
private SelectedTheme mSelectedNSTTheme;

public HomePage() {
mAppBarSV = new AppBarSV();
Expand All @@ -82,6 +87,7 @@ public void provideComponent(Provider provider) {
mExecutorService = mSvProvider.get(ExecutorService.class);
mRxDisposer = mSvProvider.get(RxDisposer.class);
mBlurFaceCommand = mSvProvider.get(BlurFaceCommand.class);
mNSTApplyCommand = mSvProvider.get(NSTApplyCommand.class);
mOnNavigationClicked = view -> {
if (!mDrawerLayout.isOpen()) {
mDrawerLayout.open();
Expand Down Expand Up @@ -109,6 +115,8 @@ protected View createView(Activity activity, ViewGroup container) {
excludeBlurButton.setOnClickListener(this);
Button selectiveBlurButton = rootLayout.findViewById(R.id.button_selective_blur_face);
selectiveBlurButton.setOnClickListener(this);
Button nstApplyPictureButton = rootLayout.findViewById(R.id.button_nst_apply_picture);
nstApplyPictureButton.setOnClickListener(this);
ViewGroup containerAppBar = rootLayout.findViewById(R.id.container_app_bar);
containerAppBar.addView(mAppBarSV.buildView(activity, container));
return rootLayout;
Expand Down Expand Up @@ -191,6 +199,40 @@ public void onClick(View view) {
} else {
startSelectiveAutoBlur();
}
} else if (id == R.id.button_nst_apply_picture) {
Activity activity = mNavigator.getActivity();
if (ActivityCompat.checkSelfPermission(activity, Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(activity, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, REQUEST_CODE_IMAGE_NST_APPLY_PICTURE);
} else {
startNstApplyPicture();
}
}
}

private void startNstApplyPicture() {
int title = m.co.rh.id.a_jarwis.base.R.string.title_what_to_do;
int body = m.co.rh.id.a_jarwis.base.R.string.pick_image_for_nst_apply_picture;
mNavigator.push(Routes.SHOW_MESSAGE_PAGE, new MessageText(title, body, true)
, (navigator, navRoute, activity1, currentView) ->
startNstApplyPicture_processFirstRespond(navRoute));
}

private void startNstApplyPicture_processFirstRespond(NavRoute navRoute) {
Serializable serializable = navRoute.getRouteResult();
if (serializable instanceof SelectedChoice) {
int selectedChoice = ((SelectedChoice) serializable).getSelectedChoice();
if (SelectedChoice.POSITIVE == selectedChoice) {
mNavigator.push(Routes.SELECT_NST_THEME_PAGE, (navigator, navRoute1, activity1, currentView) ->
startNstApplyPicture_processSecondRespond(activity1, navRoute1));
}
}
}

private void startNstApplyPicture_processSecondRespond(Activity activity, NavRoute navRoute) {
Serializable serializable = navRoute.getRouteResult();
if (serializable instanceof SelectedTheme) {
mSelectedNSTTheme = (SelectedTheme) serializable;
pickImage(activity, REQUEST_CODE_IMAGE_NST_APPLY_PICTURE);
}
}

Expand Down Expand Up @@ -363,6 +405,40 @@ public void onActivityResult(View currentView, Activity activity, INavigator INa
.subscribeOn(Schedulers.from(mExecutorService))
.subscribe(consumeFile));
}
} else if (requestCode == REQUEST_CODE_IMAGE_NST_APPLY_PICTURE && resultCode == Activity.RESULT_OK) {
ClipData listPhoto = data.getClipData();
Uri fullPhotoUri = data.getData();
BiConsumer<File, Throwable> consumeFile = (file, throwable) -> {
if (throwable != null) {
mLogger.e(TAG, throwable.getMessage(), throwable);
} else {
mLogger.i(TAG, mSvProvider.getContext()
.getString(m.co.rh.id.a_jarwis.base.R.string.processing_,
file.getName()));
}
};
if (listPhoto != null) {
int count = listPhoto.getItemCount();
List<Uri> uriList = new ArrayList<>();
for (int i = 0; i < count; i++) {
uriList.add(listPhoto.getItemAt(i).getUri());
}
if (!uriList.isEmpty()) {
mRxDisposer.add("onActivityResult_nstApplyPicture_multiple",
Flowable.fromIterable(uriList)
.map(uri -> mNSTApplyCommand.execute(uri, mSelectedNSTTheme.getSelectedTheme())
.blockingGet())
.subscribeOn(Schedulers.from(mExecutorService))
.doOnError(throwable -> consumeFile.accept(null, throwable))
.subscribe(file -> consumeFile.accept(file, null))
);
}
} else {
mRxDisposer.add("onActivityResult_nstApplyPicture",
mNSTApplyCommand.execute(fullPhotoUri, mSelectedNSTTheme.getSelectedTheme())
.subscribeOn(Schedulers.from(mExecutorService))
.subscribe(consumeFile));
}
}
}

Expand All @@ -378,6 +454,16 @@ public void onRequestPermissionsResult(View currentView, Activity activity, INav
grantResults[0] == PackageManager.PERMISSION_GRANTED
) {
startExcludeAutoBlur();
} else if (requestCode == REQUEST_CODE_IMAGE_SELECTIVE_BLUR_FACE
&& grantResults.length > 0 &&
grantResults[0] == PackageManager.PERMISSION_GRANTED
) {
startSelectiveAutoBlur();
} else if (requestCode == REQUEST_CODE_IMAGE_NST_APPLY_PICTURE
&& grantResults.length > 0 &&
grantResults[0] == PackageManager.PERMISSION_GRANTED
) {
startNstApplyPicture();
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
package m.co.rh.id.a_jarwis.app.ui.page.common;

import android.app.Activity;
import android.view.View;
import android.view.ViewGroup;

import m.co.rh.id.a_jarwis.R;
import m.co.rh.id.a_jarwis.app.ui.page.nav.param.SelectedTheme;
import m.co.rh.id.a_jarwis.ml_engine.provider.component.NSTEngine;
import m.co.rh.id.anavigator.StatefulView;
import m.co.rh.id.anavigator.annotation.NavInject;
import m.co.rh.id.anavigator.component.INavigator;

public class SelectNSTThemePage extends StatefulView<Activity> implements View.OnClickListener {

@NavInject
private transient INavigator mNavigator;

@Override
protected View createView(Activity activity, ViewGroup container) {
View rootView = activity.getLayoutInflater().inflate(R.layout.page_select_nst_theme, container, false);
View mosaic = rootView.findViewById(R.id.container_mosaic_theme);
mosaic.setOnClickListener(this);
View candy = rootView.findViewById(R.id.container_candy_theme);
candy.setOnClickListener(this);
View rainPrincess = rootView.findViewById(R.id.container_rain_princess_theme);
rainPrincess.setOnClickListener(this);
View udnie = rootView.findViewById(R.id.container_udnie_theme);
udnie.setOnClickListener(this);
View pointilism = rootView.findViewById(R.id.container_pointilism_theme);
pointilism.setOnClickListener(this);
View backButton = rootView.findViewById(R.id.button_back);
backButton.setOnClickListener(this);
return rootView;
}

@Override
public void onClick(View view) {
int id = view.getId();
if (id == R.id.container_mosaic_theme) {
mNavigator.pop(new SelectedTheme(NSTEngine.THEME_MOSAIC));
} else if (id == R.id.container_candy_theme) {
mNavigator.pop(new SelectedTheme(NSTEngine.THEME_CANDY));
} else if (id == R.id.container_rain_princess_theme) {
mNavigator.pop(new SelectedTheme(NSTEngine.THEME_RAIN_PRINCESS));
} else if (id == R.id.container_udnie_theme) {
mNavigator.pop(new SelectedTheme(NSTEngine.THEME_UDNIE));
} else if (id == R.id.container_pointilism_theme) {
mNavigator.pop(new SelectedTheme(NSTEngine.THEME_POINTILISM));
} else if (id == R.id.button_back) {
mNavigator.pop();
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package m.co.rh.id.a_jarwis.app.ui.page.nav.param;

import java.io.Serializable;

public class SelectedTheme implements Serializable {
private int selectedTheme;

public SelectedTheme(int selectedTheme) {
this.selectedTheme = selectedTheme;
}

public int getSelectedTheme() {
return selectedTheme;
}
}
Binary file added app/src/main/res/drawable/nst_theme_candy.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added app/src/main/res/drawable/nst_theme_mosaic.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added app/src/main/res/drawable/nst_theme_udnie.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
11 changes: 11 additions & 0 deletions app/src/main/res/layout/page_home.xml
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,17 @@
android:gravity="center"
android:text="@string/title_selective_blur_face"
app:icon="@drawable/ic_face_black" />

<com.google.android.material.button.MaterialButton
android:id="@+id/button_nst_apply_picture"
style="@style/Theme.Asnapblur.HomeMenuButton"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_columnWeight="1"
android:layout_gravity="fill"
android:gravity="center"
android:text="@string/title_nst_apply_picture"
app:icon="@drawable/ic_palette_black" />
</GridLayout>
</LinearLayout>

Expand Down
Loading

0 comments on commit 4734d80

Please sign in to comment.