ale commited on
Commit
2c7d9a9
1 Parent(s): 4ddfa1a

[feat] use post body as input

Browse files
.gitignore CHANGED
@@ -3,4 +3,5 @@ venv/
3
  __cache__
4
  .idea
5
  tmp/
6
- .env*
 
 
3
  __cache__
4
  .idea
5
  tmp/
6
+ .env*
7
+ *.onnx
dockerfiles/dockerfile-lambda-fastsam-api CHANGED
@@ -21,6 +21,7 @@ RUN ls -l ${LAMBDA_TASK_ROOT}/models
21
  RUN python -c "import sys; print(sys.path)"
22
  RUN python -c "import osgeo"
23
  RUN python -c "import cv2"
 
24
  RUN python -c "import onnxruntime"
25
  # RUN python -c "import rasterio"
26
  RUN python -c "import awslambdaric"
 
21
  RUN python -c "import sys; print(sys.path)"
22
  RUN python -c "import osgeo"
23
  RUN python -c "import cv2"
24
+ RUN python -c "import geopandas"
25
  RUN python -c "import onnxruntime"
26
  # RUN python -c "import rasterio"
27
  RUN python -c "import awslambdaric"
dockerfiles/dockerfile-lambda-gdal-runner CHANGED
@@ -17,6 +17,7 @@ RUN ls -ld /usr/lib/*linux-gnu/libGL.so* || echo "libGL.so* not found..."
17
  RUN which python
18
  RUN python --version
19
  RUN python -m pip install -r ${LAMBDA_TASK_ROOT}/requirements_dev.txt --target ${LAMBDA_TASK_ROOT}
 
20
  # RUN python -m pip install pillow awslambdaric aws-lambda-powertools httpx jmespath --target ${LAMBDA_TASK_ROOT}
21
 
22
  RUN curl -Lo /usr/local/bin/aws-lambda-rie ${RIE}
 
17
  RUN which python
18
  RUN python --version
19
  RUN python -m pip install -r ${LAMBDA_TASK_ROOT}/requirements_dev.txt --target ${LAMBDA_TASK_ROOT}
20
+ RUN python -c "import sys;print(sys.path)"
21
  # RUN python -m pip install pillow awslambdaric aws-lambda-powertools httpx jmespath --target ${LAMBDA_TASK_ROOT}
22
 
23
  RUN curl -Lo /usr/local/bin/aws-lambda-rie ${RIE}
events/payload_point.json ADDED
@@ -0,0 +1,11 @@
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "ne": {"lat": 45.699, "lng": 127.1},
3
+ "sw": {"lat": 30.1, "lng": 148.492},
4
+ "prompt": [{
5
+ "type": "point",
6
+ "data": [500, 600],
7
+ "label": 0
8
+ }],
9
+ "zoom": 6,
10
+ "source_type": "Satellite"
11
+ }
events/payload_point_colico.json ADDED
@@ -0,0 +1,11 @@
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "ne": {"lat": 46.1618799417681, "lng": 9.43905830383301},
3
+ "sw": {"lat": 46.12584245997462, "lng": 9.344301223754885},
4
+ "prompt": [{
5
+ "type": "point",
6
+ "data": [500, 600],
7
+ "label": 0
8
+ }],
9
+ "zoom": 14,
10
+ "source_type": "Satellite"
11
+ }
events/payload_rectangle.json ADDED
@@ -0,0 +1,10 @@
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "ne": {"lat": 45.699, "lng": 127.1},
3
+ "sw": {"lat": 30.1, "lng": 148.492},
4
+ "prompt": [{
5
+ "type": "rectangle",
6
+ "data": [400, 460, 524, 628]
7
+ }],
8
+ "zoom": 6,
9
+ "source_type": "Satellite"
10
+ }
requirements.txt CHANGED
@@ -1,11 +1,11 @@
1
  aws-lambda-powertools
2
  awslambdaric
3
  bson
 
4
  httpx
5
  jmespath
6
  numpy
7
  onnxruntime
8
  opencv-python
9
  pillow
10
- rasterio
11
- geopandas
 
1
  aws-lambda-powertools
2
  awslambdaric
3
  bson
4
+ geopandas
5
  httpx
6
  jmespath
7
  numpy
8
  onnxruntime
9
  opencv-python
10
  pillow
11
+ rasterio
 
requirements_dev.txt CHANGED
@@ -1,11 +1,11 @@
1
  aws-lambda-powertools
2
  awslambdaric
3
  bson
 
4
  httpx
5
  jmespath
6
  numpy
7
  onnxruntime
8
  opencv-python
9
  pillow
10
- rasterio
11
- geopandas
 
1
  aws-lambda-powertools
2
  awslambdaric
3
  bson
4
+ geopandas
5
  httpx
6
  jmespath
7
  numpy
8
  onnxruntime
9
  opencv-python
10
  pillow
11
+ rasterio
 
src/app.py CHANGED
@@ -2,12 +2,14 @@ import json
2
  import time
3
  from http import HTTPStatus
4
  from typing import Dict
 
5
  from aws_lambda_powertools.event_handler import content_types
6
  from aws_lambda_powertools.utilities.typing import LambdaContext
7
 
8
  from src import app_logger
9
  from src.prediction_api.predictors import samexporter_predict
10
  from src.utilities.constants import CUSTOM_RESPONSE_MESSAGES
 
11
 
12
 
13
  def get_response(status: int, start_time: float, request_id: str, response_body: Dict = None) -> str:
@@ -39,6 +41,24 @@ def get_response(status: int, start_time: float, request_id: str, response_body:
39
  return json.dumps(response)
40
 
41
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
42
  def lambda_handler(event: dict, context: LambdaContext):
43
  app_logger.info(f"start with aws_request_id:{context.aws_request_id}.")
44
  start_time = time.time()
@@ -51,19 +71,30 @@ def lambda_handler(event: dict, context: LambdaContext):
51
  app_logger.info(f"context:{context}...")
52
 
53
  try:
54
- pt0 = 45.699, 127.1
55
- pt1 = 30.1, 148.492
56
- bbox = [pt0, pt1]
57
- zoom = 6
58
- prompt = [{"type": "rectangle", "data": [400, 460, 524, 628]}]
59
- body_response = {"geojson": samexporter_predict(bbox, prompt, zoom)}
 
 
 
 
 
 
 
 
 
 
 
60
  app_logger.info(f"body_response::output:{body_response}.")
61
  response = get_response(HTTPStatus.OK.value, start_time, context.aws_request_id, body_response)
62
- except Exception as ve:
63
- app_logger.error(f"validation error:{ve}.")
64
  response = get_response(HTTPStatus.UNPROCESSABLE_ENTITY.value, start_time, context.aws_request_id, {})
65
- except Exception as e:
66
- app_logger.error(f"exception:{e}.")
67
  response = get_response(HTTPStatus.INTERNAL_SERVER_ERROR.value, start_time, context.aws_request_id, {})
68
 
69
  app_logger.info(f"response_dumped:{response}...")
 
2
  import time
3
  from http import HTTPStatus
4
  from typing import Dict
5
+
6
  from aws_lambda_powertools.event_handler import content_types
7
  from aws_lambda_powertools.utilities.typing import LambdaContext
8
 
9
  from src import app_logger
10
  from src.prediction_api.predictors import samexporter_predict
11
  from src.utilities.constants import CUSTOM_RESPONSE_MESSAGES
12
+ from src.utilities.utilities import base64_decode
13
 
14
 
15
  def get_response(status: int, start_time: float, request_id: str, response_body: Dict = None) -> str:
 
41
  return json.dumps(response)
42
 
43
 
44
+ def get_parsed_bbox_points(request_input: Dict) -> Dict:
45
+ app_logger.info(f"try to parsing input request {request_input}...")
46
+ ne = request_input["ne"]
47
+ sw = request_input["sw"]
48
+ bbox = [
49
+ [float(ne["lat"]), float(ne["lng"])],
50
+ [float(sw["lat"]), float(sw["lng"])]
51
+ ]
52
+ app_logger.info(f"bbox {bbox}.")
53
+
54
+ app_logger.info(f"unpacking {request_input}...")
55
+ return {
56
+ "bbox": bbox,
57
+ "prompt": request_input["prompt"],
58
+ "zoom": int(request_input["zoom"])
59
+ }
60
+
61
+
62
  def lambda_handler(event: dict, context: LambdaContext):
63
  app_logger.info(f"start with aws_request_id:{context.aws_request_id}.")
64
  start_time = time.time()
 
71
  app_logger.info(f"context:{context}...")
72
 
73
  try:
74
+ body = event["body"]
75
+ except Exception as e_constants1:
76
+ app_logger.error(f"e_constants1:{e_constants1}.")
77
+ body = event
78
+
79
+ app_logger.info(f"body: {type(body)}, {body}...")
80
+
81
+ if isinstance(body, str):
82
+ body_decoded_str = base64_decode(body)
83
+ app_logger.info(f"body_decoded_str: {type(body_decoded_str)}, {body_decoded_str}...")
84
+ body = json.loads(body_decoded_str)
85
+
86
+ app_logger.info(f"body:{body}...")
87
+
88
+ try:
89
+ body_request = get_parsed_bbox_points(body)
90
+ body_response = samexporter_predict(body_request["bbox"], body_request["prompt"], body_request["zoom"])
91
  app_logger.info(f"body_response::output:{body_response}.")
92
  response = get_response(HTTPStatus.OK.value, start_time, context.aws_request_id, body_response)
93
+ except Exception as ex2:
94
+ app_logger.error(f"exception2:{ex2}.")
95
  response = get_response(HTTPStatus.UNPROCESSABLE_ENTITY.value, start_time, context.aws_request_id, {})
96
+ except Exception as ex1:
97
+ app_logger.error(f"exception1:{ex1}.")
98
  response = get_response(HTTPStatus.INTERNAL_SERVER_ERROR.value, start_time, context.aws_request_id, {})
99
 
100
  app_logger.info(f"response_dumped:{response}...")
src/prediction_api/predictors.py CHANGED
@@ -1,30 +1,35 @@
1
  # Press the green button in the gutter to run the script.
2
- import os
3
- from typing import List
4
-
5
  import numpy as np
 
6
 
7
  from src import app_logger, MODEL_FOLDER
8
  from src.io.tms2geotiff import download_extent
9
  from src.prediction_api.sam_onnx import SegmentAnythingONNX
10
- from src.utilities.constants import ROOT, MODEL_ENCODER_NAME, ZOOM, SOURCE_TYPE, DEFAULT_TMS, MODEL_DECODER_NAME
11
  from src.utilities.serialize import serialize
12
  from src.utilities.type_hints import input_float_tuples
13
 
14
 
 
 
 
15
  def zip_arrays(arr1, arr2):
16
- arr1_list = arr1.tolist()
17
- arr2_list = arr2.tolist()
18
- # return {serialize(k): serialize(v) for k, v in zip(arr1_list, arr2_list)}
19
- d = {}
20
- for n1, n2 in zip(arr1_list, arr2_list):
21
- app_logger.info(f"n1:{n1}, type {type(n1)}, n2:{n2}, type {type(n2)}.")
22
- n1f = str(n1)
23
- n2f = str(n2)
24
- app_logger.info(f"n1:{n1}=>{n1f}, n2:{n2}=>{n2f}.")
25
- d[n1f] = n2f
26
- app_logger.info(f"zipped dict:{d}.")
27
- return d
 
 
 
 
28
 
29
 
30
  def load_affine_transformation_from_matrix(matrix_source_coeffs: List):
@@ -41,68 +46,71 @@ def load_affine_transformation_from_matrix(matrix_source_coeffs: List):
41
  app_logger.error(f"exception:{e}, check https://github.com/rasterio/affine project for updates")
42
 
43
 
44
- def samexporter_predict(bbox: input_float_tuples, prompt: list[dict], zoom: float = ZOOM) -> dict:
45
- import tempfile
46
-
47
  try:
48
  from rasterio.features import shapes
49
  from geopandas import GeoDataFrame
50
 
51
- with tempfile.NamedTemporaryFile(prefix=f"{SOURCE_TYPE}_", suffix=".tif", dir=ROOT) as image_input_tmp:
52
- for coord in bbox:
53
- app_logger.info(f"bbox coord:{coord}, type:{type(coord)}.")
54
- app_logger.info(f"start download_extent using bbox:{bbox}, type:{type(bbox)}, download image...")
55
-
56
- pt0 = bbox[0]
57
- pt1 = bbox[1]
58
- img, matrix = download_extent(DEFAULT_TMS, pt0[0], pt0[1], pt1[0], pt1[1], zoom)
59
-
60
- app_logger.info(f"img type {type(img)}, matrix type {type(matrix)}.")
61
- app_logger.info(f"matrix values: {serialize(matrix)}.")
62
- np_img = np.array(img)
63
- app_logger.info(f"np_img type {type(np_img)}.")
64
- app_logger.info(f"np_img dtype {np_img.dtype}, shape {np_img.shape}.")
65
- app_logger.info(f"geotiff created with size/shape {img.size} and transform matrix {str(matrix)}, start to initialize SamGeo instance:")
66
- app_logger.info(f"use ENCODER model {MODEL_ENCODER_NAME} from {MODEL_FOLDER})...")
67
- app_logger.info(f"use DECODER model {MODEL_DECODER_NAME} from {MODEL_FOLDER})...")
68
-
69
- model = SegmentAnythingONNX(
70
  encoder_model_path=MODEL_FOLDER / MODEL_ENCODER_NAME,
71
  decoder_model_path=MODEL_FOLDER / MODEL_DECODER_NAME
72
  )
73
- app_logger.info(f"model instantiated, creating embedding...")
74
- embedding = model.encode(np_img)
75
- app_logger.info(f"embedding created, running predict_masks...")
76
- prediction_masks = model.predict_masks(embedding, prompt)
77
- app_logger.info(f"predict_masks terminated")
78
- app_logger.info(f"prediction masks shape:{prediction_masks.shape}, {prediction_masks.dtype}.")
79
-
80
- mask = np.zeros((prediction_masks.shape[2], prediction_masks.shape[3]), dtype=np.uint8)
81
- for m in prediction_masks[0, :, :, :]:
82
- mask[m > 0.0] = 255
83
-
84
- mask_unique_values, mask_unique_values_count = serialize(np.unique(mask, return_counts=True))
85
- app_logger.info(f"mask_unique_values:{mask_unique_values}.")
86
- app_logger.info(f"mask_unique_values_count:{mask_unique_values_count}.")
87
-
88
- transform = load_affine_transformation_from_matrix(matrix)
89
- app_logger.info(f"image/geojson origin matrix:{matrix}, transform:{transform}.")
90
- shapes_generator = (
91
- {'properties': {'raster_val': v}, 'geometry': s}
92
- for i, (s, v)
93
- in enumerate(
94
- shapes(mask, mask=mask, transform=transform))
95
- )
96
- shapes_list = list(shapes_generator)
97
- app_logger.info(f"created {len(shapes_list)} polygons.")
98
- gpd_polygonized_raster = GeoDataFrame.from_features(shapes_list, crs="EPSG:3857")
99
- geojson = gpd_polygonized_raster.to_json(to_wgs84=True)
100
-
101
- return {
102
- "geojson": geojson,
103
- "n_shapes_geojson": len(shapes_list),
104
- "n_predictions": len(prediction_masks),
105
- "n_pixels_predictions": zip_arrays(mask_unique_values, mask_unique_values_count),
106
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
107
  except ImportError as e:
108
  app_logger.error(f"Error trying import module:{e}.")
 
1
  # Press the green button in the gutter to run the script.
 
 
 
2
  import numpy as np
3
+ from typing import List
4
 
5
  from src import app_logger, MODEL_FOLDER
6
  from src.io.tms2geotiff import download_extent
7
  from src.prediction_api.sam_onnx import SegmentAnythingONNX
8
+ from src.utilities.constants import MODEL_ENCODER_NAME, ZOOM, DEFAULT_TMS, MODEL_DECODER_NAME
9
  from src.utilities.serialize import serialize
10
  from src.utilities.type_hints import input_float_tuples
11
 
12
 
13
+ models_dict = {"fastsam": {"instance": None}}
14
+
15
+
16
  def zip_arrays(arr1, arr2):
17
+ try:
18
+ arr1_list = arr1.tolist()
19
+ arr2_list = arr2.tolist()
20
+ # return {serialize(k): serialize(v) for k, v in zip(arr1_list, arr2_list)}
21
+ d = {}
22
+ for n1, n2 in zip(arr1_list, arr2_list):
23
+ app_logger.info(f"n1:{n1}, type {type(n1)}, n2:{n2}, type {type(n2)}.")
24
+ n1f = str(n1)
25
+ n2f = str(n2)
26
+ app_logger.info(f"n1:{n1}=>{n1f}, n2:{n2}=>{n2f}.")
27
+ d[n1f] = n2f
28
+ app_logger.info(f"zipped dict:{d}.")
29
+ return d
30
+ except Exception as e_zip_arrays:
31
+ app_logger.info(f"exception zip_arrays:{e_zip_arrays}.")
32
+ return {}
33
 
34
 
35
  def load_affine_transformation_from_matrix(matrix_source_coeffs: List):
 
46
  app_logger.error(f"exception:{e}, check https://github.com/rasterio/affine project for updates")
47
 
48
 
49
+ def samexporter_predict(bbox: input_float_tuples, prompt: list[dict], zoom: float = ZOOM, model_name: str = "fastsam") -> dict:
 
 
50
  try:
51
  from rasterio.features import shapes
52
  from geopandas import GeoDataFrame
53
 
54
+ if models_dict[model_name]["instance"] is None:
55
+ app_logger.info(f"missing instance model {model_name}, instantiating it now")
56
+ model_instance = SegmentAnythingONNX(
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
57
  encoder_model_path=MODEL_FOLDER / MODEL_ENCODER_NAME,
58
  decoder_model_path=MODEL_FOLDER / MODEL_DECODER_NAME
59
  )
60
+ models_dict[model_name]["instance"] = model_instance
61
+ app_logger.info(f"using a {model_name} instance model...")
62
+ models_instance = models_dict[model_name]["instance"]
63
+
64
+ for coord in bbox:
65
+ app_logger.info(f"bbox coord:{coord}, type:{type(coord)}.")
66
+ app_logger.info(f"start download_extent using bbox:{bbox}, type:{type(bbox)}, download image...")
67
+
68
+ pt0 = bbox[0]
69
+ pt1 = bbox[1]
70
+ img, matrix = download_extent(DEFAULT_TMS, pt0[0], pt0[1], pt1[0], pt1[1], zoom)
71
+
72
+ app_logger.info(f"img type {type(img)}, matrix type {type(matrix)}.")
73
+ app_logger.info(f"matrix values: {serialize(matrix)}.")
74
+ np_img = np.array(img)
75
+ app_logger.info(f"np_img type {type(np_img)}.")
76
+ app_logger.info(f"np_img dtype {np_img.dtype}, shape {np_img.shape}.")
77
+ app_logger.info(f"geotiff created with size/shape {img.size} and transform matrix {str(matrix)}, start to initialize SamGeo instance:")
78
+ app_logger.info(f"use fastsam_model, ENCODER model {MODEL_ENCODER_NAME} and {MODEL_DECODER_NAME} from {MODEL_FOLDER})...")
79
+
80
+ app_logger.info(f"model instantiated, creating embedding...")
81
+ embedding = models_instance.encode(np_img)
82
+ app_logger.info(f"embedding created, running predict_masks...")
83
+ prediction_masks = models_instance.predict_masks(embedding, prompt)
84
+ app_logger.info(f"predict_masks terminated")
85
+ app_logger.info(f"prediction masks shape:{prediction_masks.shape}, {prediction_masks.dtype}.")
86
+
87
+ mask = np.zeros((prediction_masks.shape[2], prediction_masks.shape[3]), dtype=np.uint8)
88
+ for m in prediction_masks[0, :, :, :]:
89
+ mask[m > 0.0] = 255
90
+
91
+ mask_unique_values, mask_unique_values_count = serialize(np.unique(mask, return_counts=True))
92
+ app_logger.info(f"mask_unique_values:{mask_unique_values}.")
93
+ app_logger.info(f"mask_unique_values_count:{mask_unique_values_count}.")
94
+
95
+ transform = load_affine_transformation_from_matrix(matrix)
96
+ app_logger.info(f"image/geojson origin matrix:{matrix}, transform:{transform}.")
97
+ shapes_generator = ({
98
+ 'properties': {'raster_val': v}, 'geometry': s}
99
+ for i, (s, v)
100
+ in enumerate(shapes(mask, mask=mask, transform=transform))
101
+ )
102
+ shapes_list = list(shapes_generator)
103
+ app_logger.info(f"created {len(shapes_list)} polygons.")
104
+ gpd_polygonized_raster = GeoDataFrame.from_features(shapes_list, crs="EPSG:3857")
105
+ app_logger.info(f"created a GeoDataFrame...")
106
+ geojson = gpd_polygonized_raster.to_json(to_wgs84=True)
107
+ app_logger.info(f"created geojson...")
108
+
109
+ return {
110
+ "geojson": geojson,
111
+ "n_shapes_geojson": len(shapes_list),
112
+ "n_predictions": len(prediction_masks),
113
+ # "n_pixels_predictions": zip_arrays(mask_unique_values, mask_unique_values_count),
114
+ }
115
  except ImportError as e:
116
  app_logger.error(f"Error trying import module:{e}.")