Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft: Sprint 4 merge #102

Merged
merged 96 commits into from
Mar 13, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
96 commits
Select commit Hold shift + click to select a range
a813d3a
Added crop stages and modal bottom sheet
RD211 Mar 4, 2023
10d1b40
create patches function, fix bug
mirceatlx Mar 5, 2023
6f1b24d
persist data from modules to google storage
mirceatlx Mar 5, 2023
1298562
add uuid to pipeline object
mirceatlx Mar 5, 2023
272cfc2
store uris in firestore
mirceatlx Mar 5, 2023
9b314bc
remove json
mirceatlx Mar 5, 2023
610752e
fix mat::fread
paulmis Mar 6, 2023
58841cf
async upload
mirceatlx Mar 6, 2023
9c620a4
update gitignore
mirceatlx Mar 6, 2023
90db51e
change pipeline structure and refactor cloud code
mirceatlx Mar 6, 2023
287cd44
rename channels, assert fread
paulmis Mar 8, 2023
78a27ad
remove plot
paulmis Mar 8, 2023
ef52705
remove redundant test images
paulmis Mar 8, 2023
2327eb4
add runnable dependencies
paulmis Mar 8, 2023
ea43f0a
fix pipeline trigger
paulmis Mar 8, 2023
6654c56
test pipeline trigger
paulmis Mar 8, 2023
416835c
verify bands
paulmis Mar 9, 2023
1739351
fix pipeline, rename bands to channels
paulmis Mar 9, 2023
b84c606
add pipeline::build_module
paulmis Mar 9, 2023
7090ec0
fix pylint
paulmis Mar 9, 2023
230ac01
fix mypy
paulmis Mar 9, 2023
86f52c5
add pipeline tests
paulmis Mar 9, 2023
59aa840
add test lint stages
paulmis Mar 9, 2023
b0f7baf
add checkout, fail-under pylint
paulmis Mar 9, 2023
2d8a366
fix pylint
paulmis Mar 9, 2023
4474a12
decrease fail limit for testing
paulmis Mar 9, 2023
177dfe0
add pip to cache, cd right directories
paulmis Mar 9, 2023
f6f9b75
fix pip cache
paulmis Mar 9, 2023
19b3e62
Add modal bottom sheet for insights
nadinekuo Mar 10, 2023
a49d4b4
insight description added to bottom sheet
nadinekuo Mar 10, 2023
845c659
separate widget for bottom sheet
nadinekuo Mar 10, 2023
4ba2411
add images to insights
nadinekuo Mar 10, 2023
2d0bc8d
add characteristics and recommendations to insights
nadinekuo Mar 10, 2023
15aed82
extend insight model
nadinekuo Mar 10, 2023
e087587
modify insights store
nadinekuo Mar 10, 2023
b8d842c
modify insights store
nadinekuo Mar 10, 2023
100eecf
show date and recommendations
nadinekuo Mar 10, 2023
6824262
add scientific name and area affected
nadinekuo Mar 10, 2023
54509ef
hotfix: calculate polygon area in ha
nadinekuo Mar 10, 2023
db192ac
frontend readme.md updated
nadinekuo Mar 10, 2023
3ecb5ba
Update README.md
nadinekuo Mar 10, 2023
d9d9cf3
frontend readme.md updated
nadinekuo Mar 10, 2023
4423371
Merge branch 'dev' of https://github.com/GDSC-Delft-Dev/apa into dev
nadinekuo Mar 10, 2023
cf212d2
cloud functions setup
nadinekuo Mar 10, 2023
0d9f975
split menu button and insights selection
nadinekuo Mar 11, 2023
772778b
show dialog for picking localized insights
nadinekuo Mar 11, 2023
43dc4c8
stateful builder for insight dialog
nadinekuo Mar 11, 2023
7cbf478
Merge pull request #93 from GDSC-Delft-Dev/72-add-runnable-dependencies
paulmis Mar 11, 2023
de2fe95
fix mat::fread
paulmis Mar 6, 2023
0620084
rename channels, assert fread
paulmis Mar 8, 2023
18017a0
remove plot
paulmis Mar 8, 2023
0388271
remove redundant test images
paulmis Mar 8, 2023
1302b7e
Merge remote-tracking branch 'origin/74-populate-databases-with-examp…
paulmis Mar 11, 2023
277a58e
add fread for gryascale sources
paulmis Mar 11, 2023
99862fa
add freads tests
paulmis Mar 11, 2023
551fe91
change main path
paulmis Mar 11, 2023
cd4dd5a
Merge pull request #90 from GDSC-Delft-Dev/74-populate-databases-with…
paulmis Mar 11, 2023
87a92d8
use built in menu drawer
nadinekuo Mar 11, 2023
ce3f094
hidden menu drawer in insights screen
nadinekuo Mar 11, 2023
0ffea77
Merge pull request #99 from GDSC-Delft-Dev/96-details-localized-insights
nadinekuo Mar 11, 2023
9287f07
Update README.md
nadinekuo Mar 11, 2023
12edd87
updated structure of the insights and added scans
RD211 Mar 11, 2023
5dd6ed2
pulled dev and merged
RD211 Mar 11, 2023
3e615fb
change async upload
mirceatlx Mar 11, 2023
82ac701
add persistable with pydash
mirceatlx Mar 11, 2023
466437a
fix pylint and mypy
mirceatlx Mar 11, 2023
e2e5971
readd asyncio
mirceatlx Mar 11, 2023
dc863fe
migrated insights to the structure in the diagram
RD211 Mar 11, 2023
445b8b5
readme updated
nadinekuo Mar 11, 2023
7ab0ebc
update readme
nadinekuo Mar 11, 2023
611257e
change time and add docs
mirceatlx Mar 11, 2023
6e61bcf
fix mypy
mirceatlx Mar 11, 2023
7e0a4ce
made time shifts work
RD211 Mar 11, 2023
e0e436f
Merge branch 'dev' into 61-persist-pipeline-outputs-in-the-cloud-storage
mirceatlx Mar 11, 2023
e13af45
fix merge commit
mirceatlx Mar 11, 2023
4039848
Merge pull request #85 from GDSC-Delft-Dev/61-persist-pipeline-output…
mirceatlx Mar 11, 2023
10d467e
add test lint stages
paulmis Mar 9, 2023
2f8ed94
add checkout, fail-under pylint
paulmis Mar 9, 2023
61e5e34
fix pylint
paulmis Mar 9, 2023
b99b4e3
decrease fail limit for testing
paulmis Mar 9, 2023
bcae32b
add pip to cache, cd right directories
paulmis Mar 9, 2023
9572ffc
fix pip cache
paulmis Mar 9, 2023
5331665
Merge remote-tracking branch 'origin/87-testing-and-sa-in-cicd' into …
paulmis Mar 11, 2023
7c9837d
added pests and diseases
RD211 Mar 11, 2023
db44c49
Update README.md
nadinekuo Mar 11, 2023
8acc872
hotfix: remove stray image
nadinekuo Mar 11, 2023
eae8c44
make cloud optional, add async tests
paulmis Mar 11, 2023
b744fb2
made font change and added permission request for location
RD211 Mar 11, 2023
cc21406
Update src/frontend/lib/views/insights/widgets/insights_selection.dart
RD211 Mar 11, 2023
20295cc
Update src/frontend/lib/views/insights/widgets/insights_selection.dart
RD211 Mar 11, 2023
84bf054
some last font changes and added area
RD211 Mar 11, 2023
55d69c3
added hierarchy structure to the insight items
RD211 Mar 11, 2023
13044ae
change tests to async
paulmis Mar 12, 2023
fa66577
Merge pull request #105 from GDSC-Delft-Dev/28-display-historical-fie…
RD211 Mar 12, 2023
08865d5
fix mosaic tests
mirceatlx Mar 12, 2023
783a4b1
Merge pull request #103 from GDSC-Delft-Dev/87-testing-and-sa-in-cicd
paulmis Mar 12, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
54 changes: 52 additions & 2 deletions .github/workflows/pipeline.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ name: Pipeline build

on:
push:
paths:
- 'src/backend'
#paths:
# - 'src/backend/**'
branches:
- '*'

Expand All @@ -13,7 +13,57 @@ env:
GAR_REPOSITORY: pipelines-dev

jobs:
lint:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v3

- uses: actions/setup-python@v4
name: Setup Python 3.10
with:
python-version: "3.10"
cache: 'pip'
- run: |
cd src/backend
python -m pip install --upgrade pip
pip install mypy pylint
pip install -r requirements.txt

- name: Run pylint
run: |
cd src/backend
pylint ./pipeline --fail-under 9

- name: Run mypy
run: |
cd src/backend
mypy . --explicit-package-bases

test:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v2

- name: Set up Python 3.10
uses: actions/setup-python@v4
with:
python-version: "3.10"

- name: Install dependencies
run: |
cd src/backend
python -m pip install --upgrade pip
pip install pytest
pip install -r requirements.txt

- name: Run pytest
run: pytest

deploy:
needs: [lint, test]

# Add 'id-token' with the intended permissions for workload identity federation
permissions:
contents: 'read'
Expand Down
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,8 @@ __pycache__
.mypy_cache
.pytest_cache
expected_preprocess_masked.npy
.vscode
*json
nutrient_masks.npy
.vscode
.idea
96 changes: 87 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,24 +1,102 @@
# apa
Autonomous precision agriculture with UAVs
<!-- TODO: add code coverage? -->
# Terrafarm

![example workflow](https://github.com/GDSC-Delft-Dev/apa/actions/workflows/pipeline.yml/badge.svg)

# Setup
## About

### Problem we solving
Growing (high-quality) crops sustainably for an ever-increasing population is one of the biggest challenges we face today, as farmers all over the world are faced with complex decision making problems for a vast amount of crops. To this end, a variety of parameters need to be traced - think of application of fertilizer, soil humidity or availability of nutrients.

In traditional agriculture, fields are treated as homogeneous entities, which generally leads to sub-optimal treatment due to lack of (localized) traceability. This is problematic, as oversupply of agricultural inputs leads to environmental pollution. Moreover, unnecessary large quantities can go to waste if produce are not harvested at their optimal time. Finally, this clearly leads to low yield density and hence missed profits for farmers.

[Precision agriculture](https://en.wikipedia.org/wiki/Precision_agriculture) on the other hand, aims to produce more crops with fewer resources while maintaining quality. This sustainable agricultural model utilizes IT solutions to allow for localized treatment to a much finer degree. This paradigm shift is becoming increasingly urgent because of the worldwide increase in food demands for example: the number of people who will require food in 2050 is estimated at nine billion.

### Our solution
Our **mobile app Terrafarm** allows farmers to perform **smart monitoring, analysis and planning** in an intuitive and affordable manner. In fact, our system uses **image processing and deep learning** to extact **actionable insights** from multispectral drone images. These insights - think of pest infestations, moisture content or nutrient deficiencies - are visualized to users, thereby providing full transparancy. We aim to target both small- and medium-scale farmers. Detailed information about our image processing pipeline and Flutter mobile app can be found under `apa/src/backend` and `apa/src/frontend` respectively.

<div>
<img src="assets/Terrafarm-poster-0.jpg" alt="Image 1" width="500" style="display:inline-block;">
<img src="assets/Terrafarm-poster-1.jpg" alt="Image 2" width="500" style="display:inline-block;">
</div>

<p style="text-align:center;">Figure: Information poster presenting Terrafarm</p>



# Build Tools

![image](https://img.shields.io/badge/Flutter-02569B?style=for-the-badge&logo=flutter&logoColor=white)
</br>
![image](https://img.shields.io/badge/Dart-0175C2?style=for-the-badge&logo=dart&logoColor=white)
</br>
![image](https://img.shields.io/badge/Python-FFD43B?style=for-the-badge&logo=python&logoColor=blue)
</br>
![image](https://img.shields.io/badge/firebase-ffca28?style=for-the-badge&logo=firebase&logoColor=black)
</br>
![image](https://img.shields.io/badge/Google_Cloud-4285F4?style=for-the-badge&logo=google-cloud&logoColor=white)
</br>
![image](https://img.shields.io/badge/TensorFlow-FF6F00?style=for-the-badge&logo=tensorflow&logoColor=white)
</br>
![image](https://img.shields.io/badge/OpenCV-27338e?style=for-the-badge&logo=OpenCV&logoColor=white)
</br>
![image](https://img.shields.io/badge/GitHub_Actions-2088FF?style=for-the-badge&logo=github-actions&logoColor=white)

# Getting Started

Follow these steps to set up your project locally.

Clone the repo
```
# Clone the repo
git clone https://github.com/GDSC-Delft-Dev/apa.git
```

## Setup backend

# Setup virtual python environment
Setup virtual python environment
```
pip install virtualenv
virtualenv env
```

# Activate on MacOS or Linux
Activate on MacOS or Linux
```
source env/bin/activate
# Activate on Windows
source env/Scripts/activate
```

Activate on Windows
```
source env/Scripts/activate
```
Install Python requirements
```
pip install -r requirements.txt
```
Please refer to `apa/src/backend/README.md` for detailed information on the image processing pipeline.
<!-- TODO: Perhaps more info? -->

## Setup frontend
Please refer to `apa/src/frontend/README.md`.


# Contributing
Anyone who is eager to contribute to this project is very welcome to do so. Simply take the following steps:
1. Fork the project
2. Create your own feature branch
3. Commit your changes
4. Push to the `dev` branch and open a PR

# Datasets
You can play with the datasets in the *notebooks* folder.
You can play with the datasets in the `notebooks` folder.


# License
Distributed under the MIT License. See `LICENSE.txt` for more information.

# Contact
- Google Developers Student Club Delft - [email protected]
- Paul Misterka - [email protected]
- Mircea Lica - [email protected]
- David Dinucu-Jianu - [email protected]
- Nadine Kuo - [email protected]
<!-- Not sure if I shou -->
Binary file added assets/Terrafarm-poster-0.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/Terrafarm-poster-1.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
6 changes: 3 additions & 3 deletions src/backend/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,15 +32,15 @@ Our project uses `mypy` and `pylint` to assert the quality of the code. You can

```
python -m mypy . --explicit-package-bases
python -m pylint ../pipeline
python -m pylint ./pipeline
```

### CI/CD
The CI/CD pushes the build from the latest commit to the `pipelines-dev` repository in the Google Artifact Registry.

### Modules
To make the code extendible, maintainable, and multithreaded, the pipeline is divided into modules. Modules are run sequentially, and each can have multiple implementations that execute different logic, but compute the same type of data. We distinguish the following modules:
- Mosaicing module - transforms the flyover images into a single farmland bird's eye view image
- Mosaicing module - transforms the flyover images into a single farmland bird's eye view image. Moreover the module creates non-overlapping patches used in subsequent pipeline stages.
- Index module - computes pixel-level indicies that provide general information about the field
- Insight module - evaluates the database and indicies to provide actionable and localized insights that identify issues and improve farming efficiency
- Segmentation module - computes pixel-level masks for a number of different annotations that can be directly shown to the user
Expand Down Expand Up @@ -87,4 +87,4 @@ The module is a logical part of the image processing pipeline, chained sequentia
The parallel module is a module that can run multiple threads of execution at the same time, essentially allowing parallel module invocations. Parallel modules implement logical groups of functionalities, such as the calculation of all indicies (e.g. `NDVI` and `Mositure`) that do not rely on each other.

#### Pipeline data object
The data object contains all data relevant to the pipeline job. The pipeline initializes the data object dynamically through the use of the `prepare()` method. Note that, similarily to constructors, the preparation of your implementation should follow the preparation of the base class.
The data object contains all data relevant to the pipeline job. The pipeline initializes the data object dynamically through the use of the `prepare()` method. Note that, similarily to constructors, the preparation of your implementation should follow the preparation of the base class.
23 changes: 16 additions & 7 deletions src/backend/main.py
Original file line number Diff line number Diff line change
@@ -1,20 +1,29 @@
import glob
from pipeline.templates import full_pipeline, default_pipeline, nutrient_pipeline
from pipeline.mat import Mat
from pipeline.templates import full_pipeline, default_pipeline, training_pipeline, nutrient_pipeline
import firebase_admin
from firebase_admin import credentials, firestore
import asyncio
from pipeline.mat import Mat, Channels
import numpy as np

def main():
"""Main entry point."""

# Get test data
import os
imgs = [Mat.read(file) for file in glob.glob("./pipeline/test/data/mosaicing/farm/D*.JPG")]
imgs = [Mat.read(file) for file in glob.glob("pipeline/test/data/mosaicing/farm/D*.JPG")]
imgs = imgs[:1]

# Run the pipeline
print(len(imgs))
pipeline = nutrient_pipeline()
pipeline.show()
res = pipeline.run(imgs)
# Print the result

# Authenticate to firebase
if pipeline.config.cloud.use_cloud:
cred = credentials.Certificate("terrafarm-378218-firebase-adminsdk-nept9-e49d1713c7.json")
firebase_admin.initialize_app(cred)

# Run the pipeline
res = asyncio.run(pipeline.run(imgs))
print(res)

if __name__ == "__main__":
Expand Down
3 changes: 2 additions & 1 deletion src/backend/mypy.ini
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
[mypy]
ignore_missing_imports = True
ignore_missing_imports = True
disable_error_code = attr-defined, call-overload
16 changes: 12 additions & 4 deletions src/backend/pipeline/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,24 @@
from .modules.module import Module
from .modules.runnable import Runnable
from typing import Any, Type
from dataclasses import dataclass

@dataclass
class CloudConfig:
"""Configures cloud resources."""
use_cloud: bool = False
bucket_name: str = ""

class Config:
"""
Initializes the config.

Args:
use_cloud: whether use cloud resources, e.g. persist data to cloud storage.
If False, the user doesn't need to provide GCP credentials.
modules: dictionary of modules to initialize and their initialization data
"""
def __init__(self, modules: dict[Type[Module], Any],
parallel_modules: dict[Type[Module], Any]):
def __init__(self, modules: dict[Type[Module], Any], cloud: CloudConfig = CloudConfig()):
assert len(modules) > 0, "No modules specified"
self.modules: dict[Type[Module], Any] = modules
self.parallel_modules: dict[Type[Module], Any] = parallel_modules

self.cloud: CloudConfig = cloud
80 changes: 55 additions & 25 deletions src/backend/pipeline/mat.py
Original file line number Diff line number Diff line change
@@ -1,20 +1,19 @@
from __future__ import annotations
from enum import IntEnum
from enum import Enum
import cv2
import numpy as np

class Channels(IntEnum):
"""
Defines channel types for the input images
"""
R = 0
G = 1
B = 2
NIR = 3
FIR = 4
T = 5
A = 6
GREYSCALE = 7
class Channels(Enum):
"""Defines channel types for the input images"""
R = "Red"
G = "Green"
B = "Blue"
RE = "Red Edge"
NIR = "Near Infrared"
MIR = "Mid Infrared"
T = "Thermal"
GREYSCALE = "Grayscale"
A = "Alpha"

default_channels = [Channels.R, Channels.G, Channels.B]

Expand Down Expand Up @@ -51,10 +50,11 @@ def read(cls, path: str) -> Mat:
return cls(mat, channels = default_channels)

@classmethod
def fread(cls, paths: dict[str, list[Channels]]) -> Mat:
def fread(cls, paths: list[tuple[str, list[Channels]]]) -> Mat:
"""
UNTESTED. Full reads an image with an arbitrary number of
channels from multiple source paths.
Rreads an image with an arbitrary number of
channels from multiple source paths that contain images
with different numbers of channels.

Args:
paths: a dictionary of paths and their corresponding channels
Expand All @@ -65,23 +65,53 @@ def fread(cls, paths: dict[str, list[Channels]]) -> Mat:
"""

# Load the images
mats = [cv2.imread(path) for path in paths.keys()]
mats = [cv2.imread(path[0], cv2.IMREAD_UNCHANGED) if len(path[1]) == 1
else cv2.imread(path[0])
for path in paths]

# Verify that the number of channels match and the
# dimensions match
# Verify input data integrity
assert len(mats) == len(paths), "Reading images failed"
shape = mats[0].shape[:2]
for mat, channels in zip(mats, paths.values()): #type: tuple[cv2.Mat, list[Channels]]
assert mat.ndim == len(channels)
for mat, channels in zip(mats, [path[1] for path in paths]): #type: tuple[cv2.Mat, list[Channels]]
# Check number of channels
if len(channels) == 1:
assert mat.ndim == 2, "Image is not grayscale"
else:
assert len(channels) == mat.shape[2], "Image has incorrect number of channels"

# Check image dimensions
assert shape == mat.shape[:2]

# Flatten channels
channels = sum(paths.values(), [])

# Combine arrays
arr = np.concatenate([np.asarray(mat[:,:,:]) for mat in mats], axis=2)
# Split multichannel mats
arr_mats = np.array([mat if mat.ndim == 2 else np.split(mat) for mat in mats])

# Concatenate grayscales
arr = np.transpose(arr_mats, (1, 2, 0))

# Aggregate channels
channels = sum([path[1] for path in paths], [])

# Return the combined data
return cls(arr, channels)

@classmethod
def freads(cls, paths: list[str], channels: list[Channels]) -> Mat:
"""
Reads an image with an arbitrary number of channels from
multiple source paths containing grayscale images only.

Args:
paths: a list of paths to read the images from
channels: a list of channels in the images paths
(in the order they appear in the paths)

Returns:
The loaded Mat.
"""

assert len(paths) == len(channels), "Number of paths and channels must match"
return Mat.fread([(path, [channel]) for path, channel in zip(paths, channels)])

def get(self) -> cv2.Mat:
"""
Expand Down
Loading