Skip to content

Commit

Permalink
[Merged] Added pose detection and correction
Browse files Browse the repository at this point in the history
Added pose detection and correction
  • Loading branch information
amrhossamdev authored Jun 23, 2024
2 parents cabb75d + e22d07c commit 0d6fe64
Show file tree
Hide file tree
Showing 65 changed files with 4,830 additions and 27 deletions.
23 changes: 20 additions & 3 deletions app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,8 @@ android {
applicationId = "com.modarb.android"
minSdk = 22
targetSdk = 34
versionCode = 3
versionName = "1.2"

versionCode = 4
versionName = "2.0"
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
}

Expand Down Expand Up @@ -71,6 +70,18 @@ dependencies {
//Circle progress bar
implementation("com.mikhaellopez:circularprogressbar:3.1.0")
implementation("androidx.webkit:webkit:1.11.0")
// Pose correction
implementation("com.google.mlkit:pose-detection:18.0.0-beta4")
implementation("com.google.mlkit:pose-detection-accurate:18.0.0-beta4")
// CameraX
implementation("androidx.camera:camera-camera2:1.3.4")
implementation("androidx.camera:camera-lifecycle:1.3.4")
implementation("androidx.camera:camera-view:1.3.4")

// On Device Machine Learnings
implementation("com.google.guava:guava:32.1.2-jre")
implementation("androidx.camera:camera-core:1.3.4")
implementation("com.google.android.gms:play-services-vision-common:19.1.3")

testImplementation("junit:junit:4.13.2")
androidTestImplementation("androidx.test.ext:junit:1.1.5")
Expand All @@ -79,3 +90,9 @@ dependencies {


}

//configurations {
// // Resolves dependency conflict caused by some dependencies use
// // com.google.guava:guava and com.google.guava:listenablefuture together.
// all*.exclude group: 'com.google.guava', module: 'listenablefuture'
//}
21 changes: 18 additions & 3 deletions app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,14 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">

<uses-feature
android:name="android.hardware.camera"
android:required="false" />

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

<application
android:allowBackup="true"
Expand All @@ -16,9 +23,7 @@
android:usesCleartextTraffic="true"
tools:ignore="DiscouragedApi"
tools:targetApi="31">
<activity
android:name=".ui.WebView"
android:exported="false" />
<activity android:name=".posedetection.RequestPermissionsActivity" />
<activity
android:name=".ui.workout.activities.WorkoutActivity"
android:exported="false"
Expand Down Expand Up @@ -102,6 +107,16 @@
android:exported="false"
android:screenOrientation="nosensor" />

<activity
android:name=".posedetection.CameraActivity"
android:exported="false"
android:screenOrientation="nosensor" />

<activity
android:name=".posedetection.CameraSettingsActivity"
android:exported="false"
android:screenOrientation="nosensor" />

<activity
android:name=".ui.ChatBotWebView"
android:exported="false"
Expand Down
812 changes: 812 additions & 0 deletions app/src/main/assets/pose/fitness_pose_samples.csv

Large diffs are not rendered by default.

Binary file added app/src/main/ic_launcher-playstore.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,11 @@ import retrofit2.converter.gson.GsonConverterFactory

object RetrofitService {

private var BASE_URL = "https://moahmedwafy-modarb-be.hf.space/"
var BASE_URL = "https://moahmedwafy-modarb-be.hf.space/"

fun changeBaseUrl(newUrl: String) {
BASE_URL = newUrl
}
inline fun <reified T> handleRequest(
response: Response<T>, onSuccess: (T) -> Unit, onError: (T?) -> Unit
) {
Expand Down
170 changes: 170 additions & 0 deletions app/src/main/java/com/modarb/android/posedetection/CameraActivity.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,170 @@
package com.modarb.android.posedetection

import android.content.Intent
import android.os.Bundle
import android.util.Log
import android.widget.CompoundButton
import android.widget.ImageView
import android.widget.Toast
import android.widget.ToggleButton
import androidx.appcompat.app.AppCompatActivity
import com.google.android.gms.common.annotation.KeepName
import com.modarb.android.R
import com.modarb.android.posedetection.Utils.CameraSource
import com.modarb.android.posedetection.Utils.CameraSourcePreview
import com.modarb.android.posedetection.Utils.PreferenceUtils
import com.modarb.android.posedetection.posedetector.PoseDetectorProcessor
import java.io.IOException

@KeepName
class CameraActivity : AppCompatActivity(),
CompoundButton.OnCheckedChangeListener {

private var cameraSource: CameraSource? = null
private var preview: CameraSourcePreview? = null
private var graphicOverlay: GraphicOverlay? = null

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
Log.d(TAG, "onCreate")
setContentView(R.layout.activity_camera_view)

preview = findViewById(R.id.preview_view)
if (preview == null) {
Log.d(TAG, "Preview is null")
}

graphicOverlay = findViewById(R.id.graphic_overlay)
if (graphicOverlay == null) {
Log.d(TAG, "graphicOverlay is null")
}
initSetting()
createCameraSource(POSE_DETECTION)
handleCameraSwitch()
}

private fun initSetting() {
val settingsButton = findViewById<ImageView>(R.id.settings_button)
settingsButton.setOnClickListener {
val intent = Intent(applicationContext, CameraSettingsActivity::class.java)
intent.putExtra(
CameraSettingsActivity.EXTRA_LAUNCH_SOURCE,
CameraSettingsActivity.LaunchSource.LIVE_PREVIEW
)
startActivity(intent)
}
}

private fun handleCameraSwitch() {
val facingSwitch = findViewById<ToggleButton>(R.id.facing_switch)
facingSwitch.setOnCheckedChangeListener(this)
}


override fun onCheckedChanged(buttonView: CompoundButton, isChecked: Boolean) {
Log.d(TAG, "Set facing")
if (cameraSource != null) {
if (isChecked) {
cameraSource?.setFacing(CameraSource.CAMERA_FACING_FRONT)
} else {
cameraSource?.setFacing(CameraSource.CAMERA_FACING_BACK)
}
}
preview?.stop()
startCameraSource()
}

private fun createCameraSource(model: String) {
if (cameraSource == null) {
cameraSource = CameraSource(this, graphicOverlay)
}
try {
when (model) {

POSE_DETECTION -> {
val poseDetectorOptions =
PreferenceUtils.getPoseDetectorOptionsForLivePreview(this)
Log.i(TAG, "Using Pose Detector with options $poseDetectorOptions")
val shouldShowInFrameLikelihood =
PreferenceUtils.shouldShowPoseDetectionInFrameLikelihoodLivePreview(this)
val visualizeZ = PreferenceUtils.shouldPoseDetectionVisualizeZ(this)
val rescaleZ = PreferenceUtils.shouldPoseDetectionRescaleZForVisualization(this)
val runClassification =
true /*PreferenceUtils.shouldPoseDetectionRunClassification(this)*/
cameraSource!!.setMachineLearningFrameProcessor(
PoseDetectorProcessor(
this,
poseDetectorOptions,
shouldShowInFrameLikelihood,
visualizeZ,
rescaleZ,
runClassification,
true
)
)
}

else -> Log.e(TAG, "Unknown model: $model")
}
} catch (e: Exception) {
Log.e(TAG, "Can not create image processor: $model", e)
Toast.makeText(
applicationContext,
"Can not create image processor: " + e.message,
Toast.LENGTH_LONG
).show()
}
}

private fun startCameraSource() {
if (cameraSource != null) {
try {
if (preview == null) {
Log.d(TAG, "resume: Preview is null")
}
if (graphicOverlay == null) {
Log.d(TAG, "resume: graphOverlay is null")
}
preview!!.start(cameraSource, graphicOverlay)
} catch (e: IOException) {
Log.e(TAG, "Unable to start camera source.", e)
cameraSource!!.release()
cameraSource = null
}
}
}

public override fun onResume() {
super.onResume()
Log.d(TAG, "onResume")
createCameraSource(POSE_DETECTION)
startCameraSource()
}

override fun onPause() {
super.onPause()
preview?.stop()
// TODO check that
cameraSource?.release()
}

override fun onStop() {
super.onStop()
cameraSource?.release()
preview?.stop()

}

public override fun onDestroy() {
super.onDestroy()
if (cameraSource != null) {
cameraSource?.release()
}
}


companion object {
private const val POSE_DETECTION = "Pose Detection"
private const val TAG = "LivePreviewActivity"
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@


package com.modarb.android.posedetection;

import android.hardware.Camera;
import android.os.Bundle;
import android.preference.ListPreference;
import android.preference.PreferenceCategory;
import android.preference.PreferenceFragment;

import androidx.annotation.StringRes;

import com.modarb.android.R;
import com.modarb.android.posedetection.Utils.CameraSource;
import com.modarb.android.posedetection.Utils.PreferenceUtils;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class CameraPreferenceFragment extends PreferenceFragment {

@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);

addPreferencesFromResource(R.xml.pref_camera_view);
setUpCameraPreferences();
}

void setUpCameraPreferences() {
PreferenceCategory cameraPreference =
(PreferenceCategory) findPreference(getString(R.string.pref_category_key_camera));
cameraPreference.removePreference(
findPreference(getString(R.string.pref_key_camerax_rear_camera_target_resolution)));
cameraPreference.removePreference(
findPreference(getString(R.string.pref_key_camerax_front_camera_target_resolution)));
setUpCameraPreviewSizePreference(
R.string.pref_key_rear_camera_preview_size,
R.string.pref_key_rear_camera_picture_size,
CameraSource.CAMERA_FACING_BACK);
setUpCameraPreviewSizePreference(
R.string.pref_key_front_camera_preview_size,
R.string.pref_key_front_camera_picture_size,
CameraSource.CAMERA_FACING_FRONT);
}

private void setUpCameraPreviewSizePreference(
@StringRes int previewSizePrefKeyId, @StringRes int pictureSizePrefKeyId, int cameraId) {
ListPreference previewSizePreference =
(ListPreference) findPreference(getString(previewSizePrefKeyId));

Camera camera = null;
try {
camera = Camera.open(cameraId);

List<CameraSource.SizePair> previewSizeList = CameraSource.generateValidPreviewSizeList(camera);
String[] previewSizeStringValues = new String[previewSizeList.size()];
Map<String, String> previewToPictureSizeStringMap = new HashMap<>();
for (int i = 0; i < previewSizeList.size(); i++) {
CameraSource.SizePair sizePair = previewSizeList.get(i);
previewSizeStringValues[i] = sizePair.preview.toString();
if (sizePair.picture != null) {
previewToPictureSizeStringMap.put(
sizePair.preview.toString(), sizePair.picture.toString());
}
}
previewSizePreference.setEntries(previewSizeStringValues);
previewSizePreference.setEntryValues(previewSizeStringValues);

if (previewSizePreference.getEntry() == null) {
CameraSource.SizePair sizePair =
CameraSource.selectSizePair(
camera,
CameraSource.DEFAULT_REQUESTED_CAMERA_PREVIEW_WIDTH,
CameraSource.DEFAULT_REQUESTED_CAMERA_PREVIEW_HEIGHT);
String previewSizeString = sizePair.preview.toString();
previewSizePreference.setValue(previewSizeString);
previewSizePreference.setSummary(previewSizeString);
PreferenceUtils.saveString(
getActivity(),
pictureSizePrefKeyId,
sizePair.picture != null ? sizePair.picture.toString() : null);
} else {
previewSizePreference.setSummary(previewSizePreference.getEntry());
}

previewSizePreference.setOnPreferenceChangeListener(
(preference, newValue) -> {
String newPreviewSizeStringValue = (String) newValue;
previewSizePreference.setSummary(newPreviewSizeStringValue);
PreferenceUtils.saveString(
getActivity(),
pictureSizePrefKeyId,
previewToPictureSizeStringMap.get(newPreviewSizeStringValue));
return true;
});
} catch (RuntimeException e) {
((PreferenceCategory) findPreference(getString(R.string.pref_category_key_camera)))
.removePreference(previewSizePreference);
} finally {
if (camera != null) {
camera.release();
}
}
}


}
Loading

0 comments on commit 0d6fe64

Please sign in to comment.