Skip to content

Commit

Permalink
update RCA algo and set RELOAD in docker-compose to --reload (#78)
Browse files Browse the repository at this point in the history
Co-authored-by: janf <[email protected]>
  • Loading branch information
iferencik and janf authored Apr 3, 2024
1 parent 3fcb44a commit 88e735d
Show file tree
Hide file tree
Showing 2 changed files with 71 additions and 47 deletions.
2 changes: 2 additions & 0 deletions docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ services:
- "./src/cogserver:/opt/server/cogserver"
env_file:
- ./gdal_rio.env
environment:
- RELOAD=--reload
# the above env file contains the $PORT variable.
ports:
- "${PORT}:${PORT}"
Expand Down
116 changes: 69 additions & 47 deletions src/cogserver/algorithms/rca.py
Original file line number Diff line number Diff line change
@@ -1,79 +1,101 @@
from typing import Sequence

import numpy
from typing import List
from pydantic import Field
from rio_tiler.models import ImageData

from titiler.core.algorithm.base import BaseAlgorithm







class RapidChangeAssessment(BaseAlgorithm):

title: str = "Rapid Change Assessment Tool"
description: str = "Quick assessment to detect changes by comparing two bands"
description: str = "Detect changes by subtracting relative radiances between two images taken between and after an event"

"""Rapid change assessment."""
# parameters
threshold: float = Field(
0.8, ge=0.0, le=1.0,
title="Threshold(%)",
description="Threshold (%) to mask changes which is ranged between 0 and 1"
)
# threshold: float = Field(
# default=0, ge=0.0, le=1.0,
# title="Threshold(%)",
# description="Only pixels with change/decrease above this threshold will be supplied"
# )

only_negative: bool = Field(
default=True,
title='Only negative',
description='Compute only pixels whose values have decreased between the two dates'

cloud_mask: bool = Field(
False,
title="Cloud mask",
description="If enabled, cloud will be removed masked by band 3 and band 4."
)

cloud_mask_threshold: int = Field(
1,
# cloud_mask: bool = Field(
# False,
# title="Cloud mask",
# description="If enabled, cloud will be removed masked by band 3 and band 4."
# )

cloud_mask_value: int = Field(
default=1,
ge=0,
le=5,
title="Cloud mask threshold",
description="Remove cloud if band value is greater than this threshold"
description="Inclusive cloud threshold. Specifying a value results in considering all lower values",
options_descriptions = ['No clouds', 'Almost no clouds', 'Very few clouds', 'Partially cloudy', 'Cloudy', 'Very cloudy' ]
)



# metadata
input_nbands: int = 2
input_description: str = "the first two bands will be used to compute change detection. " \
"the last two bands will be used to mask the result. " \
"The first two bands are required. "
input_description: str = "The bands that will be used to detect changes"
input_bands: List = [
{'title': 'Start date image', 'description': 'The image before the event', 'required':True},
{'title': 'End date image', 'description': 'The image after the event', 'required':True},
{'title': 'Start date cloud mask', 'description': 'The cloud mask of the image before the event',
'required': True},
{'title': 'End date cloud mask', 'description': 'The cloud mask of the image after the event',
'required': True},
]

# input_second_image_title: str = 'Second image'
# input_second_image_description: str = 'The image after the event'

output_nbands: int = 1
output_dtype: int = "uint8"
output_min: Sequence[int] = [0]
output_max: Sequence[int] = [1]
output_description: str = "Masked result which has changed greater than threshold"
output_dtype: int = "int8"
output_min: Sequence[int] = [-100]
output_max: Sequence[int] = [100]
output_description: str = "Pixels/locations whose values have decreased/changed"

def __call__(self, img: ImageData) -> ImageData:
"""Rapid change assessment."""
b1 = img.array[0].astype("float16")
b2 = img.array[1].astype("float16")

diff = numpy.abs(b1 - b2)
total = numpy.abs(b1) + numpy.abs(b2)

# If the difference is more than threshold, return 1. Otherwise return 0
data = (diff / total > self.threshold)

# add additional mask condition to remove cloud
if self.cloud_mask and len(img.array) > 2:
# add the third band to mask
b3 = img.array[2].astype("uint8")
valid_mask = (b3 > self.cloud_mask_threshold)

if len(img.array) > 3:
# add the fourth band to mask if available
b4 = img.array[3].astype("uint8")
valid_mask = valid_mask | (b4 > self.cloud_mask_threshold)
arr = numpy.ma.masked_array(data, dtype=self.output_dtype, mask=valid_mask)
else:
arr = numpy.ma.masked_array(data, dtype=self.output_dtype)

bnames = img.band_names
b1 = img.array[0]
b2 = img.array[1]
b2 = b2 / b2.max()
b1 = b1 / b1.max()
valid_mask = (img.array[2].astype('uint8') > self.cloud_mask_value) | (img.array[3].astype('uint8') > self.cloud_mask_value)
diff = b2-b1
data = diff
v = .1
#v = data.ptp()*.1

datam = (data > -v) & (data < v)

if self.only_negative:
datam |= data>0


arr = numpy.ma.masked_array(data*100, dtype=self.output_dtype, mask=valid_mask | datam )

word = 'changes' if not self.only_negative else 'decrease'

return ImageData(
arr,
assets=img.assets,
crs=img.crs,
bounds=img.bounds,
band_names=[f"(abs({bnames[1]} - {bnames[0]}) / ({bnames[1]} + {bnames[0]})) > {self.threshold}"],
band_names=[f"Relative {word} in pixels value"],
)

0 comments on commit 88e735d

Please sign in to comment.