From 4eae6d821982d298666e37b64b3c1899528b4a6a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=80=9Ckamon?= <“kamon.sourabie@imt-atlantique.net”> Date: Thu, 19 Dec 2024 02:09:41 +0100 Subject: [PATCH 1/4] Affichage des resultats de prediction dans un tableau sur une autre page --- .../myapplication/OutputPredictedImage.java | 14 +-- .../com/example/myapplication/PhotoPage.java | 13 ++- .../myapplication/RecognizeSpecie.java | 41 +++++++++ .../myapplication/ResultsFragment.java | 91 +++++++++++++++++++ .../myapplication/TestModeleTflite.java | 12 ++- .../layout/fragment_prediction_results.xml | 53 +++++++++++ 6 files changed, 209 insertions(+), 15 deletions(-) create mode 100644 app/src/main/java/com/example/myapplication/RecognizeSpecie.java create mode 100644 app/src/main/java/com/example/myapplication/ResultsFragment.java create mode 100644 app/src/main/res/layout/fragment_prediction_results.xml diff --git a/app/src/main/java/com/example/myapplication/OutputPredictedImage.java b/app/src/main/java/com/example/myapplication/OutputPredictedImage.java index 950de8a..97423b8 100644 --- a/app/src/main/java/com/example/myapplication/OutputPredictedImage.java +++ b/app/src/main/java/com/example/myapplication/OutputPredictedImage.java @@ -3,6 +3,7 @@ package com.example.myapplication; import android.graphics.Bitmap; import android.graphics.BitmapFactory; +import java.util.List; import java.util.Map; /** @@ -10,21 +11,20 @@ import java.util.Map; */ public class OutputPredictedImage { private Bitmap annotatedImage; - private Map<String, Integer> predictionResults; + private Map<String, RecognizeSpecie> predictionResults; - public OutputPredictedImage(Bitmap annotatedImage, Map<String,Integer> predictionResults){ + public OutputPredictedImage(Bitmap annotatedImage, Map<String,RecognizeSpecie> predictionResults){ this.annotatedImage = annotatedImage; this.predictionResults = predictionResults; } + @Override public String toString(){ StringBuilder result = new StringBuilder(); - for (Map.Entry<String, Integer> entry : predictionResults.entrySet()) { + for (Map.Entry<String, RecognizeSpecie> entry : predictionResults.entrySet()) { // Append the key (species) and value (probability) in the specified format - result.append(entry.getKey()) - .append(" : ") - .append(entry.getValue()) // Format the float to 2 decimal places + result.append(entry.getValue().toString()) // Format the float to 2 decimal places .append("\n"); // Add a newline for separation } @@ -36,7 +36,7 @@ public class OutputPredictedImage { return annotatedImage; } - public Map<String, Integer> getPredictionResults() { + public Map<String, RecognizeSpecie> getPredictionResults() { return predictionResults; } diff --git a/app/src/main/java/com/example/myapplication/PhotoPage.java b/app/src/main/java/com/example/myapplication/PhotoPage.java index bd4eb1d..bd727d5 100644 --- a/app/src/main/java/com/example/myapplication/PhotoPage.java +++ b/app/src/main/java/com/example/myapplication/PhotoPage.java @@ -41,6 +41,7 @@ public class PhotoPage extends Fragment { TextView textPredictionOutput, textProbabilityOutput; ImageView imageView; FrameLayout formLayout; + OutputPredictedImage outputPredictedImage; int imageSize = 256; @@ -90,8 +91,14 @@ public class PhotoPage extends Fragment { if(predictionOutput == null){ Toast.makeText(getContext(), "Veuillez prendre une photo ou charger une image depuis votre gallerie.", Toast.LENGTH_LONG).show(); } - else Toast.makeText(getContext(), predictionOutput, Toast.LENGTH_LONG).show(); - + if(outputPredictedImage != null){ + ResultsFragment resultsFragment = new ResultsFragment(outputPredictedImage.getPredictionResults()); + requireActivity().getSupportFragmentManager() + .beginTransaction() + .replace(R.id.fragment_container, resultsFragment) // Assuming you have a container in your activity layout + .addToBackStack(null) + .commit(); + } } }); @@ -160,7 +167,7 @@ public class PhotoPage extends Fragment { try { TestModeleTflite testModeleTflite = new TestModeleTflite(this.getContext()); - OutputPredictedImage outputPredictedImage = testModeleTflite.recognizeSpeciesClass(bitmap); + outputPredictedImage = testModeleTflite.recognizeSpeciesClass(bitmap); Bitmap annotatedImage = outputPredictedImage.getAnnotatedImage(); double height = 1.5 * annotatedImage.getHeight(); double width = 1.5 * annotatedImage.getWidth(); diff --git a/app/src/main/java/com/example/myapplication/RecognizeSpecie.java b/app/src/main/java/com/example/myapplication/RecognizeSpecie.java new file mode 100644 index 0000000..43ae252 --- /dev/null +++ b/app/src/main/java/com/example/myapplication/RecognizeSpecie.java @@ -0,0 +1,41 @@ +package com.example.myapplication; + +import java.io.Serializable; +import java.util.List; + +public class RecognizeSpecie implements Serializable { + private String nameRecognizedSpecie; + private List<BoundingBox> specieBoxes; + + RecognizeSpecie(String nameRecognizedSpecie, List<BoundingBox> specieBoxes){ + this.nameRecognizedSpecie = nameRecognizedSpecie; + this.specieBoxes = specieBoxes; + } + + public String getNameRecognizedSpecie() { + return nameRecognizedSpecie; + } + + public List<BoundingBox> getSpecieBoxes() { + return specieBoxes; + } + + public String toString(){ + int totalNumber = specieBoxes.size(); + float sumProbability = 0; + for(BoundingBox boundingBox:specieBoxes){ + sumProbability += boundingBox.cnf; + } + String meanProbability = String.format("%.2f", 100 * (sumProbability / totalNumber)); + return nameRecognizedSpecie + " : " + totalNumber + "; precision " + meanProbability +"%"; + } + + public float getAverageProbability() { + int totalNumber = specieBoxes.size(); + float sumProbability = 0; + for(BoundingBox boundingBox:specieBoxes){ + sumProbability += boundingBox.cnf; + } + return 100 * (sumProbability / totalNumber); + } +} diff --git a/app/src/main/java/com/example/myapplication/ResultsFragment.java b/app/src/main/java/com/example/myapplication/ResultsFragment.java new file mode 100644 index 0000000..866329c --- /dev/null +++ b/app/src/main/java/com/example/myapplication/ResultsFragment.java @@ -0,0 +1,91 @@ +package com.example.myapplication; + +import android.os.Bundle; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.TableLayout; +import android.widget.TableRow; +import android.widget.TextView; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.fragment.app.Fragment; + +import java.util.Map; + +public class ResultsFragment extends Fragment { + + private Map<String, RecognizeSpecie> predictionResults; + + // Constructor to receive prediction results + public ResultsFragment(Map<String, RecognizeSpecie> predictionResults) { + this.predictionResults = predictionResults; + } + + @Nullable + @Override + public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { + // Inflate the layout for this fragment + View view = inflater.inflate(R.layout.fragment_prediction_results, container, false); + + // Reference the TableLayout + TableLayout tableLayout = view.findViewById(R.id.results_table); + + // Add a header row to the table + addHeaderRow(tableLayout); + + // Populate the table with prediction results + populateTable(tableLayout); + + return view; + } + + /** + * Adds a header row to the table layout. + */ + private void addHeaderRow(TableLayout tableLayout) { + TableRow headerRow = new TableRow(getContext()); + headerRow.setBackgroundColor(getResources().getColor(R.color.logo2_background)); // Optional: Add header styling + addTableCell(headerRow, "Espèce"); + addTableCell(headerRow, "Total individus"); + addTableCell(headerRow, "Précision(%)"); + tableLayout.addView(headerRow); + } + + /** + * Populates the table with rows based on the prediction results. + */ + private void populateTable(TableLayout tableLayout) { + if (predictionResults != null) { + for (Map.Entry<String, RecognizeSpecie> entry : predictionResults.entrySet()) { + TableRow dataRow = new TableRow(getContext()); + + RecognizeSpecie specie = entry.getValue(); + + addTableCell(dataRow, entry.getKey()); // Species name + addTableCell(dataRow, String.valueOf(specie.getSpecieBoxes().size())); // Count + addTableCell(dataRow, String.format("%.2f", specie.getAverageProbability())); // Average probability + + tableLayout.addView(dataRow); + } + } else { + // If no data exists, add a message row + TableRow emptyRow = new TableRow(getContext()); + addTableCell(emptyRow, "No data available"); + tableLayout.addView(emptyRow); + } + } + + /** + * Adds a cell to a given table row. + */ + private void addTableCell(TableRow row, String text) { + TextView textView = new TextView(getContext()); + textView.setText(text); + textView.setPadding(16, 16, 16, 16); + + textView.setTextColor(getResources().getColor(R.color.white)); // Optional: Add text styling + row.addView(textView); + } +} diff --git a/app/src/main/java/com/example/myapplication/TestModeleTflite.java b/app/src/main/java/com/example/myapplication/TestModeleTflite.java index 0a1c267..1cd882e 100644 --- a/app/src/main/java/com/example/myapplication/TestModeleTflite.java +++ b/app/src/main/java/com/example/myapplication/TestModeleTflite.java @@ -219,7 +219,7 @@ public class TestModeleTflite { ); canvas.drawRect(rect, paint); canvas.drawText(box.clsName, rect.left, rect.bottom, textPaint); - canvas.drawText(new StringBuilder().append("prob : ").append(String.format("%.2f", box.cnf)).toString(), rect.left, rect.top, textPaint); + //canvas.drawText(new StringBuilder().append("prob : ").append(String.format("%.2f", box.cnf)).toString(), rect.left, rect.top, textPaint); } return mutableBitmap; @@ -248,16 +248,18 @@ public class TestModeleTflite { public OutputPredictedImage recognizeSpeciesClass(Bitmap inputImage) throws NoSpeciesRecognizedException { //ArrayList<String> speciesNames = new ArrayList<>(); - Map<String, Integer> predictionResults = new HashMap<>(); + List<RecognizeSpecie> specieBoxes= new ArrayList<RecognizeSpecie>(); + Map<String, RecognizeSpecie> predictionResults = new HashMap<>(); + TensorBuffer outputBuffer = predict(inputImage); List<BoundingBox> boxes = processOutput(outputBuffer); if(boxes != null){ for(BoundingBox box:boxes){ if(predictionResults.containsKey(box.clsName)){ - int previousNumber = predictionResults.get(box.clsName); - predictionResults.put(box.clsName, previousNumber + 1); + predictionResults.get(box.clsName).getSpecieBoxes().add(box); }else{ - predictionResults.put(box.clsName, 1); + predictionResults.put(box.clsName, new RecognizeSpecie(box.clsName, new ArrayList<BoundingBox>())); + predictionResults.get(box.clsName).getSpecieBoxes().add(box); } } diff --git a/app/src/main/res/layout/fragment_prediction_results.xml b/app/src/main/res/layout/fragment_prediction_results.xml new file mode 100644 index 0000000..ac35b19 --- /dev/null +++ b/app/src/main/res/layout/fragment_prediction_results.xml @@ -0,0 +1,53 @@ +<?xml version="1.0" encoding="utf-8"?> +<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:app="http://schemas.android.com/apk/res-auto" + xmlns:tools="http://schemas.android.com/tools" + android:layout_width="match_parent" + android:layout_height="match_parent" + tools:context=".PhotoPage"> + + + <FrameLayout + style="@style/TopTopAppearance" + android:layout_width="362dp" + android:layout_height="93dp" + android:layout_gravity="center"> + + <ScrollView + android:layout_width="match_parent" + android:layout_height="match_parent"> + + <LinearLayout + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:orientation="vertical" + android:padding="16dp"> + + <TableLayout + android:id="@+id/results_table" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:stretchColumns="1"> + <!-- Rows will be added dynamically --> + </TableLayout> + + </LinearLayout> + </ScrollView> + + + </FrameLayout> + + + + + <TextView + style="@style/PageTitleAppearance" + android:id="@+id/learning_page_title" + android:layout_marginTop="50dp" + android:layout_marginBottom="60dp" + android:paddingTop="30dp" + android:paddingEnd="50dp" + android:text="@string/photo" /> + + +</FrameLayout> -- GitLab From 803fcc57f2333c5228fadc98defe2846046b6d5f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=80=9Ckamon?= <“kamon.sourabie@imt-atlantique.net”> Date: Sat, 4 Jan 2025 03:56:47 +0100 Subject: [PATCH 2/4] Amelioration de l'affichage des resultats de prediction sur PhotoPage --- .idea/deploymentTargetSelector.xml | 18 ++++ .../com/example/myapplication/PhotoPage.java | 82 +++++++++++++++++-- .../myapplication/ResultsFragment.java | 18 +++- .../main/res/layout/fragment_photo_page.xml | 67 +++++++-------- .../layout/fragment_prediction_results.xml | 26 +++++- app/src/main/res/values/colors.xml | 1 + app/src/main/res/values/strings.xml | 2 +- 7 files changed, 172 insertions(+), 42 deletions(-) create mode 100644 .idea/deploymentTargetSelector.xml diff --git a/.idea/deploymentTargetSelector.xml b/.idea/deploymentTargetSelector.xml new file mode 100644 index 0000000..bcc86d0 --- /dev/null +++ b/.idea/deploymentTargetSelector.xml @@ -0,0 +1,18 @@ +<?xml version="1.0" encoding="UTF-8"?> +<project version="4"> + <component name="deploymentTargetSelector"> + <selectionStates> + <SelectionState runConfigName="app"> + <option name="selectionMode" value="DROPDOWN" /> + <DropdownSelection timestamp="2025-01-04T02:38:58.283128700Z"> + <Target type="DEFAULT_BOOT"> + <handle> + <DeviceId pluginId="PhysicalDevice" identifier="serial=R39M30F5B2T" /> + </handle> + </Target> + </DropdownSelection> + <DialogSelection /> + </SelectionState> + </selectionStates> + </component> +</project> \ No newline at end of file diff --git a/app/src/main/java/com/example/myapplication/PhotoPage.java b/app/src/main/java/com/example/myapplication/PhotoPage.java index bd727d5..7dea134 100644 --- a/app/src/main/java/com/example/myapplication/PhotoPage.java +++ b/app/src/main/java/com/example/myapplication/PhotoPage.java @@ -25,10 +25,13 @@ import android.widget.Button; import android.widget.FrameLayout; import android.widget.ImageView; import android.widget.Spinner; +import android.widget.TableLayout; +import android.widget.TableRow; import android.widget.TextView; import android.widget.Toast; import java.io.IOException; +import java.util.Map; public class PhotoPage extends Fragment { private static final int CAMERA_REQUEST_CODE = 3; @@ -41,7 +44,10 @@ public class PhotoPage extends Fragment { TextView textPredictionOutput, textProbabilityOutput; ImageView imageView; FrameLayout formLayout; + FrameLayout tableResultsLayout; OutputPredictedImage outputPredictedImage; + private Map<String, RecognizeSpecie> predictionResults; + TableLayout tablePredictionResults; int imageSize = 256; @@ -55,8 +61,9 @@ public class PhotoPage extends Fragment { cameraButton = view.findViewById(R.id.camera_button); folderButton = view.findViewById(R.id.folder_button); identifiedSpeciesButton = view.findViewById(R.id.identified_species_button); - textPredictionOutput = view.findViewById(R.id.textPredictionOutput); - //textProbabilityOutput = view.findViewById(R.id.textProbabilityOutput); + tablePredictionResults = view.findViewById(R.id.results_table1); + tableResultsLayout = view.findViewById(R.id.tableResultsLayout); + tableResultsLayout.setVisibility(View.INVISIBLE); imageView = view.findViewById(R.id.main_image_display); Button seeTutoButton = view.findViewById(R.id.see_tuto); @@ -85,6 +92,7 @@ public class PhotoPage extends Fragment { } }); + /* identifiedSpeciesButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { @@ -101,7 +109,7 @@ public class PhotoPage extends Fragment { } } - }); + });*/ View.OnClickListener lSeeTutoButton = new View.OnClickListener() { @Override public void onClick(View view) { @@ -165,6 +173,8 @@ public class PhotoPage extends Fragment { // You can pass this `scaledBitmap` to your prediction program Log.d("PhotoPage", "Bitmap processed for prediction: " + scaledBitmap.toString()); + + try { TestModeleTflite testModeleTflite = new TestModeleTflite(this.getContext()); outputPredictedImage = testModeleTflite.recognizeSpeciesClass(bitmap); @@ -173,18 +183,76 @@ public class PhotoPage extends Fragment { double width = 1.5 * annotatedImage.getWidth(); scaledBitmap = Bitmap.createScaledBitmap(annotatedImage, (int) width, (int) height, true); imageView.setImageBitmap(scaledBitmap); - //TextView textView = getView().findViewById(R.id.textView2); predictionOutput=outputPredictedImage.toString(); - textPredictionOutput.setText(outputPredictedImage.toString()); + predictionResults = outputPredictedImage.getPredictionResults(); + tableResultsLayout.setVisibility(View.VISIBLE); + populateTable(); } catch (IOException e) { throw new RuntimeException(e); } catch (NoSpeciesRecognizedException e) { - Toast.makeText(getContext(), "L'application n'a pas reconnu d'espèce connue sur votre photo", Toast.LENGTH_SHORT).show(); + tableResultsLayout.setVisibility(View.INVISIBLE); + Toast.makeText(getContext(), "L'application n'a pas reconnu d'espèce connue sur votre photo.", Toast.LENGTH_LONG).show(); predictionOutput="nothing"; - textPredictionOutput.setText("Cette espèce n'est pas encore répertoriée."); + //textPredictionOutput.setText("Cette espèce n'est pas encore répertoriée."); + } + } + + /** + * Adds a header row to the table layout. + */ + private void addHeaderRow() { + TableRow headerRow = new TableRow(getContext()); + headerRow.setBackgroundColor(getResources().getColor(R.color.myBlue)); // Optional: Add header styling + addTableCell(headerRow, "Espèce"); + addTableCell(headerRow, "Total individus"); + addTableCell(headerRow, "Précision(%)"); + tablePredictionResults.addView(headerRow); + } + + /** + * Populates the table with rows based on the prediction results. + */ + private void populateTable() { + if (predictionResults != null) { + // Clear the table to remove any previous rows + tablePredictionResults.removeAllViews(); + + // Add the header row + addHeaderRow(); + + for (Map.Entry<String, RecognizeSpecie> entry : predictionResults.entrySet()) { + TableRow dataRow = new TableRow(getContext()); + + RecognizeSpecie specie = entry.getValue(); + + addTableCell(dataRow, entry.getKey()); // Species name + addTableCell(dataRow, String.valueOf(specie.getSpecieBoxes().size())); // Count + addTableCell(dataRow, String.format("%.2f", specie.getAverageProbability())); // Average probability + + tablePredictionResults.addView(dataRow); + } + } else { + // If no data exists, clear and add a message row + tablePredictionResults.removeAllViews(); // Clear existing rows + TableRow emptyRow = new TableRow(getContext()); + addTableCell(emptyRow, "No data available"); + tablePredictionResults.addView(emptyRow); } } + + /** + * Adds a cell to a given table row. + */ + private void addTableCell(TableRow row, String text) { + TextView textView = new TextView(getContext()); + textView.setText(text); + textView.setPadding(16, 16, 16, 16); + + textView.setTextColor(getResources().getColor(R.color.white)); // Optional: Add text styling + row.addView(textView); + } + @RequiresApi(api = Build.VERSION_CODES.M) @Override public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { diff --git a/app/src/main/java/com/example/myapplication/ResultsFragment.java b/app/src/main/java/com/example/myapplication/ResultsFragment.java index 866329c..6822333 100644 --- a/app/src/main/java/com/example/myapplication/ResultsFragment.java +++ b/app/src/main/java/com/example/myapplication/ResultsFragment.java @@ -4,6 +4,7 @@ import android.os.Bundle; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; +import android.widget.Button; import android.widget.TableLayout; import android.widget.TableRow; import android.widget.TextView; @@ -17,6 +18,7 @@ import java.util.Map; public class ResultsFragment extends Fragment { private Map<String, RecognizeSpecie> predictionResults; + private Button backToPredictionButton; // Constructor to receive prediction results public ResultsFragment(Map<String, RecognizeSpecie> predictionResults) { @@ -29,6 +31,7 @@ public class ResultsFragment extends Fragment { // Inflate the layout for this fragment View view = inflater.inflate(R.layout.fragment_prediction_results, container, false); + backToPredictionButton = view.findViewById(R.id.backToPrediction); // Reference the TableLayout TableLayout tableLayout = view.findViewById(R.id.results_table); @@ -38,7 +41,19 @@ public class ResultsFragment extends Fragment { // Populate the table with prediction results populateTable(tableLayout); + // Set up back button functionality + backToPredictionButton.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + requireActivity() + .getSupportFragmentManager() + .popBackStack(); // Navigate back to the previous fragment + } + }); + return view; + + } /** @@ -46,11 +61,12 @@ public class ResultsFragment extends Fragment { */ private void addHeaderRow(TableLayout tableLayout) { TableRow headerRow = new TableRow(getContext()); - headerRow.setBackgroundColor(getResources().getColor(R.color.logo2_background)); // Optional: Add header styling + headerRow.setBackgroundColor(getResources().getColor(R.color.myBlue)); // Optional: Add header styling addTableCell(headerRow, "Espèce"); addTableCell(headerRow, "Total individus"); addTableCell(headerRow, "Précision(%)"); tableLayout.addView(headerRow); + } /** diff --git a/app/src/main/res/layout/fragment_photo_page.xml b/app/src/main/res/layout/fragment_photo_page.xml index a6148b9..903b689 100644 --- a/app/src/main/res/layout/fragment_photo_page.xml +++ b/app/src/main/res/layout/fragment_photo_page.xml @@ -73,7 +73,7 @@ android:layout_marginTop="20dp" android:fontFamily="@font/inter" android:paddingHorizontal="40dp" - android:text="Voir espèce(s) identifiée(s)" + android:text="Espèce(s) identifiée(s)" android:textSize="15sp" /> </FrameLayout> @@ -85,43 +85,46 @@ <TextView - android:id="@+id/textPredictionOutput" - android:layout_width="351dp" - android:layout_height="28dp" - android:layout_gravity="bottom|center_horizontal" - android:layout_marginBottom="146dp" - android:text="" - android:textColor="#F12B2B" - android:textSize="17sp" - android:textStyle="bold" /> - - - <TextView - android:id="@+id/prediction_label" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_gravity="center_horizontal|bottom" - android:layout_marginBottom="186dp" - android:text="PREDICTION:" - android:textColor="#D9D9D9" - android:textSize="24sp" - android:textStyle="bold" /> - - <TextView - style="@style/PageTitleAppearance" android:id="@+id/learning_page_title" + style="@style/PageTitleAppearance" android:layout_marginTop="50dp" android:layout_marginBottom="60dp" android:paddingTop="30dp" android:paddingEnd="50dp" android:text="@string/photo" /> - <EditText - android:id="@+id/editTextTextMultiLine4" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:ems="10" - android:gravity="start|top" - android:inputType="textMultiLine" /> + + <FrameLayout + style="@style/TopTopAppearance" + android:id="@+id/tableResultsLayout" + android:layout_width="362dp" + android:layout_height="90dp" + android:layout_gravity="center_horizontal|bottom" + android:layout_marginBottom="110dp"> + + <ScrollView + android:layout_width="match_parent" + android:layout_height="match_parent"> + + <LinearLayout + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:orientation="vertical" + android:padding="7dp"> + + <TableLayout + android:id="@+id/results_table1" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:stretchColumns="1"> + <!-- Rows will be added dynamically --> + </TableLayout> + + </LinearLayout> + </ScrollView> + + </FrameLayout> + + </FrameLayout> diff --git a/app/src/main/res/layout/fragment_prediction_results.xml b/app/src/main/res/layout/fragment_prediction_results.xml index ac35b19..ad2ed74 100644 --- a/app/src/main/res/layout/fragment_prediction_results.xml +++ b/app/src/main/res/layout/fragment_prediction_results.xml @@ -10,7 +10,7 @@ <FrameLayout style="@style/TopTopAppearance" android:layout_width="362dp" - android:layout_height="93dp" + android:layout_height="120dp" android:layout_gravity="center"> <ScrollView @@ -37,6 +37,30 @@ </FrameLayout> + <TextView + android:id="@+id/prediction_label" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_gravity="center_horizontal|top" + android:layout_marginTop="320dp" + android:text="Détails de la Prédiction:" + android:textColor="#D9D9D9" + android:textSize="30sp" + android:textStyle="bold" /> + + <Button + android:id="@+id/backToPrediction" + style="@style/ButtonAppearance" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_gravity="bottom|end" + android:layout_marginBottom="300dp" + android:fontFamily="@font/inter" + android:text="Retour" + android:textSize="20sp" /> + + + diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml index c8524cd..edd55ab 100644 --- a/app/src/main/res/values/colors.xml +++ b/app/src/main/res/values/colors.xml @@ -2,4 +2,5 @@ <resources> <color name="black">#FF000000</color> <color name="white">#FFFFFFFF</color> + <color name="myBlue">#517293</color> </resources> \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 1272c73..8f813d0 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -82,7 +82,7 @@ <string name="mode_sombre_texte">Le mode sombre économise votre batterie et lutte contre la fatigue visuelle</string> <string name="download_text">Télécharge un guide d’utilisation approfondi de l’application au format pdf</string> <string name="photo_time_text">Confirmez l\'heure de la prise de la photo</string> - <string name="locate_text">Localisation (qu\'est ce que vous voyez?)</string> + <string name="locate_text">Localisation (qu\'est ce que vous voyez autour de vous? ex: un arbre, un rocher, autre?)</string> <string name="send">Envoyer</string> <string name="desc_text">Que voyez-vous autour de vous ?</string> <string name="zone_info_text">Plus d\'info sur les zones</string> -- GitLab From b001537103af607630dc28b69d005487064fbd67 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=80=9Ckamon?= <“kamon.sourabie@imt-atlantique.net”> Date: Tue, 7 Jan 2025 01:22:02 +0100 Subject: [PATCH 3/4] amelioration de l'affichage des cadres et noms des especes avec un leger bug dans le cas d'especes non reconnues --- .idea/compiler.xml | 2 +- .idea/deploymentTargetSelector.xml | 4 +- .idea/misc.xml | 3 +- .../example/myapplication/MainActivity.java | 39 --- .../myapplication/TestModeleTflite.java | 225 ++++++++++-------- 5 files changed, 132 insertions(+), 141 deletions(-) diff --git a/.idea/compiler.xml b/.idea/compiler.xml index b86273d..b589d56 100644 --- a/.idea/compiler.xml +++ b/.idea/compiler.xml @@ -1,6 +1,6 @@ <?xml version="1.0" encoding="UTF-8"?> <project version="4"> <component name="CompilerConfiguration"> - <bytecodeTargetLevel target="21" /> + <bytecodeTargetLevel target="17" /> </component> </project> \ No newline at end of file diff --git a/.idea/deploymentTargetSelector.xml b/.idea/deploymentTargetSelector.xml index bcc86d0..7762630 100644 --- a/.idea/deploymentTargetSelector.xml +++ b/.idea/deploymentTargetSelector.xml @@ -4,10 +4,10 @@ <selectionStates> <SelectionState runConfigName="app"> <option name="selectionMode" value="DROPDOWN" /> - <DropdownSelection timestamp="2025-01-04T02:38:58.283128700Z"> + <DropdownSelection timestamp="2025-01-07T00:07:51.508741600Z"> <Target type="DEFAULT_BOOT"> <handle> - <DeviceId pluginId="PhysicalDevice" identifier="serial=R39M30F5B2T" /> + <DeviceId pluginId="LocalEmulator" identifier="path=C:\Users\Prosp\.android\avd\Pixel_8a_API_35.avd" /> </handle> </Target> </DropdownSelection> diff --git a/.idea/misc.xml b/.idea/misc.xml index b2c751a..0ad17cb 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -1,6 +1,7 @@ +<?xml version="1.0" encoding="UTF-8"?> <project version="4"> <component name="ExternalStorageConfigurationManager" enabled="true" /> - <component name="ProjectRootManager" version="2" languageLevel="JDK_21" default="true" project-jdk-name="jbr-21" project-jdk-type="JavaSDK"> + <component name="ProjectRootManager" version="2" languageLevel="JDK_17" default="true" project-jdk-name="jbr-17" project-jdk-type="JavaSDK"> <output url="file://$PROJECT_DIR$/build/classes" /> </component> <component name="ProjectType"> diff --git a/app/src/main/java/com/example/myapplication/MainActivity.java b/app/src/main/java/com/example/myapplication/MainActivity.java index 12f9d38..2393421 100644 --- a/app/src/main/java/com/example/myapplication/MainActivity.java +++ b/app/src/main/java/com/example/myapplication/MainActivity.java @@ -30,7 +30,6 @@ public class MainActivity extends AppCompatActivity { setContentView(R.layout.activity_main); - int delayMillis = 3000; new Handler().postDelayed(new Runnable() { @@ -46,44 +45,6 @@ public class MainActivity extends AppCompatActivity { } - public void testPredict(View v){ - TextView textView = findViewById(R.id.testInference); - TestModeleTflite testModeleTflite = null; - try { - testModeleTflite = new TestModeleTflite(this); - String currentDir = System.getProperty("user.dir"); - String imagePath = "mouette_rieuse.jpg"; - Bitmap inputImage = TestModeleTflite.loadImage(imagePath, this); - - if (inputImage == null) { - System.out.println("Failed to load the image. Check the file path."); - return; - } - - // Perform prediction - TensorBuffer outputBuffer = testModeleTflite.predict(inputImage); - - // Process the output to get bounding boxes - List<BoundingBox> boxes = testModeleTflite.processOutput(outputBuffer); - - if (boxes == null || boxes.isEmpty()) { - textView.setText("No objects detected in the image."); - } else { - // Output results to the console - for (BoundingBox box : boxes) { - System.out.println("Class: " + box.clsName); - System.out.println("Confidence: " + box.cnf); - System.out.println("----------------------------"); - textView.setText("Class: " + box.clsName + " Confidence : " + box.cnf); - } - - } - } catch (IOException e) { - throw new RuntimeException(e); - } - - //textView.setText("bonjour"); - } } diff --git a/app/src/main/java/com/example/myapplication/TestModeleTflite.java b/app/src/main/java/com/example/myapplication/TestModeleTflite.java index 1cd882e..6b10aca 100644 --- a/app/src/main/java/com/example/myapplication/TestModeleTflite.java +++ b/app/src/main/java/com/example/myapplication/TestModeleTflite.java @@ -4,7 +4,6 @@ import static android.graphics.Paint.*; import android.content.Context; import android.graphics.Bitmap; -import android.graphics.BitmapFactory; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Paint; @@ -20,8 +19,6 @@ import org.tensorflow.lite.support.image.TensorImage; import org.tensorflow.lite.support.tensorbuffer.TensorBuffer; import java.io.BufferedReader; -import java.io.File; -import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; @@ -31,6 +28,10 @@ import java.util.HashMap; import java.util.List; import java.util.Map; +/** + * A class for performing species recognition using a TensorFlow Lite model. + * Handles model loading, inference, output processing, and annotation of input images. + */ public class TestModeleTflite { private static final String MODEL_PATH = "model_float32.tflite"; private static final String LABEL_PATH = "labels.txt"; @@ -50,16 +51,22 @@ public class TestModeleTflite { private List<String> labels = new ArrayList<>(); private ImageProcessor imageProcessor; + /** + * Initializes the TensorFlow Lite model interpreter and loads associated resources. + * + * @param context The application context for accessing assets. + * @throws IOException If the model or label files cannot be loaded. + */ public TestModeleTflite(Context context) throws IOException { - // Load the model + // Load the TensorFlow Lite model MappedByteBuffer model = FileUtil.loadMappedFile(context, MODEL_PATH); - // Set interpreter options + // Configure the interpreter with multithreading options Interpreter.Options options = new Interpreter.Options(); options.setNumThreads(4); interpreter = new Interpreter(model, options); - // Get tensor shapes + // Extract tensor shapes from the model int[] inputShape = interpreter.getInputTensor(0).shape(); int[] outputShape = interpreter.getOutputTensor(0).shape(); @@ -68,7 +75,7 @@ public class TestModeleTflite { numChannel = outputShape[1]; numElements = outputShape[2]; - // Load labels + // Load the label list try (InputStream inputStream = context.getAssets().open(LABEL_PATH); BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream))) { String line; @@ -77,54 +84,95 @@ public class TestModeleTflite { } } - // Initialize image processor + // Configure preprocessing steps for the input image imageProcessor = new ImageProcessor.Builder() .add(new NormalizeOp(INPUT_MEAN, INPUT_STANDARD_DEVIATION)) .add(new CastOp(INPUT_IMAGE_TYPE)) .build(); } + /** + * Prepares an input image and performs inference using the TensorFlow Lite model. + * + * @param bitmap The input image to analyze. + * @return A TensorBuffer containing the model's raw output. + */ public TensorBuffer predict(Bitmap bitmap) { - // Resize bitmap to model's input size Bitmap resizedBitmap = Bitmap.createScaledBitmap(bitmap, tensorWidth, tensorHeight, false); - // Preprocess input image TensorImage tensorImage = new TensorImage(DataType.FLOAT32); tensorImage.load(resizedBitmap); TensorImage processedImage = imageProcessor.process(tensorImage); - // Prepare output buffer TensorBuffer outputBuffer = TensorBuffer.createFixedSize( new int[]{1, numChannel, numElements}, OUTPUT_IMAGE_TYPE ); - // Run inference interpreter.run(processedImage.getBuffer(), outputBuffer.getBuffer()); - return outputBuffer; } - public List<String> getLabels() { - return labels; - } - - public void close() { - if (interpreter != null) { - interpreter.close(); - interpreter = null; - } - } - + /** + * Processes the raw model output to generate bounding boxes for detected objects. + * + * @param outputBuffer The TensorBuffer containing raw model output. + * @return A list of bounding boxes for detected objects. + */ public List<BoundingBox> processOutput(TensorBuffer outputBuffer) { float[] outputArray = outputBuffer.getFloatArray(); return bestBox(outputArray); } + /** + * Annotates an input image with bounding boxes for detected objects. + * + * @param inputImage The original image to annotate. + * @param boxes The bounding boxes of detected objects. + * @return A new image with bounding boxes drawn. + */ public Bitmap annotateImage(Bitmap inputImage, List<BoundingBox> boxes) { return drawBoundingBoxes(inputImage, boxes); } + /** + * Groups detections by class and constructs a structured output image and metadata. + * + * @param inputImage The input image for recognition. + * @return An OutputPredictedImage containing the annotated image and prediction results. + * @throws NoSpeciesRecognizedException If no objects are detected. + */ + public OutputPredictedImage recognizeSpeciesClass(Bitmap inputImage) throws NoSpeciesRecognizedException { + Map<String, RecognizeSpecie> predictionResults = new HashMap<>(); + TensorBuffer outputBuffer = predict(inputImage); + List<BoundingBox> boxes = processOutput(outputBuffer); + + if (boxes != null) { + System.out.println("not empty"); + for (BoundingBox box : boxes) { + predictionResults.computeIfAbsent(box.clsName, k -> new RecognizeSpecie(k, new ArrayList<>())) + .getSpecieBoxes().add(box); + } + Bitmap annotatedImage = annotateImage(inputImage, boxes); + return new OutputPredictedImage(annotatedImage, predictionResults); + } else { + throw new NoSpeciesRecognizedException(); + } + } + + /** + * Releases the resources held by the TensorFlow Lite interpreter. + */ + public void close() { + if (interpreter != null) { + interpreter.close(); + interpreter = null; + } + } + + /** + * Filters bounding boxes based on confidence and non-maximum suppression (NMS). + */ private List<BoundingBox> bestBox(float[] array) { List<BoundingBox> boundingBoxes = new ArrayList<>(); @@ -161,26 +209,29 @@ public class TestModeleTflite { } } - if (boundingBoxes.isEmpty()) return null; - return applyNMS(boundingBoxes); } + /** + * Applies non-maximum suppression (NMS) to eliminate overlapping boxes. + */ private List<BoundingBox> applyNMS(List<BoundingBox> boxes) { List<BoundingBox> sortedBoxes = new ArrayList<>(boxes); - sortedBoxes.sort((b1, b2) -> Float.compare(b2.cnf, b1.cnf)); // Sort by confidence descending + sortedBoxes.sort((b1, b2) -> Float.compare(b2.cnf, b1.cnf)); List<BoundingBox> selectedBoxes = new ArrayList<>(); while (!sortedBoxes.isEmpty()) { BoundingBox first = sortedBoxes.remove(0); selectedBoxes.add(first); - sortedBoxes.removeIf(nextBox -> calculateIoU(first, nextBox) >= IOU_THRESHOLD); } return selectedBoxes; } + /** + * Calculates the Intersection over Union (IoU) between two bounding boxes. + */ private float calculateIoU(BoundingBox box1, BoundingBox box2) { float x1 = Math.max(box1.x1, box2.x1); float y1 = Math.max(box1.y1, box2.y1); @@ -194,8 +245,10 @@ public class TestModeleTflite { return intersectionArea / (box1Area + box2Area - intersectionArea); } - - + /** + * Draws bounding boxes on an image. + */ + /* private Bitmap drawBoundingBoxes(Bitmap bitmap, List<BoundingBox> boxes) { Bitmap mutableBitmap = bitmap.copy(Bitmap.Config.ARGB_8888, true); Canvas canvas = new Canvas(mutableBitmap); @@ -204,7 +257,7 @@ public class TestModeleTflite { paint.setColor(Color.RED); paint.setStyle(Style.STROKE); paint.setStrokeWidth(8f); - + /* Paint textPaint = new Paint(); textPaint.setColor(Color.WHITE); textPaint.setTextSize(40f); @@ -218,89 +271,65 @@ public class TestModeleTflite { box.y2 * mutableBitmap.getHeight() ); canvas.drawRect(rect, paint); - canvas.drawText(box.clsName, rect.left, rect.bottom, textPaint); - //canvas.drawText(new StringBuilder().append("prob : ").append(String.format("%.2f", box.cnf)).toString(), rect.left, rect.top, textPaint); + //canvas.drawText(box.clsName, rect.left, rect.bottom, textPaint); } return mutableBitmap; - } + }*/ - /** - * Loads an image from the specified file path and converts it to a Bitmap. - * - * @param fileName Path to the image file. - * @return Bitmap representation of the image or null if an error occurs. - */ - public static Bitmap loadImage(String fileName, Context context) { - try (InputStream inputStream = context.getAssets().open(fileName)) { - return BitmapFactory.decodeStream(inputStream); - } catch (IOException e) { - e.printStackTrace(); - return null; - } - } + private Bitmap drawBoundingBoxes(Bitmap bitmap, List<BoundingBox> boxes) { + Bitmap mutableBitmap = bitmap.copy(Bitmap.Config.ARGB_8888, true); + Canvas canvas = new Canvas(mutableBitmap); - public Bitmap recognizeSpecies(Bitmap inputImage){ - TensorBuffer outputBuffer = predict(inputImage); - List<BoundingBox> boxes = processOutput(outputBuffer); - return annotateImage(inputImage, boxes); - } + // Scale stroke width dynamically based on image resolution + float strokeWidth = Math.max(mutableBitmap.getWidth(), mutableBitmap.getHeight()) / 470.0f; - public OutputPredictedImage recognizeSpeciesClass(Bitmap inputImage) throws NoSpeciesRecognizedException { - //ArrayList<String> speciesNames = new ArrayList<>(); - List<RecognizeSpecie> specieBoxes= new ArrayList<RecognizeSpecie>(); - Map<String, RecognizeSpecie> predictionResults = new HashMap<>(); + Paint paint = new Paint(); + paint.setColor(Color.RED); + paint.setStyle(Style.STROKE); + paint.setStrokeWidth(strokeWidth); // Dynamically set stroke width - TensorBuffer outputBuffer = predict(inputImage); - List<BoundingBox> boxes = processOutput(outputBuffer); - if(boxes != null){ - for(BoundingBox box:boxes){ - if(predictionResults.containsKey(box.clsName)){ - predictionResults.get(box.clsName).getSpecieBoxes().add(box); - }else{ - predictionResults.put(box.clsName, new RecognizeSpecie(box.clsName, new ArrayList<BoundingBox>())); - predictionResults.get(box.clsName).getSpecieBoxes().add(box); - } + Paint textPaint = new Paint(); + textPaint.setColor(Color.WHITE); + textPaint.setStyle(Style.FILL); - } - Bitmap annotatedImage = annotateImage(inputImage, boxes); - return new OutputPredictedImage(annotatedImage, predictionResults); - }else throw new NoSpeciesRecognizedException(); - } + for (BoundingBox box : boxes) { + RectF rect = new RectF( + box.x1 * mutableBitmap.getWidth(), + box.y1 * mutableBitmap.getHeight(), + box.x2 * mutableBitmap.getWidth(), + box.y2 * mutableBitmap.getHeight() + ); + // Draw the bounding box + canvas.drawRect(rect, paint); - public static void main(String [] args){ - /* - TestModeleTflite testModeleTflite = new TestModeleTflite(this); - String imagePath = "../../res/drawable/bernache_cravant.jpg"; - Bitmap inputImage = TestModeleTflite.loadImage(imagePath); + // Dynamically adjust text size to fit within the box + float boxWidth = rect.width(); + float boxHeight = rect.height(); + float maxTextSize = boxHeight * 0.2f; // Limit text size to a fraction of the box height + textPaint.setTextSize(maxTextSize); - if (inputImage == null) { - System.out.println("Failed to load the image. Check the file path."); - return; + // Measure text width and adjust if necessary + float textWidth = textPaint.measureText(box.clsName); + if (textWidth > boxWidth) { + textPaint.setTextSize(maxTextSize * (boxWidth / textWidth)); + } + + // Draw the text at the bottom of the box + canvas.drawText(box.clsName, rect.left, rect.bottom - 5, textPaint); // Adjust -5 for padding } - // Perform prediction - TensorBuffer outputBuffer = testModeleTflite.predict(inputImage); + return mutableBitmap; + } - // Process the output to get bounding boxes - List<BoundingBox> boxes = testModeleTflite.processOutput(outputBuffer); - if (boxes == null || boxes.isEmpty()) { - System.out.println("No objects detected in the image."); - } else { - // Output results to the console - for (BoundingBox box : boxes) { - System.out.println("Class: " + box.clsName); - System.out.println("Confidence: " + box.cnf); - System.out.println("----------------------------"); - } - }*/ + + /** + * Main method for debugging or testing purposes. + */ + public static void main(String[] args) { String currentDir = System.getProperty("user.dir"); System.out.println("Current working directory: " + currentDir); } - - - } - -- GitLab From 5a9a52654d0b055e28cdd70116a148af59dee0aa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=80=9Ckamon?= <“kamon.sourabie@imt-atlantique.net”> Date: Tue, 7 Jan 2025 03:09:54 +0100 Subject: [PATCH 4/4] bug sur les especes non reconnues corrige, organisation modulaire du code et commentaire javadoc ajoute. Manque d'authentification pour les stats --- .idea/deploymentTargetSelector.xml | 2 +- app/src/main/AndroidManifest.xml | 2 +- .../example/myapplication/HomeActivity.java | 90 +++++++++----- .../example/myapplication/LearningPage.java | 113 ++++++++++++------ .../example/myapplication/MainActivity.java | 44 ++++--- .../com/example/myapplication/PhotoPage.java | 95 ++++++++++----- .../example/myapplication/PublicActivity.java | 54 ++++++--- .../myapplication/ResultsFragment.java | 107 ----------------- .../OverlayDialogFragmentBecasseau.java | 4 +- .../OverlayDialogFragmentBernache.java | 4 +- .../OverlayDialogFragmentChevalier.java | 4 +- .../OverlayDialogFragmentCormoran.java | 4 +- .../OverlayDialogFragmentForm.java | 9 +- .../OverlayDialogFragmentFoulque.java | 4 +- .../OverlayDialogFragmentGoeland.java | 4 +- .../OverlayDialogFragmentMouette.java | 4 +- .../OverlayDialogFragmentPluvier.java | 4 +- .../OverlayDialogFragmentTadorne.java | 4 +- .../OverlayDialogFragmentZonage.java | 4 +- .../{ => recognation_logic}/BoundingBox.java | 2 +- .../NoSpeciesRecognizedException.java | 2 +- .../OutputPredictedImage.java | 4 +- .../RecognizeSpecie.java | 2 +- .../TestModeleTflite.java | 47 ++------ .../{ => tutorials}/TutorialActivity.java | 5 +- .../TutorialFragmentAdapter.java | 2 +- .../{ => tutorials}/TutorialPageFragment.java | 3 +- app/src/main/res/layout/activity_tutorial.xml | 2 +- 28 files changed, 326 insertions(+), 299 deletions(-) delete mode 100644 app/src/main/java/com/example/myapplication/ResultsFragment.java rename app/src/main/java/com/example/myapplication/{ => overlays}/OverlayDialogFragmentBecasseau.java (94%) rename app/src/main/java/com/example/myapplication/{ => overlays}/OverlayDialogFragmentBernache.java (94%) rename app/src/main/java/com/example/myapplication/{ => overlays}/OverlayDialogFragmentChevalier.java (94%) rename app/src/main/java/com/example/myapplication/{ => overlays}/OverlayDialogFragmentCormoran.java (94%) rename app/src/main/java/com/example/myapplication/{ => overlays}/OverlayDialogFragmentForm.java (98%) rename app/src/main/java/com/example/myapplication/{ => overlays}/OverlayDialogFragmentFoulque.java (94%) rename app/src/main/java/com/example/myapplication/{ => overlays}/OverlayDialogFragmentGoeland.java (94%) rename app/src/main/java/com/example/myapplication/{ => overlays}/OverlayDialogFragmentMouette.java (94%) rename app/src/main/java/com/example/myapplication/{ => overlays}/OverlayDialogFragmentPluvier.java (94%) rename app/src/main/java/com/example/myapplication/{ => overlays}/OverlayDialogFragmentTadorne.java (94%) rename app/src/main/java/com/example/myapplication/{ => overlays}/OverlayDialogFragmentZonage.java (94%) rename app/src/main/java/com/example/myapplication/{ => recognation_logic}/BoundingBox.java (92%) rename app/src/main/java/com/example/myapplication/{ => recognation_logic}/NoSpeciesRecognizedException.java (55%) rename app/src/main/java/com/example/myapplication/{ => recognation_logic}/OutputPredictedImage.java (92%) rename app/src/main/java/com/example/myapplication/{ => recognation_logic}/RecognizeSpecie.java (95%) rename app/src/main/java/com/example/myapplication/{ => recognation_logic}/TestModeleTflite.java (89%) rename app/src/main/java/com/example/myapplication/{ => tutorials}/TutorialActivity.java (95%) rename app/src/main/java/com/example/myapplication/{ => tutorials}/TutorialFragmentAdapter.java (94%) rename app/src/main/java/com/example/myapplication/{ => tutorials}/TutorialPageFragment.java (93%) diff --git a/.idea/deploymentTargetSelector.xml b/.idea/deploymentTargetSelector.xml index 7762630..2b4faaa 100644 --- a/.idea/deploymentTargetSelector.xml +++ b/.idea/deploymentTargetSelector.xml @@ -4,7 +4,7 @@ <selectionStates> <SelectionState runConfigName="app"> <option name="selectionMode" value="DROPDOWN" /> - <DropdownSelection timestamp="2025-01-07T00:07:51.508741600Z"> + <DropdownSelection timestamp="2025-01-07T01:25:25.467972600Z"> <Target type="DEFAULT_BOOT"> <handle> <DeviceId pluginId="LocalEmulator" identifier="path=C:\Users\Prosp\.android\avd\Pixel_8a_API_35.avd" /> diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 3cef3fd..41dbf85 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -38,7 +38,7 @@ android:name=".HomeActivity" android:screenOrientation="portrait" /> <activity - android:name=".TutorialActivity" + android:name=".tutorials.TutorialActivity" android:screenOrientation="portrait" /> <activity android:name=".PublicActivity" diff --git a/app/src/main/java/com/example/myapplication/HomeActivity.java b/app/src/main/java/com/example/myapplication/HomeActivity.java index 8bef76f..976416a 100644 --- a/app/src/main/java/com/example/myapplication/HomeActivity.java +++ b/app/src/main/java/com/example/myapplication/HomeActivity.java @@ -9,31 +9,62 @@ import android.widget.Button; import androidx.appcompat.app.AppCompatActivity; import androidx.appcompat.app.AppCompatDelegate; +import com.example.myapplication.tutorials.TutorialActivity; + +/** + * HomeActivity represents the main screen of the application where users can access + * various sections of the app, such as Learning, Stats, Settings, and Photo. The activity + * also handles displaying tutorials for first-time users or specific navigation flows. + */ public class HomeActivity extends AppCompatActivity { + + /** + * Called when the activity is resumed. It checks if a tutorial should be shown based + * on the user's previous interactions or the source of the current intent (e.g., from the photo page). + * If the tutorial is needed, it is triggered. + */ @Override protected void onResume() { super.onResume(); - // If tutorial ran from photo page + // If tutorial ran from photo page, show the tutorial boolean fromPhotoPage = getIntent().getBooleanExtra("FROM_PHOTO_PAGE", false); if (fromPhotoPage) runTutorial(true); String tutorialKey = "FIRST_TIME_TUTORIAL"; - boolean preferences = getIntent().getBooleanExtra("PREFERENCES",true); + boolean preferences = getIntent().getBooleanExtra("PREFERENCES", true); + + // If preferences is false, skip the tutorial on the first time if (!preferences) getPreferences(MODE_PRIVATE).edit().putBoolean(tutorialKey, false).apply(); + boolean firstTime = getPreferences(MODE_PRIVATE).getBoolean(tutorialKey, true); + + // Run the tutorial if it's the first time using the app if (firstTime) { runTutorial(false); } } + /** + * Starts the TutorialActivity to display a tutorial. + * It checks if the tutorial is to be shown due to navigating from the photo page. + * + * @param fromPhotoPage A boolean that specifies whether the tutorial is shown due to navigation from the photo page. + */ private void runTutorial(boolean fromPhotoPage) { - Intent intent = new Intent(HomeActivity.this, TutorialActivity.class); - intent.putExtra("FROM_PHOTO_PAGE", fromPhotoPage); - startActivity(intent); - finish(); + Intent intent = new Intent(HomeActivity.this, TutorialActivity.class); + intent.putExtra("FROM_PHOTO_PAGE", fromPhotoPage); + startActivity(intent); + finish(); } + /** + * Called when the activity is created. This method sets up the theme based on user preferences, + * initializes the UI, and sets click listeners for each button to navigate to different pages. + * + * @param savedInstanceState A bundle containing the saved state of the activity, + * or null if there was no previously saved state. + */ @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); @@ -45,12 +76,10 @@ public class HomeActivity extends AppCompatActivity { setContentView(R.layout.activity_home); - /* - On click listener for the Learning Button to open the "Apprendre" page by clicking - on the "Apprendre" button on the home page - */ - Button learningButton=(Button)findViewById(R.id.learning_button); - View.OnClickListener listenerLearningButton=new View.OnClickListener() { + // On click listener for the Learning Button to open the "Apprendre" page by clicking + // on the "Apprendre" button on the home page + Button learningButton = (Button) findViewById(R.id.learning_button); + View.OnClickListener listenerLearningButton = new View.OnClickListener() { @Override public void onClick(View view) { goToPage("LearningPage"); @@ -58,12 +87,10 @@ public class HomeActivity extends AppCompatActivity { }; learningButton.setOnClickListener(listenerLearningButton); - /* - On click listener for the Stats Button to open the "Statistiques" page by clicking - on the "Statistiques" button on the home page - */ - Button statsButton=(Button)findViewById(R.id.stats_button); - View.OnClickListener listenerStatsButton=new View.OnClickListener() { + // On click listener for the Stats Button to open the "Statistiques" page by clicking + // on the "Statistiques" button on the home page + Button statsButton = (Button) findViewById(R.id.stats_button); + View.OnClickListener listenerStatsButton = new View.OnClickListener() { @Override public void onClick(View view) { goToPage("StatsPage"); @@ -71,12 +98,10 @@ public class HomeActivity extends AppCompatActivity { }; statsButton.setOnClickListener(listenerStatsButton); - /* - On click listener for the Settings Button to open the "Paramètres" page by clicking - on the "Paramètres" button on the home page - */ - Button settingsButton=(Button)findViewById(R.id.settings_button); - View.OnClickListener listenerSettingsButton=new View.OnClickListener() { + // On click listener for the Settings Button to open the "Paramètres" page by clicking + // on the "Paramètres" button on the home page + Button settingsButton = (Button) findViewById(R.id.settings_button); + View.OnClickListener listenerSettingsButton = new View.OnClickListener() { @Override public void onClick(View view) { goToPage("SettingsPage"); @@ -84,12 +109,10 @@ public class HomeActivity extends AppCompatActivity { }; settingsButton.setOnClickListener(listenerSettingsButton); - /* - On click listener for the Photo Button to open the "Documenter" page by clicking - on the "Documenter" button on the home page - */ - Button photoButton=(Button)findViewById(R.id.photo_button); - View.OnClickListener listenerPhotoButton=new View.OnClickListener() { + // On click listener for the Photo Button to open the "Documenter" page by clicking + // on the "Documenter" button on the home page + Button photoButton = (Button) findViewById(R.id.photo_button); + View.OnClickListener listenerPhotoButton = new View.OnClickListener() { @Override public void onClick(View view) { goToPage("PhotoPage"); @@ -97,9 +120,12 @@ public class HomeActivity extends AppCompatActivity { }; photoButton.setOnClickListener(listenerPhotoButton); } + /** - * A method to go to the learning page - * @param pageToGo a string to signify to which page we want to go + * Navigates the user to the specified page by opening the PublicActivity + * and passing the target fragment as an extra. + * + * @param pageToGo A string representing the target page (fragment) to navigate to. */ public void goToPage(String pageToGo) { Intent intent = new Intent(HomeActivity.this, PublicActivity.class); diff --git a/app/src/main/java/com/example/myapplication/LearningPage.java b/app/src/main/java/com/example/myapplication/LearningPage.java index de979da..a1f490c 100644 --- a/app/src/main/java/com/example/myapplication/LearningPage.java +++ b/app/src/main/java/com/example/myapplication/LearningPage.java @@ -11,7 +11,30 @@ import android.view.View; import android.view.ViewGroup; import android.widget.Button; +import com.example.myapplication.overlays.OverlayDialogFragmentBecasseau; +import com.example.myapplication.overlays.OverlayDialogFragmentBernache; +import com.example.myapplication.overlays.OverlayDialogFragmentChevalier; +import com.example.myapplication.overlays.OverlayDialogFragmentCormoran; +import com.example.myapplication.overlays.OverlayDialogFragmentFoulque; +import com.example.myapplication.overlays.OverlayDialogFragmentGoeland; +import com.example.myapplication.overlays.OverlayDialogFragmentMouette; +import com.example.myapplication.overlays.OverlayDialogFragmentPluvier; +import com.example.myapplication.overlays.OverlayDialogFragmentTadorne; + +/** + * This fragment represents a learning page where the user can interact with different buttons. + * Each button triggers a dialog showing detailed information about a particular bird species. + */ public class LearningPage extends Fragment { + + /** + * Inflates the layout for this fragment. + * + * @param inflater The LayoutInflater object that can be used to inflate views in the fragment. + * @param container The parent container that the fragment's UI should be attached to. + * @param savedInstanceState A bundle containing the state of the fragment. + * @return The root view of the fragment's layout. + */ @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { @@ -19,158 +42,176 @@ public class LearningPage extends Fragment { return inflater.inflate(R.layout.fragment_learning_page, container, false); } + /** + * Sets up the listeners for each button on the learning page. Each listener triggers the display + * of a corresponding overlay dialog fragment for a bird species. + * + * @param view The root view of the fragment. + * @param savedInstanceState A bundle containing the state of the fragment. + */ @Override public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { - // Listener pour le becasseau + + // Set up the listener for the Becasseau button Button buttonBecasseau = (Button) this.getView().findViewById(R.id.button_becasseau); - View.OnClickListener listenerButtonBecasseau=new View.OnClickListener() { + View.OnClickListener listenerButtonBecasseau = new View.OnClickListener() { @Override public void onClick(View view) { showOverlayBecasseau(); } }; - buttonBecasseau.setOnClickListener(listenerButtonBecasseau); - // Listener pour la bernache + // Set up the listener for the Bernache button Button buttonBernache = (Button) this.getView().findViewById(R.id.button_bernache); - - View.OnClickListener listenerButtonBernache=new View.OnClickListener() { + View.OnClickListener listenerButtonBernache = new View.OnClickListener() { @Override public void onClick(View view) { showOverlayBernache(); } }; - buttonBernache.setOnClickListener(listenerButtonBernache); - // Listener pour le goeland + // Set up the listener for the Goeland button Button buttonGoeland = (Button) this.getView().findViewById(R.id.button_goeland); - - View.OnClickListener listenerButtonGoeland=new View.OnClickListener() { + View.OnClickListener listenerButtonGoeland = new View.OnClickListener() { @Override public void onClick(View view) { showOverlayGoeland(); } }; - buttonGoeland.setOnClickListener(listenerButtonGoeland); - // Listener pour la mouette + // Set up the listener for the Mouette button Button buttonMouette = (Button) this.getView().findViewById(R.id.button_mouette); - - View.OnClickListener listenerButtonMouette=new View.OnClickListener() { + View.OnClickListener listenerButtonMouette = new View.OnClickListener() { @Override public void onClick(View view) { showOverlayMouette(); } }; - buttonMouette.setOnClickListener(listenerButtonMouette); - // Listener pour le pluvier + // Set up the listener for the Pluvier button Button buttonPluvier = (Button) this.getView().findViewById(R.id.button_pluvier); - - View.OnClickListener listenerButtonPluvier=new View.OnClickListener() { + View.OnClickListener listenerButtonPluvier = new View.OnClickListener() { @Override public void onClick(View view) { showOverlayPluvier(); } }; - buttonPluvier.setOnClickListener(listenerButtonPluvier); - // Listener pour le cormoran + // Set up the listener for the Cormoran button Button buttonCormoran = (Button) this.getView().findViewById(R.id.button_cormoran); - - View.OnClickListener listenerButtonCormoran=new View.OnClickListener() { + View.OnClickListener listenerButtonCormoran = new View.OnClickListener() { @Override public void onClick(View view) { showOverlayCormoran(); } }; - buttonCormoran.setOnClickListener(listenerButtonCormoran); - // Listener pour la foulque macroule + // Set up the listener for the Foulque button Button buttonFoulque = (Button) this.getView().findViewById(R.id.button_foulque); - - View.OnClickListener listenerButtonFoulque=new View.OnClickListener() { + View.OnClickListener listenerButtonFoulque = new View.OnClickListener() { @Override public void onClick(View view) { showOverlayFoulque(); } }; - buttonFoulque.setOnClickListener(listenerButtonFoulque); - // Listener pour le tadorne de Belon + // Set up the listener for the Tadorne button Button buttonTadorne = (Button) this.getView().findViewById(R.id.button_tadorne); - - View.OnClickListener listenerButtonTadorne=new View.OnClickListener() { + View.OnClickListener listenerButtonTadorne = new View.OnClickListener() { @Override public void onClick(View view) { showOverlayTadorne(); } }; - buttonTadorne.setOnClickListener(listenerButtonTadorne); - // Listener pour le chevalier gambette + // Set up the listener for the Chevalier button Button buttonChevalier = (Button) this.getView().findViewById(R.id.button_chevalier); - - View.OnClickListener listenerButtonChevalier=new View.OnClickListener() { + View.OnClickListener listenerButtonChevalier = new View.OnClickListener() { @Override public void onClick(View view) { showOverlayChevalier(); } }; - buttonChevalier.setOnClickListener(listenerButtonChevalier); } + /** + * Shows an overlay dialog for the Becasseau bird species. + */ public void showOverlayBecasseau() { OverlayDialogFragmentBecasseau overlayDialogBecasseau = new OverlayDialogFragmentBecasseau(); overlayDialogBecasseau.show(getParentFragmentManager(), "overlayDialogBecasseau"); } + /** + * Shows an overlay dialog for the Bernache bird species. + */ public void showOverlayBernache() { OverlayDialogFragmentBernache overlayDialogBernache = new OverlayDialogFragmentBernache(); overlayDialogBernache.show(getParentFragmentManager(), "overlayDialogBernache"); } + /** + * Shows an overlay dialog for the Goeland bird species. + */ public void showOverlayGoeland() { OverlayDialogFragmentGoeland overlayDialogGoeland = new OverlayDialogFragmentGoeland(); overlayDialogGoeland.show(getParentFragmentManager(), "overlayDialogGoeland"); } + /** + * Shows an overlay dialog for the Mouette bird species. + */ public void showOverlayMouette() { OverlayDialogFragmentMouette overlayDialogMouette = new OverlayDialogFragmentMouette(); overlayDialogMouette.show(getParentFragmentManager(), "overlayDialogMouette"); } + /** + * Shows an overlay dialog for the Pluvier bird species. + */ public void showOverlayPluvier() { OverlayDialogFragmentPluvier overlayDialogPluvier = new OverlayDialogFragmentPluvier(); overlayDialogPluvier.show(getParentFragmentManager(), "overlayDialogPluvier"); } + /** + * Shows an overlay dialog for the Cormoran bird species. + */ public void showOverlayCormoran() { OverlayDialogFragmentCormoran overlayDialogCormoran = new OverlayDialogFragmentCormoran(); overlayDialogCormoran.show(getParentFragmentManager(), "overlayDialogCormoran"); } + /** + * Shows an overlay dialog for the Foulque bird species. + */ public void showOverlayFoulque() { OverlayDialogFragmentFoulque overlayDialogFoulque = new OverlayDialogFragmentFoulque(); overlayDialogFoulque.show(getParentFragmentManager(), "overlayDialogFoulque"); } + /** + * Shows an overlay dialog for the Tadorne bird species. + */ public void showOverlayTadorne() { OverlayDialogFragmentTadorne overlayDialogTadorne = new OverlayDialogFragmentTadorne(); overlayDialogTadorne.show(getParentFragmentManager(), "overlayDialogTadorne"); } + /** + * Shows an overlay dialog for the Chevalier bird species. + */ public void showOverlayChevalier() { OverlayDialogFragmentChevalier overlayDialogChevalier = new OverlayDialogFragmentChevalier(); overlayDialogChevalier.show(getParentFragmentManager(), "overlayDialogChevalier"); } -} \ No newline at end of file +} diff --git a/app/src/main/java/com/example/myapplication/MainActivity.java b/app/src/main/java/com/example/myapplication/MainActivity.java index 2393421..3ff2cfb 100644 --- a/app/src/main/java/com/example/myapplication/MainActivity.java +++ b/app/src/main/java/com/example/myapplication/MainActivity.java @@ -2,23 +2,27 @@ package com.example.myapplication; import android.content.Intent; import android.content.SharedPreferences; -import android.graphics.Bitmap; import android.os.Bundle; import android.os.Handler; -import android.view.View; -import android.widget.Button; -import android.widget.ImageView; -import android.widget.TextView; import androidx.appcompat.app.AppCompatActivity; import androidx.appcompat.app.AppCompatDelegate; -import org.tensorflow.lite.support.tensorbuffer.TensorBuffer; - -import java.io.IOException; -import java.util.List; - +/** + * The MainActivity class represents the main entry point of the application. + * It is responsible for loading the theme preference and transitioning to the HomeActivity + * after a brief delay. + */ public class MainActivity extends AppCompatActivity { + + /** + * Called when the activity is first created. This method is used to initialize + * the app's settings, including theme preferences, and to set up a delayed transition + * to the HomeActivity. + * + * @param savedInstanceState A bundle containing the saved state of the activity, + * or null if there was no previously saved state. + */ @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); @@ -26,25 +30,29 @@ public class MainActivity extends AppCompatActivity { // Load the saved theme mode from SharedPreferences SharedPreferences preferences = getSharedPreferences("theme_prefs", MODE_PRIVATE); int themeMode = preferences.getInt("theme_mode", AppCompatDelegate.MODE_NIGHT_NO); + + // Apply the saved theme mode to the app AppCompatDelegate.setDefaultNightMode(themeMode); + // Set the content view to the layout for this activity setContentView(R.layout.activity_main); + // Delay time before transitioning to HomeActivity (in milliseconds) int delayMillis = 3000; + // Create a new handler to delay the transition to HomeActivity new Handler().postDelayed(new Runnable() { @Override public void run() { + // Create an intent to launch HomeActivity Intent intent = new Intent(MainActivity.this, HomeActivity.class); - startActivity(intent); - finish(); - } - }, delayMillis); - + // Start HomeActivity + startActivity(intent); + // Finish MainActivity so that it won't remain in the back stack + finish(); + } + }, delayMillis); // Delay the transition by the specified time } - - - } diff --git a/app/src/main/java/com/example/myapplication/PhotoPage.java b/app/src/main/java/com/example/myapplication/PhotoPage.java index 7dea134..199b9f9 100644 --- a/app/src/main/java/com/example/myapplication/PhotoPage.java +++ b/app/src/main/java/com/example/myapplication/PhotoPage.java @@ -8,7 +8,12 @@ import android.graphics.Bitmap; import android.net.Uri; import android.os.Build; import android.os.Bundle; -import com.example.myapplication.R; + +import com.example.myapplication.overlays.OverlayDialogFragmentForm; +import com.example.myapplication.recognation_logic.NoSpeciesRecognizedException; +import com.example.myapplication.recognation_logic.OutputPredictedImage; +import com.example.myapplication.recognation_logic.RecognizeSpecie; +import com.example.myapplication.recognation_logic.TestModeleTflite; import androidx.annotation.NonNull; import androidx.annotation.Nullable; @@ -24,7 +29,6 @@ import android.view.ViewGroup; import android.widget.Button; import android.widget.FrameLayout; import android.widget.ImageView; -import android.widget.Spinner; import android.widget.TableLayout; import android.widget.TableRow; import android.widget.TextView; @@ -33,6 +37,10 @@ import android.widget.Toast; import java.io.IOException; import java.util.Map; +/** + * A Fragment class responsible for capturing photos, selecting images from the gallery, + * processing them, and displaying species recognition results. + */ public class PhotoPage extends Fragment { private static final int CAMERA_REQUEST_CODE = 3; private static final int GALLERY_REQUEST_CODE = 1; @@ -49,14 +57,22 @@ public class PhotoPage extends Fragment { private Map<String, RecognizeSpecie> predictionResults; TableLayout tablePredictionResults; - int imageSize = 256; + /** + * Inflates the layout and initializes UI components for the fragment. + * + * @param inflater LayoutInflater for inflating the layout. + * @param container Parent container for the fragment. + * @param savedInstanceState Saved state of the fragment. + * @return The view for the fragment. + */ @Nullable @Override public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { predictionOutput = null; View view = inflater.inflate(R.layout.fragment_photo_page, container, false); + // Initialize UI elements cameraButton = view.findViewById(R.id.camera_button); folderButton = view.findViewById(R.id.folder_button); @@ -67,17 +83,18 @@ public class PhotoPage extends Fragment { imageView = view.findViewById(R.id.main_image_display); Button seeTutoButton = view.findViewById(R.id.see_tuto); - - // Set up button click listeners cameraButton.setOnClickListener(new View.OnClickListener() { @RequiresApi(api = Build.VERSION_CODES.M) @Override public void onClick(View v) { + // Check for camera permission and request it if necessary if (ContextCompat.checkSelfPermission(getContext(), Manifest.permission.CAMERA) == PackageManager.PERMISSION_GRANTED) { + // Start the camera activity if permission is granted Intent cameraIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE); startActivityForResult(cameraIntent, CAMERA_REQUEST_CODE); } else { + // Request camera permission if not granted requestPermissions(new String[]{Manifest.permission.CAMERA}, CAMERA_PERMISSION_REQUEST_CODE); } } @@ -86,30 +103,26 @@ public class PhotoPage extends Fragment { folderButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { + // Open the gallery to pick an image Intent folderIntent = new Intent(Intent.ACTION_PICK, MediaStore.Images.Media.EXTERNAL_CONTENT_URI); startActivityForResult(folderIntent, GALLERY_REQUEST_CODE); - } }); - /* identifiedSpeciesButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { + // Display the identified species or a message if no species is recognized if(predictionOutput == null){ Toast.makeText(getContext(), "Veuillez prendre une photo ou charger une image depuis votre gallerie.", Toast.LENGTH_LONG).show(); } - if(outputPredictedImage != null){ - ResultsFragment resultsFragment = new ResultsFragment(outputPredictedImage.getPredictionResults()); - requireActivity().getSupportFragmentManager() - .beginTransaction() - .replace(R.id.fragment_container, resultsFragment) // Assuming you have a container in your activity layout - .addToBackStack(null) - .commit(); + else { + Toast.makeText(getContext(), predictionOutput, Toast.LENGTH_LONG).show(); } - } - });*/ + }); + + // Set up tutorial button View.OnClickListener lSeeTutoButton = new View.OnClickListener() { @Override public void onClick(View view) { @@ -118,8 +131,6 @@ public class PhotoPage extends Fragment { startActivity(intent); getActivity().finish(); } - - }; seeTutoButton.setOnClickListener(lSeeTutoButton); @@ -127,10 +138,18 @@ public class PhotoPage extends Fragment { return view; } + /** + * Handles the results from the camera or gallery activities. + * + * @param requestCode Code indicating the request type (camera or gallery). + * @param resultCode Result code from the activity. + * @param data Intent containing the result data. + */ @Override public void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) { super.onActivityResult(requestCode, resultCode, data); + // Handle result based on the request code if (data == null || resultCode != getActivity().RESULT_OK) { Log.e("PhotoPage", "Error: Data is null or result not OK"); return; @@ -166,16 +185,20 @@ public class PhotoPage extends Fragment { } } + /** + * Processes the given bitmap image for species recognition. + * + * @param bitmap The image to be processed. + */ private void processImage(Bitmap bitmap) { // Resize and/or preprocess the image for your recognition program Bitmap scaledBitmap = Bitmap.createScaledBitmap(bitmap, imageSize, imageSize, true); - // You can pass this `scaledBitmap` to your prediction program + // You can pass this scaledBitmap to your prediction program Log.d("PhotoPage", "Bitmap processed for prediction: " + scaledBitmap.toString()); - - try { + // Initialize the model and get predictions TestModeleTflite testModeleTflite = new TestModeleTflite(this.getContext()); outputPredictedImage = testModeleTflite.recognizeSpeciesClass(bitmap); Bitmap annotatedImage = outputPredictedImage.getAnnotatedImage(); @@ -183,7 +206,7 @@ public class PhotoPage extends Fragment { double width = 1.5 * annotatedImage.getWidth(); scaledBitmap = Bitmap.createScaledBitmap(annotatedImage, (int) width, (int) height, true); imageView.setImageBitmap(scaledBitmap); - predictionOutput=outputPredictedImage.toString(); + predictionOutput = outputPredictedImage.toString(); predictionResults = outputPredictedImage.getPredictionResults(); tableResultsLayout.setVisibility(View.VISIBLE); populateTable(); @@ -192,8 +215,7 @@ public class PhotoPage extends Fragment { } catch (NoSpeciesRecognizedException e) { tableResultsLayout.setVisibility(View.INVISIBLE); Toast.makeText(getContext(), "L'application n'a pas reconnu d'espèce connue sur votre photo.", Toast.LENGTH_LONG).show(); - predictionOutput="nothing"; - //textPredictionOutput.setText("Cette espèce n'est pas encore répertoriée."); + predictionOutput = "nothing"; } } @@ -220,6 +242,7 @@ public class PhotoPage extends Fragment { // Add the header row addHeaderRow(); + // Add data rows for each prediction result for (Map.Entry<String, RecognizeSpecie> entry : predictionResults.entrySet()) { TableRow dataRow = new TableRow(getContext()); @@ -240,41 +263,59 @@ public class PhotoPage extends Fragment { } } - /** * Adds a cell to a given table row. + * + * @param row The table row to which the cell will be added. + * @param text The text to display in the cell. */ private void addTableCell(TableRow row, String text) { TextView textView = new TextView(getContext()); textView.setText(text); textView.setPadding(16, 16, 16, 16); - textView.setTextColor(getResources().getColor(R.color.white)); // Optional: Add text styling row.addView(textView); } + /** + * Handles the results of a permission request, such as for camera access. + * + * @param requestCode Code indicating the request type. + * @param permissions Requested permissions. + * @param grantResults Results of the permission requests. + */ @RequiresApi(api = Build.VERSION_CODES.M) @Override public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { super.onRequestPermissionsResult(requestCode, permissions, grantResults); + // Handle camera permission request results if (requestCode == CAMERA_PERMISSION_REQUEST_CODE) { if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) { + // Start the camera activity if permission is granted Intent cameraIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE); startActivityForResult(cameraIntent, CAMERA_REQUEST_CODE); } else { + // Handle the case where permission is denied Log.e("PhotoPage", "Camera permission denied"); textPredictionOutput.setText("Camera permission denied. Please allow it to use this feature."); } } - } + /** + * Displays an overlay form for additional input. + */ public void showOverlayForm() { OverlayDialogFragmentForm overlayDialogForm = new OverlayDialogFragmentForm(); overlayDialogForm.show(getParentFragmentManager(), "overlayDialogForm"); } + /** + * Retrieves the prediction output string. + * + * @return The prediction output string. + */ public static String getPredictionOutput() { return predictionOutput; } diff --git a/app/src/main/java/com/example/myapplication/PublicActivity.java b/app/src/main/java/com/example/myapplication/PublicActivity.java index fe03368..ae8653e 100644 --- a/app/src/main/java/com/example/myapplication/PublicActivity.java +++ b/app/src/main/java/com/example/myapplication/PublicActivity.java @@ -1,6 +1,5 @@ package com.example.myapplication; - import androidx.appcompat.app.AppCompatActivity; import androidx.fragment.app.Fragment; @@ -9,43 +8,60 @@ import android.os.Bundle; import com.google.android.material.bottomnavigation.BottomNavigationView; +/** + * PublicActivity represents an activity that hosts different fragments and provides navigation + * through a BottomNavigationView. It loads fragments based on the provided intent extra + * or the user's navigation choice. + */ public class PublicActivity extends AppCompatActivity { + + /** + * Called when the activity is created. This method initializes the UI, sets up the + * BottomNavigationView, and loads the correct fragment based on the "TARGET_FRAGMENT" + * provided in the intent or the user's navigation selection. + * + * @param savedInstanceState A bundle containing the saved state of the activity, + * or null if there was no previously saved state. + */ @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_public); + // Initialize the BottomNavigationView BottomNavigationView bottomNavigationView = findViewById(R.id.bottom_navigation); - // Check for which fragment to load - String targetFragment=getIntent().getStringExtra("TARGET_FRAGMENT"); + // Get the target fragment name from the intent + String targetFragment = getIntent().getStringExtra("TARGET_FRAGMENT"); + + // Assert the fragment name is not null, then load the appropriate fragment assert targetFragment != null; switch (targetFragment) { case "LearningPage": - loadFragment(new LearningPage()); - bottomNavigationView.setSelectedItemId(R.id.navigation_apprendre); + loadFragment(new LearningPage()); // Load the LearningPage fragment + bottomNavigationView.setSelectedItemId(R.id.navigation_apprendre); // Select the appropriate bottom nav item break; case "StatsPage": - loadFragment(new StatsPage()); + loadFragment(new StatsPage()); // Load the StatsPage fragment bottomNavigationView.setSelectedItemId(R.id.navigation_statistiques); break; case "SettingsPage": + // Launch a new instance of PublicActivity to load the SettingsPage fragment Intent intent = new Intent(PublicActivity.this, PublicActivity.class); intent.putExtra("TARGET_FRAGMENT", "SettingsPage"); loadFragment(new SettingsPage()); bottomNavigationView.setSelectedItemId(R.id.navigation_parametres); break; case "PhotoPage": - loadFragment(new PhotoPage()); + loadFragment(new PhotoPage()); // Load the PhotoPage fragment bottomNavigationView.setSelectedItemId(R.id.navigation_identifier); break; } - // Set up navigation item selection - bottomNavigationView.setOnNavigationItemSelectedListener(item -> - - { + // Set up the BottomNavigationView item selection listener + bottomNavigationView.setOnNavigationItemSelectedListener(item -> { Fragment selectedFragment; + // Check the selected navigation item and load the corresponding fragment if (item.getItemId() == R.id.navigation_apprendre) { selectedFragment = new LearningPage(); } else if (item.getItemId() == R.id.navigation_statistiques) { @@ -55,21 +71,27 @@ public class PublicActivity extends AppCompatActivity { } else if (item.getItemId() == R.id.navigation_identifier) { selectedFragment = new PhotoPage(); } else { - selectedFragment=null; + // If none of the known items are selected, navigate back to HomeActivity + selectedFragment = null; Intent intent = new Intent(PublicActivity.this, HomeActivity.class); startActivity(intent); finish(); } - loadFragment(selectedFragment); + loadFragment(selectedFragment); // Load the selected fragment return true; }); } + /** + * Loads the specified fragment into the fragment container. + * + * @param fragment The fragment to load. + */ private void loadFragment(Fragment fragment) { + // Begin a fragment transaction to replace the current fragment with the new one getSupportFragmentManager() .beginTransaction() - .replace(R.id.fragment_container, fragment) - .commit(); + .replace(R.id.fragment_container, fragment) // Replace the current fragment + .commit(); // Commit the transaction } } - diff --git a/app/src/main/java/com/example/myapplication/ResultsFragment.java b/app/src/main/java/com/example/myapplication/ResultsFragment.java deleted file mode 100644 index 6822333..0000000 --- a/app/src/main/java/com/example/myapplication/ResultsFragment.java +++ /dev/null @@ -1,107 +0,0 @@ -package com.example.myapplication; - -import android.os.Bundle; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; -import android.widget.Button; -import android.widget.TableLayout; -import android.widget.TableRow; -import android.widget.TextView; - -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import androidx.fragment.app.Fragment; - -import java.util.Map; - -public class ResultsFragment extends Fragment { - - private Map<String, RecognizeSpecie> predictionResults; - private Button backToPredictionButton; - - // Constructor to receive prediction results - public ResultsFragment(Map<String, RecognizeSpecie> predictionResults) { - this.predictionResults = predictionResults; - } - - @Nullable - @Override - public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { - // Inflate the layout for this fragment - View view = inflater.inflate(R.layout.fragment_prediction_results, container, false); - - backToPredictionButton = view.findViewById(R.id.backToPrediction); - // Reference the TableLayout - TableLayout tableLayout = view.findViewById(R.id.results_table); - - // Add a header row to the table - addHeaderRow(tableLayout); - - // Populate the table with prediction results - populateTable(tableLayout); - - // Set up back button functionality - backToPredictionButton.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - requireActivity() - .getSupportFragmentManager() - .popBackStack(); // Navigate back to the previous fragment - } - }); - - return view; - - - } - - /** - * Adds a header row to the table layout. - */ - private void addHeaderRow(TableLayout tableLayout) { - TableRow headerRow = new TableRow(getContext()); - headerRow.setBackgroundColor(getResources().getColor(R.color.myBlue)); // Optional: Add header styling - addTableCell(headerRow, "Espèce"); - addTableCell(headerRow, "Total individus"); - addTableCell(headerRow, "Précision(%)"); - tableLayout.addView(headerRow); - - } - - /** - * Populates the table with rows based on the prediction results. - */ - private void populateTable(TableLayout tableLayout) { - if (predictionResults != null) { - for (Map.Entry<String, RecognizeSpecie> entry : predictionResults.entrySet()) { - TableRow dataRow = new TableRow(getContext()); - - RecognizeSpecie specie = entry.getValue(); - - addTableCell(dataRow, entry.getKey()); // Species name - addTableCell(dataRow, String.valueOf(specie.getSpecieBoxes().size())); // Count - addTableCell(dataRow, String.format("%.2f", specie.getAverageProbability())); // Average probability - - tableLayout.addView(dataRow); - } - } else { - // If no data exists, add a message row - TableRow emptyRow = new TableRow(getContext()); - addTableCell(emptyRow, "No data available"); - tableLayout.addView(emptyRow); - } - } - - /** - * Adds a cell to a given table row. - */ - private void addTableCell(TableRow row, String text) { - TextView textView = new TextView(getContext()); - textView.setText(text); - textView.setPadding(16, 16, 16, 16); - - textView.setTextColor(getResources().getColor(R.color.white)); // Optional: Add text styling - row.addView(textView); - } -} diff --git a/app/src/main/java/com/example/myapplication/OverlayDialogFragmentBecasseau.java b/app/src/main/java/com/example/myapplication/overlays/OverlayDialogFragmentBecasseau.java similarity index 94% rename from app/src/main/java/com/example/myapplication/OverlayDialogFragmentBecasseau.java rename to app/src/main/java/com/example/myapplication/overlays/OverlayDialogFragmentBecasseau.java index 43092ee..a009906 100644 --- a/app/src/main/java/com/example/myapplication/OverlayDialogFragmentBecasseau.java +++ b/app/src/main/java/com/example/myapplication/overlays/OverlayDialogFragmentBecasseau.java @@ -1,4 +1,4 @@ -package com.example.myapplication; +package com.example.myapplication.overlays; import android.os.Bundle; import android.view.LayoutInflater; @@ -10,6 +10,8 @@ import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.fragment.app.DialogFragment; +import com.example.myapplication.R; + public class OverlayDialogFragmentBecasseau extends DialogFragment { @Nullable @Override diff --git a/app/src/main/java/com/example/myapplication/OverlayDialogFragmentBernache.java b/app/src/main/java/com/example/myapplication/overlays/OverlayDialogFragmentBernache.java similarity index 94% rename from app/src/main/java/com/example/myapplication/OverlayDialogFragmentBernache.java rename to app/src/main/java/com/example/myapplication/overlays/OverlayDialogFragmentBernache.java index e939787..a113948 100644 --- a/app/src/main/java/com/example/myapplication/OverlayDialogFragmentBernache.java +++ b/app/src/main/java/com/example/myapplication/overlays/OverlayDialogFragmentBernache.java @@ -1,4 +1,4 @@ -package com.example.myapplication; +package com.example.myapplication.overlays; import android.os.Bundle; import android.view.LayoutInflater; @@ -10,6 +10,8 @@ import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.fragment.app.DialogFragment; +import com.example.myapplication.R; + public class OverlayDialogFragmentBernache extends DialogFragment { @Nullable @Override diff --git a/app/src/main/java/com/example/myapplication/OverlayDialogFragmentChevalier.java b/app/src/main/java/com/example/myapplication/overlays/OverlayDialogFragmentChevalier.java similarity index 94% rename from app/src/main/java/com/example/myapplication/OverlayDialogFragmentChevalier.java rename to app/src/main/java/com/example/myapplication/overlays/OverlayDialogFragmentChevalier.java index 09673ec..c412b77 100644 --- a/app/src/main/java/com/example/myapplication/OverlayDialogFragmentChevalier.java +++ b/app/src/main/java/com/example/myapplication/overlays/OverlayDialogFragmentChevalier.java @@ -1,4 +1,4 @@ -package com.example.myapplication; +package com.example.myapplication.overlays; import android.os.Bundle; import android.view.LayoutInflater; @@ -10,6 +10,8 @@ import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.fragment.app.DialogFragment; +import com.example.myapplication.R; + public class OverlayDialogFragmentChevalier extends DialogFragment { @Nullable @Override diff --git a/app/src/main/java/com/example/myapplication/OverlayDialogFragmentCormoran.java b/app/src/main/java/com/example/myapplication/overlays/OverlayDialogFragmentCormoran.java similarity index 94% rename from app/src/main/java/com/example/myapplication/OverlayDialogFragmentCormoran.java rename to app/src/main/java/com/example/myapplication/overlays/OverlayDialogFragmentCormoran.java index 16bc12f..c5724c2 100644 --- a/app/src/main/java/com/example/myapplication/OverlayDialogFragmentCormoran.java +++ b/app/src/main/java/com/example/myapplication/overlays/OverlayDialogFragmentCormoran.java @@ -1,4 +1,4 @@ -package com.example.myapplication; +package com.example.myapplication.overlays; import android.os.Bundle; import android.view.LayoutInflater; @@ -10,6 +10,8 @@ import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.fragment.app.DialogFragment; +import com.example.myapplication.R; + public class OverlayDialogFragmentCormoran extends DialogFragment { @Nullable @Override diff --git a/app/src/main/java/com/example/myapplication/OverlayDialogFragmentForm.java b/app/src/main/java/com/example/myapplication/overlays/OverlayDialogFragmentForm.java similarity index 98% rename from app/src/main/java/com/example/myapplication/OverlayDialogFragmentForm.java rename to app/src/main/java/com/example/myapplication/overlays/OverlayDialogFragmentForm.java index 1194bf3..c9141a6 100644 --- a/app/src/main/java/com/example/myapplication/OverlayDialogFragmentForm.java +++ b/app/src/main/java/com/example/myapplication/overlays/OverlayDialogFragmentForm.java @@ -1,7 +1,5 @@ -package com.example.myapplication; +package com.example.myapplication.overlays; -import android.app.AlertDialog; -import android.content.DialogInterface; import android.os.Bundle; import android.text.Editable; import android.text.TextWatcher; @@ -14,13 +12,16 @@ import android.widget.Button; import android.widget.EditText; import android.widget.Spinner; import android.widget.TextView; -import android.widget.TimePicker; import android.widget.Toast; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.fragment.app.DialogFragment; +import com.example.myapplication.GoogleFormApi; +import com.example.myapplication.PhotoPage; +import com.example.myapplication.R; + import retrofit2.Call; import retrofit2.Callback; import retrofit2.Response; diff --git a/app/src/main/java/com/example/myapplication/OverlayDialogFragmentFoulque.java b/app/src/main/java/com/example/myapplication/overlays/OverlayDialogFragmentFoulque.java similarity index 94% rename from app/src/main/java/com/example/myapplication/OverlayDialogFragmentFoulque.java rename to app/src/main/java/com/example/myapplication/overlays/OverlayDialogFragmentFoulque.java index 324ad85..1a12ad2 100644 --- a/app/src/main/java/com/example/myapplication/OverlayDialogFragmentFoulque.java +++ b/app/src/main/java/com/example/myapplication/overlays/OverlayDialogFragmentFoulque.java @@ -1,4 +1,4 @@ -package com.example.myapplication; +package com.example.myapplication.overlays; import android.os.Bundle; import android.view.LayoutInflater; @@ -10,6 +10,8 @@ import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.fragment.app.DialogFragment; +import com.example.myapplication.R; + public class OverlayDialogFragmentFoulque extends DialogFragment { @Nullable @Override diff --git a/app/src/main/java/com/example/myapplication/OverlayDialogFragmentGoeland.java b/app/src/main/java/com/example/myapplication/overlays/OverlayDialogFragmentGoeland.java similarity index 94% rename from app/src/main/java/com/example/myapplication/OverlayDialogFragmentGoeland.java rename to app/src/main/java/com/example/myapplication/overlays/OverlayDialogFragmentGoeland.java index ffc3136..a0c7d91 100644 --- a/app/src/main/java/com/example/myapplication/OverlayDialogFragmentGoeland.java +++ b/app/src/main/java/com/example/myapplication/overlays/OverlayDialogFragmentGoeland.java @@ -1,4 +1,4 @@ -package com.example.myapplication; +package com.example.myapplication.overlays; import android.os.Bundle; import android.view.LayoutInflater; @@ -10,6 +10,8 @@ import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.fragment.app.DialogFragment; +import com.example.myapplication.R; + public class OverlayDialogFragmentGoeland extends DialogFragment { @Nullable @Override diff --git a/app/src/main/java/com/example/myapplication/OverlayDialogFragmentMouette.java b/app/src/main/java/com/example/myapplication/overlays/OverlayDialogFragmentMouette.java similarity index 94% rename from app/src/main/java/com/example/myapplication/OverlayDialogFragmentMouette.java rename to app/src/main/java/com/example/myapplication/overlays/OverlayDialogFragmentMouette.java index 47948e2..862a71e 100644 --- a/app/src/main/java/com/example/myapplication/OverlayDialogFragmentMouette.java +++ b/app/src/main/java/com/example/myapplication/overlays/OverlayDialogFragmentMouette.java @@ -1,4 +1,4 @@ -package com.example.myapplication; +package com.example.myapplication.overlays; import android.os.Bundle; import android.view.LayoutInflater; @@ -10,6 +10,8 @@ import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.fragment.app.DialogFragment; +import com.example.myapplication.R; + public class OverlayDialogFragmentMouette extends DialogFragment { @Nullable @Override diff --git a/app/src/main/java/com/example/myapplication/OverlayDialogFragmentPluvier.java b/app/src/main/java/com/example/myapplication/overlays/OverlayDialogFragmentPluvier.java similarity index 94% rename from app/src/main/java/com/example/myapplication/OverlayDialogFragmentPluvier.java rename to app/src/main/java/com/example/myapplication/overlays/OverlayDialogFragmentPluvier.java index 7fab249..fa6a4ac 100644 --- a/app/src/main/java/com/example/myapplication/OverlayDialogFragmentPluvier.java +++ b/app/src/main/java/com/example/myapplication/overlays/OverlayDialogFragmentPluvier.java @@ -1,4 +1,4 @@ -package com.example.myapplication; +package com.example.myapplication.overlays; import android.os.Bundle; import android.view.LayoutInflater; @@ -10,6 +10,8 @@ import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.fragment.app.DialogFragment; +import com.example.myapplication.R; + public class OverlayDialogFragmentPluvier extends DialogFragment { @Nullable @Override diff --git a/app/src/main/java/com/example/myapplication/OverlayDialogFragmentTadorne.java b/app/src/main/java/com/example/myapplication/overlays/OverlayDialogFragmentTadorne.java similarity index 94% rename from app/src/main/java/com/example/myapplication/OverlayDialogFragmentTadorne.java rename to app/src/main/java/com/example/myapplication/overlays/OverlayDialogFragmentTadorne.java index 1ca876c..371958f 100644 --- a/app/src/main/java/com/example/myapplication/OverlayDialogFragmentTadorne.java +++ b/app/src/main/java/com/example/myapplication/overlays/OverlayDialogFragmentTadorne.java @@ -1,4 +1,4 @@ -package com.example.myapplication; +package com.example.myapplication.overlays; import android.os.Bundle; import android.view.LayoutInflater; @@ -10,6 +10,8 @@ import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.fragment.app.DialogFragment; +import com.example.myapplication.R; + public class OverlayDialogFragmentTadorne extends DialogFragment { @Nullable @Override diff --git a/app/src/main/java/com/example/myapplication/OverlayDialogFragmentZonage.java b/app/src/main/java/com/example/myapplication/overlays/OverlayDialogFragmentZonage.java similarity index 94% rename from app/src/main/java/com/example/myapplication/OverlayDialogFragmentZonage.java rename to app/src/main/java/com/example/myapplication/overlays/OverlayDialogFragmentZonage.java index 0c3d5b0..b17dcaa 100644 --- a/app/src/main/java/com/example/myapplication/OverlayDialogFragmentZonage.java +++ b/app/src/main/java/com/example/myapplication/overlays/OverlayDialogFragmentZonage.java @@ -1,4 +1,4 @@ -package com.example.myapplication; +package com.example.myapplication.overlays; import android.os.Bundle; import android.view.LayoutInflater; @@ -10,6 +10,8 @@ import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.fragment.app.DialogFragment; +import com.example.myapplication.R; + public class OverlayDialogFragmentZonage extends DialogFragment { @Nullable @Override diff --git a/app/src/main/java/com/example/myapplication/BoundingBox.java b/app/src/main/java/com/example/myapplication/recognation_logic/BoundingBox.java similarity index 92% rename from app/src/main/java/com/example/myapplication/BoundingBox.java rename to app/src/main/java/com/example/myapplication/recognation_logic/BoundingBox.java index 20110f1..6396e59 100644 --- a/app/src/main/java/com/example/myapplication/BoundingBox.java +++ b/app/src/main/java/com/example/myapplication/recognation_logic/BoundingBox.java @@ -1,4 +1,4 @@ -package com.example.myapplication; +package com.example.myapplication.recognation_logic; public class BoundingBox { public float x1, y1, x2, y2, cx, cy, w, h, cnf; diff --git a/app/src/main/java/com/example/myapplication/NoSpeciesRecognizedException.java b/app/src/main/java/com/example/myapplication/recognation_logic/NoSpeciesRecognizedException.java similarity index 55% rename from app/src/main/java/com/example/myapplication/NoSpeciesRecognizedException.java rename to app/src/main/java/com/example/myapplication/recognation_logic/NoSpeciesRecognizedException.java index a23697c..fd5f1df 100644 --- a/app/src/main/java/com/example/myapplication/NoSpeciesRecognizedException.java +++ b/app/src/main/java/com/example/myapplication/recognation_logic/NoSpeciesRecognizedException.java @@ -1,4 +1,4 @@ -package com.example.myapplication; +package com.example.myapplication.recognation_logic; public class NoSpeciesRecognizedException extends Exception{ diff --git a/app/src/main/java/com/example/myapplication/OutputPredictedImage.java b/app/src/main/java/com/example/myapplication/recognation_logic/OutputPredictedImage.java similarity index 92% rename from app/src/main/java/com/example/myapplication/OutputPredictedImage.java rename to app/src/main/java/com/example/myapplication/recognation_logic/OutputPredictedImage.java index 97423b8..2f4e005 100644 --- a/app/src/main/java/com/example/myapplication/OutputPredictedImage.java +++ b/app/src/main/java/com/example/myapplication/recognation_logic/OutputPredictedImage.java @@ -1,9 +1,7 @@ -package com.example.myapplication; +package com.example.myapplication.recognation_logic; import android.graphics.Bitmap; -import android.graphics.BitmapFactory; -import java.util.List; import java.util.Map; /** diff --git a/app/src/main/java/com/example/myapplication/RecognizeSpecie.java b/app/src/main/java/com/example/myapplication/recognation_logic/RecognizeSpecie.java similarity index 95% rename from app/src/main/java/com/example/myapplication/RecognizeSpecie.java rename to app/src/main/java/com/example/myapplication/recognation_logic/RecognizeSpecie.java index 43ae252..de3f52a 100644 --- a/app/src/main/java/com/example/myapplication/RecognizeSpecie.java +++ b/app/src/main/java/com/example/myapplication/recognation_logic/RecognizeSpecie.java @@ -1,4 +1,4 @@ -package com.example.myapplication; +package com.example.myapplication.recognation_logic; import java.io.Serializable; import java.util.List; diff --git a/app/src/main/java/com/example/myapplication/TestModeleTflite.java b/app/src/main/java/com/example/myapplication/recognation_logic/TestModeleTflite.java similarity index 89% rename from app/src/main/java/com/example/myapplication/TestModeleTflite.java rename to app/src/main/java/com/example/myapplication/recognation_logic/TestModeleTflite.java index 6b10aca..3f0a504 100644 --- a/app/src/main/java/com/example/myapplication/TestModeleTflite.java +++ b/app/src/main/java/com/example/myapplication/recognation_logic/TestModeleTflite.java @@ -1,4 +1,4 @@ -package com.example.myapplication; +package com.example.myapplication.recognation_logic; import static android.graphics.Paint.*; @@ -40,7 +40,7 @@ public class TestModeleTflite { private static final float INPUT_STANDARD_DEVIATION = 255f; private static final DataType INPUT_IMAGE_TYPE = DataType.FLOAT32; private static final DataType OUTPUT_IMAGE_TYPE = DataType.FLOAT32; - private static final float CONFIDENCE_THRESHOLD = 0.3F; + private static final float CONFIDENCE_THRESHOLD = 0.4F; private static final float IOU_THRESHOLD = 0.5F; private Interpreter interpreter; @@ -144,20 +144,24 @@ public class TestModeleTflite { */ public OutputPredictedImage recognizeSpeciesClass(Bitmap inputImage) throws NoSpeciesRecognizedException { Map<String, RecognizeSpecie> predictionResults = new HashMap<>(); + TensorBuffer outputBuffer = predict(inputImage); List<BoundingBox> boxes = processOutput(outputBuffer); - - if (boxes != null) { - System.out.println("not empty"); + if (boxes != null && !boxes.isEmpty()) { for (BoundingBox box : boxes) { - predictionResults.computeIfAbsent(box.clsName, k -> new RecognizeSpecie(k, new ArrayList<>())) - .getSpecieBoxes().add(box); + if (predictionResults.containsKey(box.clsName)) { + predictionResults.get(box.clsName).getSpecieBoxes().add(box); + } else { + predictionResults.put(box.clsName, new RecognizeSpecie(box.clsName, new ArrayList<>())); + predictionResults.get(box.clsName).getSpecieBoxes().add(box); + } } Bitmap annotatedImage = annotateImage(inputImage, boxes); return new OutputPredictedImage(annotatedImage, predictionResults); } else { throw new NoSpeciesRecognizedException(); } + } /** @@ -248,35 +252,6 @@ public class TestModeleTflite { /** * Draws bounding boxes on an image. */ - /* - private Bitmap drawBoundingBoxes(Bitmap bitmap, List<BoundingBox> boxes) { - Bitmap mutableBitmap = bitmap.copy(Bitmap.Config.ARGB_8888, true); - Canvas canvas = new Canvas(mutableBitmap); - - Paint paint = new Paint(); - paint.setColor(Color.RED); - paint.setStyle(Style.STROKE); - paint.setStrokeWidth(8f); - /* - Paint textPaint = new Paint(); - textPaint.setColor(Color.WHITE); - textPaint.setTextSize(40f); - //textPaint.setTypeface(Paint.DEFAULT_BOLD); - - for (BoundingBox box : boxes) { - RectF rect = new RectF( - box.x1 * mutableBitmap.getWidth(), - box.y1 * mutableBitmap.getHeight(), - box.x2 * mutableBitmap.getWidth(), - box.y2 * mutableBitmap.getHeight() - ); - canvas.drawRect(rect, paint); - //canvas.drawText(box.clsName, rect.left, rect.bottom, textPaint); - } - - return mutableBitmap; - }*/ - private Bitmap drawBoundingBoxes(Bitmap bitmap, List<BoundingBox> boxes) { Bitmap mutableBitmap = bitmap.copy(Bitmap.Config.ARGB_8888, true); Canvas canvas = new Canvas(mutableBitmap); diff --git a/app/src/main/java/com/example/myapplication/TutorialActivity.java b/app/src/main/java/com/example/myapplication/tutorials/TutorialActivity.java similarity index 95% rename from app/src/main/java/com/example/myapplication/TutorialActivity.java rename to app/src/main/java/com/example/myapplication/tutorials/TutorialActivity.java index 9749d00..5605f67 100644 --- a/app/src/main/java/com/example/myapplication/TutorialActivity.java +++ b/app/src/main/java/com/example/myapplication/tutorials/TutorialActivity.java @@ -1,4 +1,4 @@ -package com.example.myapplication; +package com.example.myapplication.tutorials; import android.content.Intent; import android.os.Bundle; @@ -11,6 +11,9 @@ import androidx.appcompat.app.AppCompatActivity; import androidx.core.content.ContextCompat; import androidx.viewpager2.widget.ViewPager2; +import com.example.myapplication.HomeActivity; +import com.example.myapplication.R; + import java.util.Arrays; import java.util.List; diff --git a/app/src/main/java/com/example/myapplication/TutorialFragmentAdapter.java b/app/src/main/java/com/example/myapplication/tutorials/TutorialFragmentAdapter.java similarity index 94% rename from app/src/main/java/com/example/myapplication/TutorialFragmentAdapter.java rename to app/src/main/java/com/example/myapplication/tutorials/TutorialFragmentAdapter.java index 1deec3b..de09261 100644 --- a/app/src/main/java/com/example/myapplication/TutorialFragmentAdapter.java +++ b/app/src/main/java/com/example/myapplication/tutorials/TutorialFragmentAdapter.java @@ -1,4 +1,4 @@ -package com.example.myapplication; +package com.example.myapplication.tutorials; import androidx.annotation.NonNull; import androidx.fragment.app.Fragment; diff --git a/app/src/main/java/com/example/myapplication/TutorialPageFragment.java b/app/src/main/java/com/example/myapplication/tutorials/TutorialPageFragment.java similarity index 93% rename from app/src/main/java/com/example/myapplication/TutorialPageFragment.java rename to app/src/main/java/com/example/myapplication/tutorials/TutorialPageFragment.java index 803cea3..89ff29c 100644 --- a/app/src/main/java/com/example/myapplication/TutorialPageFragment.java +++ b/app/src/main/java/com/example/myapplication/tutorials/TutorialPageFragment.java @@ -1,10 +1,9 @@ -package com.example.myapplication; +package com.example.myapplication.tutorials; import android.os.Bundle; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; -import android.widget.Button; import androidx.annotation.NonNull; import androidx.annotation.Nullable; diff --git a/app/src/main/res/layout/activity_tutorial.xml b/app/src/main/res/layout/activity_tutorial.xml index 4f6b328..ecb3794 100644 --- a/app/src/main/res/layout/activity_tutorial.xml +++ b/app/src/main/res/layout/activity_tutorial.xml @@ -6,7 +6,7 @@ android:layout_width="match_parent" android:layout_height="match_parent" android:background="@color/black" - tools:context=".TutorialActivity"> + tools:context=".tutorials.TutorialActivity"> <ImageView android:id="@+id/image_1" -- GitLab