orient_express 2.4.0__tar.gz → 2.4.1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {orient_express-2.4.0 → orient_express-2.4.1}/PKG-INFO +186 -1
- {orient_express-2.4.0 → orient_express-2.4.1}/README.md +185 -0
- {orient_express-2.4.0 → orient_express-2.4.1}/orient_express/predictors/predictor.py +1 -1
- {orient_express-2.4.0 → orient_express-2.4.1}/orient_express/predictors/semantic_segmentation.py +15 -6
- {orient_express-2.4.0 → orient_express-2.4.1}/orient_express/utils/image_processor.py +6 -0
- {orient_express-2.4.0 → orient_express-2.4.1}/pyproject.toml +4 -1
- {orient_express-2.4.0 → orient_express-2.4.1}/orient_express/__init__.py +0 -0
- {orient_express-2.4.0 → orient_express-2.4.1}/orient_express/deployment.py +0 -0
- {orient_express-2.4.0 → orient_express-2.4.1}/orient_express/model_wrapper.py +0 -0
- {orient_express-2.4.0 → orient_express-2.4.1}/orient_express/predictors/__init__.py +0 -0
- {orient_express-2.4.0 → orient_express-2.4.1}/orient_express/predictors/classification.py +0 -0
- {orient_express-2.4.0 → orient_express-2.4.1}/orient_express/predictors/feature_extraction.py +0 -0
- {orient_express-2.4.0 → orient_express-2.4.1}/orient_express/predictors/instance_segmentation.py +0 -0
- {orient_express-2.4.0 → orient_express-2.4.1}/orient_express/predictors/multi_label_classification.py +0 -0
- {orient_express-2.4.0 → orient_express-2.4.1}/orient_express/predictors/object_detection.py +0 -0
- {orient_express-2.4.0 → orient_express-2.4.1}/orient_express/predictors/vector_index.py +0 -0
- {orient_express-2.4.0 → orient_express-2.4.1}/orient_express/sklearn_pipeline.py +0 -0
- {orient_express-2.4.0 → orient_express-2.4.1}/orient_express/utils/colors.py +0 -0
- {orient_express-2.4.0 → orient_express-2.4.1}/orient_express/utils/gs.py +0 -0
- {orient_express-2.4.0 → orient_express-2.4.1}/orient_express/utils/paths.py +0 -0
- {orient_express-2.4.0 → orient_express-2.4.1}/orient_express/utils/retry.py +0 -0
- {orient_express-2.4.0 → orient_express-2.4.1}/orient_express/vertex.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: orient_express
|
|
3
|
-
Version: 2.4.
|
|
3
|
+
Version: 2.4.1
|
|
4
4
|
Summary: A library to simplify model deployment to Vertex AI
|
|
5
5
|
Author: Alexey Zankevich
|
|
6
6
|
Author-email: alex.zankevich@shiftsmart.com
|
|
@@ -593,6 +593,191 @@ class SearchResult:
|
|
|
593
593
|
|
|
594
594
|
---
|
|
595
595
|
|
|
596
|
+
## Deployed Endpoint APIs
|
|
597
|
+
|
|
598
|
+
When you upload a model with orient-express and deploy it to a Vertex AI endpoint, the actual HTTP API exposed by the endpoint is determined by the serving container image — not by your Python predictor code. Orient-express ships two such images:
|
|
599
|
+
|
|
600
|
+
- `image-onnx` — serves the built-in ONNX predictor types (classification, detection, segmentation).
|
|
601
|
+
- `xgboost-scikit-learn` — serves any joblib-loadable model (sklearn pipelines, xgboost, etc.).
|
|
602
|
+
|
|
603
|
+
This section documents the request/response shape each image's endpoint exposes once deployed.
|
|
604
|
+
|
|
605
|
+
### How the docker images connect to GCP endpoints
|
|
606
|
+
|
|
607
|
+
The deployment flow is:
|
|
608
|
+
|
|
609
|
+
1. **Train + export.** You build a predictor locally (e.g. `ClassificationPredictor("model.onnx", classes)`) or train a sklearn/xgboost model.
|
|
610
|
+
2. **Upload.** `upload_model` / `upload_model_joblib` pushes the artifacts to GCS under `gs://<bucket>/models/<model_name>/<version>/` and registers a Vertex AI Model with `serving_container_image_uri` pointing at one of orient-express's images.
|
|
611
|
+
3. **Deploy.** `vertex_model.deploy_to_endpoint(...)` (or the Vertex console) attaches the registered model to a Vertex AI Endpoint. Vertex starts the container with `AIP_STORAGE_URI` set to the GCS path from step 2, plus `MODEL_NAME` set to the model's display name.
|
|
612
|
+
4. **Serve.** The container downloads the artifacts on startup, instantiates the right predictor via metadata, and listens on `/v1/models/<MODEL_NAME>:predict`.
|
|
613
|
+
5. **Call.** Clients POST to `https://<region>-aiplatform.googleapis.com/v1/projects/<project>/locations/<region>/endpoints/<endpoint_id>:predict` with a Bearer token. Vertex routes the request into the container and returns the JSON response.
|
|
614
|
+
|
|
615
|
+
Every endpoint accepts the same envelope:
|
|
616
|
+
|
|
617
|
+
```json
|
|
618
|
+
{
|
|
619
|
+
"instances": [...],
|
|
620
|
+
"parameters": {...}
|
|
621
|
+
}
|
|
622
|
+
```
|
|
623
|
+
|
|
624
|
+
What goes in `instances` / `parameters`, and what comes back in `predictions`, is per-image and per-predictor — covered below.
|
|
625
|
+
|
|
626
|
+
### ONNX Image Endpoint
|
|
627
|
+
|
|
628
|
+
Image: `us-west1-docker.pkg.dev/shiftsmart-api/orient-express/image-onnx:<tag>`
|
|
629
|
+
|
|
630
|
+
Common request shape across all ONNX predictor types:
|
|
631
|
+
|
|
632
|
+
```json
|
|
633
|
+
{
|
|
634
|
+
"instances": [
|
|
635
|
+
{"image": "<http(s) URL | gs:// URI | base64 | data URI>"}
|
|
636
|
+
],
|
|
637
|
+
"parameters": {
|
|
638
|
+
"confidence": 0.5
|
|
639
|
+
}
|
|
640
|
+
}
|
|
641
|
+
```
|
|
642
|
+
|
|
643
|
+
Common response envelope:
|
|
644
|
+
|
|
645
|
+
```json
|
|
646
|
+
{
|
|
647
|
+
"predictions": [
|
|
648
|
+
{"status": "success", ...predictor-specific fields...}
|
|
649
|
+
]
|
|
650
|
+
}
|
|
651
|
+
```
|
|
652
|
+
|
|
653
|
+
`status` values: `"success"`, `"failed to download image"`, or `"failed to get debug image"`. Malformed payloads return top-level `{"error": "Failed to decode input"}` instead of `predictions`.
|
|
654
|
+
|
|
655
|
+
The predictor-specific fields differ by model type — covered in the subsections below.
|
|
656
|
+
|
|
657
|
+
#### Classification
|
|
658
|
+
|
|
659
|
+
For models uploaded as `ClassificationPredictor`. `parameters.confidence` is **not** honored (the predictor always returns the top class).
|
|
660
|
+
|
|
661
|
+
Per-image response:
|
|
662
|
+
|
|
663
|
+
```json
|
|
664
|
+
{
|
|
665
|
+
"status": "success",
|
|
666
|
+
"class": "cat",
|
|
667
|
+
"score": 0.95,
|
|
668
|
+
"class_scores": {"cat": 0.95, "dog": 0.03, "bird": 0.02}
|
|
669
|
+
}
|
|
670
|
+
```
|
|
671
|
+
|
|
672
|
+
No `debug_image` for classification (nothing meaningful to draw).
|
|
673
|
+
|
|
674
|
+
#### Multi-label classification
|
|
675
|
+
|
|
676
|
+
For models uploaded as `MultiLabelClassificationPredictor`. `parameters.confidence` is the per-class threshold for inclusion in `classes` (default `0.5`).
|
|
677
|
+
|
|
678
|
+
Per-image response:
|
|
679
|
+
|
|
680
|
+
```json
|
|
681
|
+
{
|
|
682
|
+
"status": "success",
|
|
683
|
+
"predictions": {
|
|
684
|
+
"classes": ["contains_cat", "contains_bird"],
|
|
685
|
+
"class_scores": {"contains_cat": 0.95, "contains_dog": 0.03, "contains_bird": 0.82}
|
|
686
|
+
},
|
|
687
|
+
"debug_image": null
|
|
688
|
+
}
|
|
689
|
+
```
|
|
690
|
+
|
|
691
|
+
`debug_image` is always `null` for multi-label.
|
|
692
|
+
|
|
693
|
+
#### Object detection
|
|
694
|
+
|
|
695
|
+
For models uploaded as `BoundingBoxPredictor`. `parameters.confidence` filters detections below the threshold (default `0.5`).
|
|
696
|
+
|
|
697
|
+
Per-image response:
|
|
698
|
+
|
|
699
|
+
```json
|
|
700
|
+
{
|
|
701
|
+
"status": "success",
|
|
702
|
+
"predictions": [
|
|
703
|
+
{"class": "person", "score": 0.92, "bbox": {"x1": 100.5, "y1": 50.2, "x2": 300.8, "y2": 400.1}}
|
|
704
|
+
],
|
|
705
|
+
"debug_image": "<base64 JPEG with boxes overlaid>"
|
|
706
|
+
}
|
|
707
|
+
```
|
|
708
|
+
|
|
709
|
+
`bbox` coordinates are in pixels of the original (EXIF-corrected) image. `predictions` is an empty list when nothing clears the confidence threshold.
|
|
710
|
+
|
|
711
|
+
#### Instance segmentation
|
|
712
|
+
|
|
713
|
+
For models uploaded as `InstanceSegmentationPredictor`. `parameters.confidence` filters detections (default `0.5`).
|
|
714
|
+
|
|
715
|
+
Per-image response:
|
|
716
|
+
|
|
717
|
+
```json
|
|
718
|
+
{
|
|
719
|
+
"status": "success",
|
|
720
|
+
"predictions": [
|
|
721
|
+
{"class": "person", "score": 0.89, "bbox": {"x1": 100.5, "y1": 50.2, "x2": 300.8, "y2": 400.1}}
|
|
722
|
+
],
|
|
723
|
+
"debug_image": "<base64 JPEG with masks overlaid>"
|
|
724
|
+
}
|
|
725
|
+
```
|
|
726
|
+
|
|
727
|
+
Per-instance mask arrays are **not** included in the response by default (too large). The annotated mask overlay is baked into `debug_image`.
|
|
728
|
+
|
|
729
|
+
#### Semantic segmentation
|
|
730
|
+
|
|
731
|
+
For models uploaded as `SemanticSegmentationPredictor`. `parameters.confidence` is the per-pixel threshold above which a class is considered "valid" (default `0.5`).
|
|
732
|
+
|
|
733
|
+
Per-image response:
|
|
734
|
+
|
|
735
|
+
```json
|
|
736
|
+
{
|
|
737
|
+
"status": "success",
|
|
738
|
+
"predictions": {
|
|
739
|
+
"class_mask": "<base64 PNG, uint8, per-pixel class id>",
|
|
740
|
+
"valid_mask": "<base64 PNG, uint8, 0=below threshold, 1=above>"
|
|
741
|
+
},
|
|
742
|
+
"debug_image": "<base64 JPEG with color-coded overlay>"
|
|
743
|
+
}
|
|
744
|
+
```
|
|
745
|
+
|
|
746
|
+
`class_mask` always paints every pixel with the argmax winner. `valid_mask` tells you which pixels actually cleared the confidence threshold — AND them together client-side to get the "real" segmentation.
|
|
747
|
+
|
|
748
|
+
### XGBoost / scikit-learn Endpoint
|
|
749
|
+
|
|
750
|
+
Image: `us-west1-docker.pkg.dev/shiftsmart-api/orient-express/xgboost-scikit-learn:<tag>`
|
|
751
|
+
|
|
752
|
+
For models uploaded via `upload_model_joblib` — sklearn pipelines, xgboost models, or anything `joblib.load`-able with a `.predict(DataFrame)` method.
|
|
753
|
+
|
|
754
|
+
Request shape — `instances` is a list of dicts, one row per input:
|
|
755
|
+
|
|
756
|
+
```json
|
|
757
|
+
{
|
|
758
|
+
"instances": [
|
|
759
|
+
{"pclass": 1, "sex": "female", "age": 29, "fare": 100.0, "embarked": "S"},
|
|
760
|
+
{"pclass": 3, "sex": "male", "age": 35, "fare": 8.05, "embarked": "S"}
|
|
761
|
+
]
|
|
762
|
+
}
|
|
763
|
+
```
|
|
764
|
+
|
|
765
|
+
The server constructs `pd.DataFrame(instances)` and calls `model.predict(df)` on it. The columns your pipeline expects must be present in each instance dict.
|
|
766
|
+
|
|
767
|
+
Response shape — one prediction per input row:
|
|
768
|
+
|
|
769
|
+
```json
|
|
770
|
+
{
|
|
771
|
+
"predictions": [0, 1]
|
|
772
|
+
}
|
|
773
|
+
```
|
|
774
|
+
|
|
775
|
+
Each element is whatever your `.predict()` returns — a class label for classifiers, a numeric value for regressors, an array for multi-output models.
|
|
776
|
+
|
|
777
|
+
`parameters` is ignored — there's no per-request configuration for this image.
|
|
778
|
+
|
|
779
|
+
---
|
|
780
|
+
|
|
596
781
|
## Color Schemes
|
|
597
782
|
|
|
598
783
|
For predictors that support annotation (`BoundingBoxPredictor`, `InstanceSegmentationPredictor`, `SemanticSegmentationPredictor`), you can set a custom color scheme:
|
|
@@ -566,6 +566,191 @@ class SearchResult:
|
|
|
566
566
|
|
|
567
567
|
---
|
|
568
568
|
|
|
569
|
+
## Deployed Endpoint APIs
|
|
570
|
+
|
|
571
|
+
When you upload a model with orient-express and deploy it to a Vertex AI endpoint, the actual HTTP API exposed by the endpoint is determined by the serving container image — not by your Python predictor code. Orient-express ships two such images:
|
|
572
|
+
|
|
573
|
+
- `image-onnx` — serves the built-in ONNX predictor types (classification, detection, segmentation).
|
|
574
|
+
- `xgboost-scikit-learn` — serves any joblib-loadable model (sklearn pipelines, xgboost, etc.).
|
|
575
|
+
|
|
576
|
+
This section documents the request/response shape each image's endpoint exposes once deployed.
|
|
577
|
+
|
|
578
|
+
### How the docker images connect to GCP endpoints
|
|
579
|
+
|
|
580
|
+
The deployment flow is:
|
|
581
|
+
|
|
582
|
+
1. **Train + export.** You build a predictor locally (e.g. `ClassificationPredictor("model.onnx", classes)`) or train a sklearn/xgboost model.
|
|
583
|
+
2. **Upload.** `upload_model` / `upload_model_joblib` pushes the artifacts to GCS under `gs://<bucket>/models/<model_name>/<version>/` and registers a Vertex AI Model with `serving_container_image_uri` pointing at one of orient-express's images.
|
|
584
|
+
3. **Deploy.** `vertex_model.deploy_to_endpoint(...)` (or the Vertex console) attaches the registered model to a Vertex AI Endpoint. Vertex starts the container with `AIP_STORAGE_URI` set to the GCS path from step 2, plus `MODEL_NAME` set to the model's display name.
|
|
585
|
+
4. **Serve.** The container downloads the artifacts on startup, instantiates the right predictor via metadata, and listens on `/v1/models/<MODEL_NAME>:predict`.
|
|
586
|
+
5. **Call.** Clients POST to `https://<region>-aiplatform.googleapis.com/v1/projects/<project>/locations/<region>/endpoints/<endpoint_id>:predict` with a Bearer token. Vertex routes the request into the container and returns the JSON response.
|
|
587
|
+
|
|
588
|
+
Every endpoint accepts the same envelope:
|
|
589
|
+
|
|
590
|
+
```json
|
|
591
|
+
{
|
|
592
|
+
"instances": [...],
|
|
593
|
+
"parameters": {...}
|
|
594
|
+
}
|
|
595
|
+
```
|
|
596
|
+
|
|
597
|
+
What goes in `instances` / `parameters`, and what comes back in `predictions`, is per-image and per-predictor — covered below.
|
|
598
|
+
|
|
599
|
+
### ONNX Image Endpoint
|
|
600
|
+
|
|
601
|
+
Image: `us-west1-docker.pkg.dev/shiftsmart-api/orient-express/image-onnx:<tag>`
|
|
602
|
+
|
|
603
|
+
Common request shape across all ONNX predictor types:
|
|
604
|
+
|
|
605
|
+
```json
|
|
606
|
+
{
|
|
607
|
+
"instances": [
|
|
608
|
+
{"image": "<http(s) URL | gs:// URI | base64 | data URI>"}
|
|
609
|
+
],
|
|
610
|
+
"parameters": {
|
|
611
|
+
"confidence": 0.5
|
|
612
|
+
}
|
|
613
|
+
}
|
|
614
|
+
```
|
|
615
|
+
|
|
616
|
+
Common response envelope:
|
|
617
|
+
|
|
618
|
+
```json
|
|
619
|
+
{
|
|
620
|
+
"predictions": [
|
|
621
|
+
{"status": "success", ...predictor-specific fields...}
|
|
622
|
+
]
|
|
623
|
+
}
|
|
624
|
+
```
|
|
625
|
+
|
|
626
|
+
`status` values: `"success"`, `"failed to download image"`, or `"failed to get debug image"`. Malformed payloads return top-level `{"error": "Failed to decode input"}` instead of `predictions`.
|
|
627
|
+
|
|
628
|
+
The predictor-specific fields differ by model type — covered in the subsections below.
|
|
629
|
+
|
|
630
|
+
#### Classification
|
|
631
|
+
|
|
632
|
+
For models uploaded as `ClassificationPredictor`. `parameters.confidence` is **not** honored (the predictor always returns the top class).
|
|
633
|
+
|
|
634
|
+
Per-image response:
|
|
635
|
+
|
|
636
|
+
```json
|
|
637
|
+
{
|
|
638
|
+
"status": "success",
|
|
639
|
+
"class": "cat",
|
|
640
|
+
"score": 0.95,
|
|
641
|
+
"class_scores": {"cat": 0.95, "dog": 0.03, "bird": 0.02}
|
|
642
|
+
}
|
|
643
|
+
```
|
|
644
|
+
|
|
645
|
+
No `debug_image` for classification (nothing meaningful to draw).
|
|
646
|
+
|
|
647
|
+
#### Multi-label classification
|
|
648
|
+
|
|
649
|
+
For models uploaded as `MultiLabelClassificationPredictor`. `parameters.confidence` is the per-class threshold for inclusion in `classes` (default `0.5`).
|
|
650
|
+
|
|
651
|
+
Per-image response:
|
|
652
|
+
|
|
653
|
+
```json
|
|
654
|
+
{
|
|
655
|
+
"status": "success",
|
|
656
|
+
"predictions": {
|
|
657
|
+
"classes": ["contains_cat", "contains_bird"],
|
|
658
|
+
"class_scores": {"contains_cat": 0.95, "contains_dog": 0.03, "contains_bird": 0.82}
|
|
659
|
+
},
|
|
660
|
+
"debug_image": null
|
|
661
|
+
}
|
|
662
|
+
```
|
|
663
|
+
|
|
664
|
+
`debug_image` is always `null` for multi-label.
|
|
665
|
+
|
|
666
|
+
#### Object detection
|
|
667
|
+
|
|
668
|
+
For models uploaded as `BoundingBoxPredictor`. `parameters.confidence` filters detections below the threshold (default `0.5`).
|
|
669
|
+
|
|
670
|
+
Per-image response:
|
|
671
|
+
|
|
672
|
+
```json
|
|
673
|
+
{
|
|
674
|
+
"status": "success",
|
|
675
|
+
"predictions": [
|
|
676
|
+
{"class": "person", "score": 0.92, "bbox": {"x1": 100.5, "y1": 50.2, "x2": 300.8, "y2": 400.1}}
|
|
677
|
+
],
|
|
678
|
+
"debug_image": "<base64 JPEG with boxes overlaid>"
|
|
679
|
+
}
|
|
680
|
+
```
|
|
681
|
+
|
|
682
|
+
`bbox` coordinates are in pixels of the original (EXIF-corrected) image. `predictions` is an empty list when nothing clears the confidence threshold.
|
|
683
|
+
|
|
684
|
+
#### Instance segmentation
|
|
685
|
+
|
|
686
|
+
For models uploaded as `InstanceSegmentationPredictor`. `parameters.confidence` filters detections (default `0.5`).
|
|
687
|
+
|
|
688
|
+
Per-image response:
|
|
689
|
+
|
|
690
|
+
```json
|
|
691
|
+
{
|
|
692
|
+
"status": "success",
|
|
693
|
+
"predictions": [
|
|
694
|
+
{"class": "person", "score": 0.89, "bbox": {"x1": 100.5, "y1": 50.2, "x2": 300.8, "y2": 400.1}}
|
|
695
|
+
],
|
|
696
|
+
"debug_image": "<base64 JPEG with masks overlaid>"
|
|
697
|
+
}
|
|
698
|
+
```
|
|
699
|
+
|
|
700
|
+
Per-instance mask arrays are **not** included in the response by default (too large). The annotated mask overlay is baked into `debug_image`.
|
|
701
|
+
|
|
702
|
+
#### Semantic segmentation
|
|
703
|
+
|
|
704
|
+
For models uploaded as `SemanticSegmentationPredictor`. `parameters.confidence` is the per-pixel threshold above which a class is considered "valid" (default `0.5`).
|
|
705
|
+
|
|
706
|
+
Per-image response:
|
|
707
|
+
|
|
708
|
+
```json
|
|
709
|
+
{
|
|
710
|
+
"status": "success",
|
|
711
|
+
"predictions": {
|
|
712
|
+
"class_mask": "<base64 PNG, uint8, per-pixel class id>",
|
|
713
|
+
"valid_mask": "<base64 PNG, uint8, 0=below threshold, 1=above>"
|
|
714
|
+
},
|
|
715
|
+
"debug_image": "<base64 JPEG with color-coded overlay>"
|
|
716
|
+
}
|
|
717
|
+
```
|
|
718
|
+
|
|
719
|
+
`class_mask` always paints every pixel with the argmax winner. `valid_mask` tells you which pixels actually cleared the confidence threshold — AND them together client-side to get the "real" segmentation.
|
|
720
|
+
|
|
721
|
+
### XGBoost / scikit-learn Endpoint
|
|
722
|
+
|
|
723
|
+
Image: `us-west1-docker.pkg.dev/shiftsmart-api/orient-express/xgboost-scikit-learn:<tag>`
|
|
724
|
+
|
|
725
|
+
For models uploaded via `upload_model_joblib` — sklearn pipelines, xgboost models, or anything `joblib.load`-able with a `.predict(DataFrame)` method.
|
|
726
|
+
|
|
727
|
+
Request shape — `instances` is a list of dicts, one row per input:
|
|
728
|
+
|
|
729
|
+
```json
|
|
730
|
+
{
|
|
731
|
+
"instances": [
|
|
732
|
+
{"pclass": 1, "sex": "female", "age": 29, "fare": 100.0, "embarked": "S"},
|
|
733
|
+
{"pclass": 3, "sex": "male", "age": 35, "fare": 8.05, "embarked": "S"}
|
|
734
|
+
]
|
|
735
|
+
}
|
|
736
|
+
```
|
|
737
|
+
|
|
738
|
+
The server constructs `pd.DataFrame(instances)` and calls `model.predict(df)` on it. The columns your pipeline expects must be present in each instance dict.
|
|
739
|
+
|
|
740
|
+
Response shape — one prediction per input row:
|
|
741
|
+
|
|
742
|
+
```json
|
|
743
|
+
{
|
|
744
|
+
"predictions": [0, 1]
|
|
745
|
+
}
|
|
746
|
+
```
|
|
747
|
+
|
|
748
|
+
Each element is whatever your `.predict()` returns — a class label for classifiers, a numeric value for regressors, an array for multi-output models.
|
|
749
|
+
|
|
750
|
+
`parameters` is ignored — there's no per-request configuration for this image.
|
|
751
|
+
|
|
752
|
+
---
|
|
753
|
+
|
|
569
754
|
## Color Schemes
|
|
570
755
|
|
|
571
756
|
For predictors that support annotation (`BoundingBoxPredictor`, `InstanceSegmentationPredictor`, `SemanticSegmentationPredictor`), you can set a custom color scheme:
|
|
@@ -46,7 +46,7 @@ class ImagePredictor(Predictor):
|
|
|
46
46
|
self.model_path = model_path
|
|
47
47
|
|
|
48
48
|
def get_serving_container_image_uri(self):
|
|
49
|
-
return "us-west1-docker.pkg.dev/shiftsmart-api/orient-express/image-onnx:v2.4.
|
|
49
|
+
return "us-west1-docker.pkg.dev/shiftsmart-api/orient-express/image-onnx:v2.4.1"
|
|
50
50
|
|
|
51
51
|
def get_serving_container_health_route(self, model_name):
|
|
52
52
|
return f"/v1/models/{model_name}"
|
{orient_express-2.4.0 → orient_express-2.4.1}/orient_express/predictors/semantic_segmentation.py
RENAMED
|
@@ -7,17 +7,19 @@ import torch
|
|
|
7
7
|
import torch.nn.functional as F
|
|
8
8
|
|
|
9
9
|
from .predictor import OnnxSessionWrapper, ImagePredictor
|
|
10
|
-
from ..utils.image_processor import pil_to_opencv, opencv_to_pil
|
|
10
|
+
from ..utils.image_processor import mask_to_base64, pil_to_opencv, opencv_to_pil
|
|
11
11
|
|
|
12
12
|
|
|
13
13
|
@dataclass
|
|
14
14
|
class SemanticSegmentationPrediction:
|
|
15
15
|
class_mask: np.ndarray
|
|
16
|
+
valid_mask: np.ndarray
|
|
16
17
|
conf_masks: np.ndarray
|
|
17
18
|
|
|
18
19
|
def to_dict(self, include_conf_masks: bool = False):
|
|
19
20
|
dict_repr = {
|
|
20
|
-
"class_mask": self.class_mask,
|
|
21
|
+
"class_mask": mask_to_base64(self.class_mask.astype(np.uint8)),
|
|
22
|
+
"valid_mask": mask_to_base64(self.valid_mask.astype(np.uint8)),
|
|
21
23
|
}
|
|
22
24
|
if include_conf_masks:
|
|
23
25
|
dict_repr["conf_masks"] = self.conf_masks.tolist()
|
|
@@ -60,31 +62,38 @@ class SemanticSegmentationPredictor(ImagePredictor):
|
|
|
60
62
|
backend_model = OnnxSemanticSegmentation
|
|
61
63
|
|
|
62
64
|
def predict(
|
|
63
|
-
self, images: list[Image.Image]
|
|
65
|
+
self, images: list[Image.Image], confidence: float = 0.5
|
|
64
66
|
) -> list[SemanticSegmentationPrediction]:
|
|
65
67
|
if not images:
|
|
66
68
|
return []
|
|
67
69
|
raw_outputs = self.model(images)
|
|
68
70
|
outputs = []
|
|
69
71
|
for masks in raw_outputs:
|
|
70
|
-
class_mask = np.argmax(masks, axis=0)
|
|
72
|
+
class_mask = np.argmax(masks, axis=0).astype(np.uint8)
|
|
73
|
+
valid_mask = np.max(masks, axis=0) >= confidence
|
|
71
74
|
outputs.append(
|
|
72
75
|
SemanticSegmentationPrediction(
|
|
73
76
|
class_mask=class_mask,
|
|
77
|
+
valid_mask=valid_mask,
|
|
74
78
|
conf_masks=masks,
|
|
75
79
|
)
|
|
76
80
|
)
|
|
77
81
|
return outputs
|
|
78
82
|
|
|
79
83
|
def get_annotated_image(
|
|
80
|
-
self,
|
|
84
|
+
self,
|
|
85
|
+
image: Image.Image,
|
|
86
|
+
prediction: SemanticSegmentationPrediction,
|
|
87
|
+
mask_opacity: float = 0.3,
|
|
81
88
|
) -> Image.Image:
|
|
82
89
|
opencv_image = pil_to_opencv(image)
|
|
83
90
|
mask_overlay = opencv_image.copy()
|
|
84
91
|
|
|
85
92
|
for class_id, class_name in self.classes.items():
|
|
86
93
|
fill_color = self.color_scheme.get(class_name, (120, 120, 120))
|
|
87
|
-
mask_overlay[
|
|
94
|
+
mask_overlay[
|
|
95
|
+
(prediction.class_mask == class_id) & prediction.valid_mask
|
|
96
|
+
] = fill_color[:3]
|
|
88
97
|
|
|
89
98
|
annotated_image = cv2.addWeighted(
|
|
90
99
|
mask_overlay, mask_opacity, opencv_image, 1 - mask_opacity, 0
|
|
@@ -74,6 +74,12 @@ def image_to_base64(image):
|
|
|
74
74
|
return base64.b64encode(bytes_content).decode("utf-8")
|
|
75
75
|
|
|
76
76
|
|
|
77
|
+
def mask_to_base64(mask: np.ndarray) -> str:
|
|
78
|
+
buffered = BytesIO()
|
|
79
|
+
Image.fromarray(mask).save(buffered, format="PNG")
|
|
80
|
+
return base64.b64encode(buffered.getvalue()).decode("utf-8")
|
|
81
|
+
|
|
82
|
+
|
|
77
83
|
def image_to_bytes(image):
|
|
78
84
|
buffered = BytesIO()
|
|
79
85
|
if image.mode == "RGBA":
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[tool.poetry]
|
|
2
2
|
name = "orient_express"
|
|
3
|
-
version = "2.4.
|
|
3
|
+
version = "2.4.1"
|
|
4
4
|
description = "A library to simplify model deployment to Vertex AI"
|
|
5
5
|
authors = ["Alexey Zankevich <alex.zankevich@shiftsmart.com>", "Ian Myers <ian.myers@shiftsmart.com>"]
|
|
6
6
|
readme = "README.md"
|
|
@@ -25,6 +25,9 @@ onnxruntime = {version = "^1.20", markers = "sys_platform == 'darwin' or platfor
|
|
|
25
25
|
[tool.poetry.group.dev.dependencies]
|
|
26
26
|
black = "24.10.0"
|
|
27
27
|
pytest = "8.3.3"
|
|
28
|
+
ipykernel = "^7.2.0"
|
|
29
|
+
|
|
30
|
+
[tool.poetry.group.server.dependencies]
|
|
28
31
|
kserve = "^0.16.0"
|
|
29
32
|
python-json-logger = "^4.0.0"
|
|
30
33
|
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{orient_express-2.4.0 → orient_express-2.4.1}/orient_express/predictors/feature_extraction.py
RENAMED
|
File without changes
|
{orient_express-2.4.0 → orient_express-2.4.1}/orient_express/predictors/instance_segmentation.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|