mindee-lite 5.0.0__py3-none-any.whl
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.
- docs/conf.py +76 -0
- mindee/__init__.py +53 -0
- mindee/__main__.py +4 -0
- mindee/cli.py +7 -0
- mindee/client_mixin.py +23 -0
- mindee/client_options/__init__.py +0 -0
- mindee/client_options/polling_options.py +19 -0
- mindee/dependencies/__init__.py +9 -0
- mindee/dependencies/checkers.py +33 -0
- mindee/dependencies/decorators.py +30 -0
- mindee/error/__init__.py +27 -0
- mindee/error/geometry_error.py +2 -0
- mindee/error/mimetype_error.py +2 -0
- mindee/error/mindee_dependency_error.py +5 -0
- mindee/error/mindee_error.py +14 -0
- mindee/error/mindee_http_error.py +115 -0
- mindee/error/mindee_image_error.py +2 -0
- mindee/error/mindee_pdf_error.py +2 -0
- mindee/geometry/__init__.py +31 -0
- mindee/geometry/bbox.py +73 -0
- mindee/geometry/minmax.py +32 -0
- mindee/geometry/point.py +17 -0
- mindee/geometry/polygon.py +76 -0
- mindee/geometry/polygon_utils.py +36 -0
- mindee/geometry/quadrilateral.py +57 -0
- mindee/image/__init__.py +3 -0
- mindee/image/extracted_image.py +108 -0
- mindee/image/image_compressor.py +45 -0
- mindee/image/image_extractor.py +186 -0
- mindee/input/__init__.py +19 -0
- mindee/input/base_64_input.py +20 -0
- mindee/input/bytes_input.py +19 -0
- mindee/input/file_input.py +28 -0
- mindee/input/local_input_source.py +284 -0
- mindee/input/local_response.py +118 -0
- mindee/input/page_options.py +30 -0
- mindee/input/path_input.py +19 -0
- mindee/input/url_input_source.py +228 -0
- mindee/logger.py +9 -0
- mindee/mindee_http/__init__.py +11 -0
- mindee/mindee_http/response_validation.py +76 -0
- mindee/mindee_http/settings_mixin.py +15 -0
- mindee/parsing/__init__.py +5 -0
- mindee/parsing/common/__init__.py +15 -0
- mindee/parsing/common/common_response.py +29 -0
- mindee/parsing/common/string_dict.py +4 -0
- mindee/parsing/common/summary_helper.py +33 -0
- mindee/pdf/__init__.py +15 -0
- mindee/pdf/extracted_pdf.py +60 -0
- mindee/pdf/pdf_char_data.py +31 -0
- mindee/pdf/pdf_compressor.py +242 -0
- mindee/pdf/pdf_extractor.py +103 -0
- mindee/pdf/pdf_utils.py +300 -0
- mindee/py.typed +0 -0
- mindee/v1/__init__.py +17 -0
- mindee/v1/client.py +616 -0
- mindee/v1/client_options/__init__.py +4 -0
- mindee/v1/client_options/predict_options.py +28 -0
- mindee/v1/client_options/workflow_options.py +30 -0
- mindee/v1/commands/__init__.py +9 -0
- mindee/v1/commands/cli_parser.py +282 -0
- mindee/v1/commands/cli_products.py +109 -0
- mindee/v1/error/__init__.py +4 -0
- mindee/v1/error/mindee_api_error.py +5 -0
- mindee/v1/error/mindee_product_error.py +5 -0
- mindee/v1/mindee_http/__init__.py +14 -0
- mindee/v1/mindee_http/base_endpoint.py +24 -0
- mindee/v1/mindee_http/base_settings.py +63 -0
- mindee/v1/mindee_http/endpoint.py +388 -0
- mindee/v1/mindee_http/mindee_api.py +29 -0
- mindee/v1/mindee_http/workflow_endpoint.py +75 -0
- mindee/v1/mindee_http/workflow_settings.py +24 -0
- mindee/v1/parsing/__init__.py +8 -0
- mindee/v1/parsing/common/__init__.py +43 -0
- mindee/v1/parsing/common/api_request.py +28 -0
- mindee/v1/parsing/common/api_response.py +29 -0
- mindee/v1/parsing/common/async_predict_response.py +35 -0
- mindee/v1/parsing/common/document.py +84 -0
- mindee/v1/parsing/common/execution.py +84 -0
- mindee/v1/parsing/common/execution_file.py +15 -0
- mindee/v1/parsing/common/execution_priority.py +9 -0
- mindee/v1/parsing/common/extras/__init__.py +7 -0
- mindee/v1/parsing/common/extras/cropper_extra.py +19 -0
- mindee/v1/parsing/common/extras/extras.py +43 -0
- mindee/v1/parsing/common/extras/full_text_ocr_extra.py +18 -0
- mindee/v1/parsing/common/extras/rag_extra.py +11 -0
- mindee/v1/parsing/common/feedback_response.py +13 -0
- mindee/v1/parsing/common/inference.py +77 -0
- mindee/v1/parsing/common/job.py +50 -0
- mindee/v1/parsing/common/ocr/__init__.py +3 -0
- mindee/v1/parsing/common/ocr/mvision_v1.py +17 -0
- mindee/v1/parsing/common/ocr/ocr.py +15 -0
- mindee/v1/parsing/common/ocr/ocr_line.py +13 -0
- mindee/v1/parsing/common/ocr/ocr_page.py +75 -0
- mindee/v1/parsing/common/ocr/ocr_word.py +19 -0
- mindee/v1/parsing/common/orientation.py +38 -0
- mindee/v1/parsing/common/page.py +47 -0
- mindee/v1/parsing/common/predict_response.py +28 -0
- mindee/v1/parsing/common/prediction.py +18 -0
- mindee/v1/parsing/common/product.py +15 -0
- mindee/v1/parsing/common/workflow_response.py +21 -0
- mindee/v1/parsing/custom/__init__.py +11 -0
- mindee/v1/parsing/custom/classification.py +17 -0
- mindee/v1/parsing/custom/line_items.py +175 -0
- mindee/v1/parsing/custom/list.py +64 -0
- mindee/v1/parsing/generated/__init__.py +11 -0
- mindee/v1/parsing/generated/generated_list.py +54 -0
- mindee/v1/parsing/generated/generated_object.py +81 -0
- mindee/v1/parsing/standard/__init__.py +52 -0
- mindee/v1/parsing/standard/address.py +63 -0
- mindee/v1/parsing/standard/amount.py +44 -0
- mindee/v1/parsing/standard/base.py +197 -0
- mindee/v1/parsing/standard/boolean.py +33 -0
- mindee/v1/parsing/standard/classification.py +31 -0
- mindee/v1/parsing/standard/company_registration.py +45 -0
- mindee/v1/parsing/standard/date.py +54 -0
- mindee/v1/parsing/standard/locale.py +56 -0
- mindee/v1/parsing/standard/payment_details.py +93 -0
- mindee/v1/parsing/standard/position.py +78 -0
- mindee/v1/parsing/standard/tax.py +124 -0
- mindee/v1/parsing/standard/text.py +36 -0
- mindee/v1/pdf/__init__.py +4 -0
- mindee/v1/pdf/multi_receipts_extractor.py +33 -0
- mindee/v1/pdf/pdf_extractor.py +52 -0
- mindee/v1/product/__init__.py +96 -0
- mindee/v1/product/barcode_reader/__init__.py +9 -0
- mindee/v1/product/barcode_reader/barcode_reader_v1.py +37 -0
- mindee/v1/product/barcode_reader/barcode_reader_v1_document.py +45 -0
- mindee/v1/product/cropper/__init__.py +13 -0
- mindee/v1/product/cropper/cropper_v1.py +40 -0
- mindee/v1/product/cropper/cropper_v1_document.py +8 -0
- mindee/v1/product/cropper/cropper_v1_page.py +38 -0
- mindee/v1/product/custom/__init__.py +11 -0
- mindee/v1/product/custom/custom_v1.py +30 -0
- mindee/v1/product/custom/custom_v1_document.py +59 -0
- mindee/v1/product/custom/custom_v1_page.py +49 -0
- mindee/v1/product/financial_document/__init__.py +15 -0
- mindee/v1/product/financial_document/financial_document_v1.py +37 -0
- mindee/v1/product/financial_document/financial_document_v1_document.py +318 -0
- mindee/v1/product/financial_document/financial_document_v1_line_item.py +105 -0
- mindee/v1/product/fr/__init__.py +49 -0
- mindee/v1/product/fr/bank_account_details/__init__.py +23 -0
- mindee/v1/product/fr/bank_account_details/bank_account_details_v1.py +37 -0
- mindee/v1/product/fr/bank_account_details/bank_account_details_v1_document.py +46 -0
- mindee/v1/product/fr/bank_account_details/bank_account_details_v2.py +37 -0
- mindee/v1/product/fr/bank_account_details/bank_account_details_v2_bban.py +63 -0
- mindee/v1/product/fr/bank_account_details/bank_account_details_v2_document.py +56 -0
- mindee/v1/product/fr/carte_grise/__init__.py +9 -0
- mindee/v1/product/fr/carte_grise/carte_grise_v1.py +37 -0
- mindee/v1/product/fr/carte_grise/carte_grise_v1_document.py +313 -0
- mindee/v1/product/fr/id_card/__init__.py +23 -0
- mindee/v1/product/fr/id_card/id_card_v1.py +40 -0
- mindee/v1/product/fr/id_card/id_card_v1_document.py +99 -0
- mindee/v1/product/fr/id_card/id_card_v1_page.py +35 -0
- mindee/v1/product/fr/id_card/id_card_v2.py +40 -0
- mindee/v1/product/fr/id_card/id_card_v2_document.py +134 -0
- mindee/v1/product/fr/id_card/id_card_v2_page.py +42 -0
- mindee/v1/product/generated/__init__.py +11 -0
- mindee/v1/product/generated/generated_v1.py +31 -0
- mindee/v1/product/generated/generated_v1_document.py +40 -0
- mindee/v1/product/generated/generated_v1_page.py +42 -0
- mindee/v1/product/generated/generated_v1_prediction.py +78 -0
- mindee/v1/product/international_id/__init__.py +9 -0
- mindee/v1/product/international_id/international_id_v2.py +37 -0
- mindee/v1/product/international_id/international_id_v2_document.py +152 -0
- mindee/v1/product/invoice/__init__.py +13 -0
- mindee/v1/product/invoice/invoice_v4.py +37 -0
- mindee/v1/product/invoice/invoice_v4_document.py +285 -0
- mindee/v1/product/invoice/invoice_v4_line_item.py +105 -0
- mindee/v1/product/invoice_splitter/__init__.py +13 -0
- mindee/v1/product/invoice_splitter/invoice_splitter_v1.py +37 -0
- mindee/v1/product/invoice_splitter/invoice_splitter_v1_document.py +55 -0
- mindee/v1/product/invoice_splitter/invoice_splitter_v1_invoice_page_group.py +51 -0
- mindee/v1/product/multi_receipts_detector/__init__.py +11 -0
- mindee/v1/product/multi_receipts_detector/multi_receipts_detector_v1.py +37 -0
- mindee/v1/product/multi_receipts_detector/multi_receipts_detector_v1_document.py +35 -0
- mindee/v1/product/passport/__init__.py +9 -0
- mindee/v1/product/passport/passport_v1.py +37 -0
- mindee/v1/product/passport/passport_v1_document.py +106 -0
- mindee/v1/product/receipt/__init__.py +13 -0
- mindee/v1/product/receipt/receipt_v5.py +37 -0
- mindee/v1/product/receipt/receipt_v5_document.py +180 -0
- mindee/v1/product/receipt/receipt_v5_line_item.py +77 -0
- mindee/v1/product/us/__init__.py +13 -0
- mindee/v1/product/us/bank_check/__init__.py +13 -0
- mindee/v1/product/us/bank_check/bank_check_v1.py +40 -0
- mindee/v1/product/us/bank_check/bank_check_v1_document.py +72 -0
- mindee/v1/product/us/bank_check/bank_check_v1_page.py +45 -0
- mindee/v2/__init__.py +40 -0
- mindee/v2/cli.py +11 -0
- mindee/v2/client.py +190 -0
- mindee/v2/client_options/__init__.py +0 -0
- mindee/v2/client_options/base_parameters.py +44 -0
- mindee/v2/commands/__init__.py +0 -0
- mindee/v2/commands/cli_parser.py +136 -0
- mindee/v2/error/__init__.py +4 -0
- mindee/v2/error/mindee_api_v2_error.py +5 -0
- mindee/v2/error/mindee_http_error_v2.py +51 -0
- mindee/v2/file_operations/__init__.py +12 -0
- mindee/v2/file_operations/crop.py +52 -0
- mindee/v2/file_operations/crop_files.py +20 -0
- mindee/v2/file_operations/split.py +38 -0
- mindee/v2/file_operations/split_files.py +20 -0
- mindee/v2/mindee_http/__init__.py +0 -0
- mindee/v2/mindee_http/mindee_api_v2.py +322 -0
- mindee/v2/mindee_http/response_validation_v2.py +35 -0
- mindee/v2/parsing/__init__.py +25 -0
- mindee/v2/parsing/error/__init__.py +4 -0
- mindee/v2/parsing/error/error_item.py +14 -0
- mindee/v2/parsing/error/error_response.py +30 -0
- mindee/v2/parsing/inference/__init__.py +0 -0
- mindee/v2/parsing/inference/base_inference.py +29 -0
- mindee/v2/parsing/inference/base_response.py +22 -0
- mindee/v2/parsing/inference/field/__init__.py +17 -0
- mindee/v2/parsing/inference/field/base_field.py +46 -0
- mindee/v2/parsing/inference/field/factory.py +17 -0
- mindee/v2/parsing/inference/field/field_confidence.py +36 -0
- mindee/v2/parsing/inference/field/field_location.py +28 -0
- mindee/v2/parsing/inference/field/inference_fields.py +63 -0
- mindee/v2/parsing/inference/field/list_field.py +58 -0
- mindee/v2/parsing/inference/field/object_field.py +133 -0
- mindee/v2/parsing/inference/field/simple_field.py +21 -0
- mindee/v2/parsing/inference/inference_active_options.py +64 -0
- mindee/v2/parsing/inference/inference_file.py +29 -0
- mindee/v2/parsing/inference/inference_job.py +14 -0
- mindee/v2/parsing/inference/inference_model.py +14 -0
- mindee/v2/parsing/inference/rag_metadata.py +10 -0
- mindee/v2/parsing/inference/raw_text.py +21 -0
- mindee/v2/parsing/inference/raw_text_page.py +11 -0
- mindee/v2/parsing/job/__init__.py +5 -0
- mindee/v2/parsing/job/job.py +55 -0
- mindee/v2/parsing/job/job_response.py +14 -0
- mindee/v2/parsing/job/job_webhook.py +35 -0
- mindee/v2/parsing/search/__init__.py +0 -0
- mindee/v2/parsing/search/model_webhook.py +17 -0
- mindee/v2/parsing/search/paginationmetadata.py +28 -0
- mindee/v2/parsing/search/search_model.py +25 -0
- mindee/v2/parsing/search/search_models.py +24 -0
- mindee/v2/parsing/search/search_response.py +32 -0
- mindee/v2/product/__init__.py +23 -0
- mindee/v2/product/classification/__init__.py +21 -0
- mindee/v2/product/classification/classification_classifier.py +22 -0
- mindee/v2/product/classification/classification_inference.py +27 -0
- mindee/v2/product/classification/classification_response.py +21 -0
- mindee/v2/product/classification/classification_result.py +16 -0
- mindee/v2/product/classification/params/__init__.py +5 -0
- mindee/v2/product/classification/params/classification_parameters.py +11 -0
- mindee/v2/product/crop/__init__.py +14 -0
- mindee/v2/product/crop/crop_inference.py +28 -0
- mindee/v2/product/crop/crop_item.py +43 -0
- mindee/v2/product/crop/crop_response.py +19 -0
- mindee/v2/product/crop/crop_result.py +29 -0
- mindee/v2/product/crop/params/__init__.py +3 -0
- mindee/v2/product/crop/params/crop_parameters.py +11 -0
- mindee/v2/product/extraction/__init__.py +21 -0
- mindee/v2/product/extraction/extraction_inference.py +28 -0
- mindee/v2/product/extraction/extraction_response.py +26 -0
- mindee/v2/product/extraction/extraction_result.py +27 -0
- mindee/v2/product/extraction/params/__init__.py +15 -0
- mindee/v2/product/extraction/params/data_schema.py +19 -0
- mindee/v2/product/extraction/params/data_schema_field.py +30 -0
- mindee/v2/product/extraction/params/data_schema_replace.py +20 -0
- mindee/v2/product/extraction/params/extraction_parameters.py +63 -0
- mindee/v2/product/extraction/params/string_data_class.py +17 -0
- mindee/v2/product/ocr/__init__.py +15 -0
- mindee/v2/product/ocr/ocr_inference.py +27 -0
- mindee/v2/product/ocr/ocr_page.py +23 -0
- mindee/v2/product/ocr/ocr_response.py +19 -0
- mindee/v2/product/ocr/ocr_result.py +19 -0
- mindee/v2/product/ocr/ocr_word.py +17 -0
- mindee/v2/product/ocr/params/__init__.py +3 -0
- mindee/v2/product/ocr/params/ocr_parameters.py +11 -0
- mindee/v2/product/split/__init__.py +13 -0
- mindee/v2/product/split/params/__init__.py +3 -0
- mindee/v2/product/split/params/split_parameters.py +11 -0
- mindee/v2/product/split/split_inference.py +28 -0
- mindee/v2/product/split/split_range.py +42 -0
- mindee/v2/product/split/split_response.py +19 -0
- mindee/v2/product/split/split_result.py +31 -0
- mindee/versions.py +21 -0
- mindee_lite-5.0.0.dist-info/METADATA +88 -0
- mindee_lite-5.0.0.dist-info/RECORD +418 -0
- mindee_lite-5.0.0.dist-info/WHEEL +5 -0
- mindee_lite-5.0.0.dist-info/entry_points.txt +3 -0
- mindee_lite-5.0.0.dist-info/licenses/LICENSE +21 -0
- mindee_lite-5.0.0.dist-info/top_level.txt +4 -0
- scripts/generate_lite_toml.py +42 -0
- tests/__init__.py +0 -0
- tests/conftest.py +16 -0
- tests/dependencies/__init__.py +0 -0
- tests/dependencies/test_dependencies.py +23 -0
- tests/input/__init__.py +0 -0
- tests/input/test_apply_page_options.py +164 -0
- tests/input/test_compression.py +232 -0
- tests/input/test_fix_pdf.py +24 -0
- tests/input/test_inputs.py +103 -0
- tests/test_geometry.py +137 -0
- tests/test_pkg_versions.py +34 -0
- tests/utils.py +55 -0
- tests/v1/__init__.py +0 -0
- tests/v1/api/__init__.py +0 -0
- tests/v1/api/test_async_response.py +121 -0
- tests/v1/api/test_feedback_response.py +14 -0
- tests/v1/api/test_response.py +86 -0
- tests/v1/extraction/__init__.py +0 -0
- tests/v1/extraction/test_image_extractor.py +46 -0
- tests/v1/extraction/test_invoice_splitter_auto_extraction.py +72 -0
- tests/v1/extraction/test_multi_receipts_extractor.py +121 -0
- tests/v1/extraction/test_pdf_extractor.py +85 -0
- tests/v1/extras/__init__.py +0 -0
- tests/v1/extras/test_extras_integration.py +31 -0
- tests/v1/extras/test_full_text_ocr.py +38 -0
- tests/v1/input/__init__.py +0 -0
- tests/v1/input/test_local_response.py +42 -0
- tests/v1/input/test_url_input_source_integration.py +65 -0
- tests/v1/mindee_http/__init__.py +0 -0
- tests/v1/mindee_http/test_error.py +116 -0
- tests/v1/parsing/__init__.py +0 -0
- tests/v1/parsing/common/__init__.py +0 -0
- tests/v1/parsing/common/test_ocr.py +14 -0
- tests/v1/parsing/common/test_orientation.py +25 -0
- tests/v1/parsing/standard/__init__.py +0 -0
- tests/v1/parsing/standard/test_amount.py +23 -0
- tests/v1/parsing/standard/test_date.py +27 -0
- tests/v1/parsing/standard/test_field.py +101 -0
- tests/v1/parsing/standard/test_locale.py +40 -0
- tests/v1/parsing/standard/test_payment_details.py +71 -0
- tests/v1/parsing/standard/test_position.py +36 -0
- tests/v1/parsing/standard/test_string.py +34 -0
- tests/v1/parsing/standard/test_tax.py +44 -0
- tests/v1/product/__init__.py +14 -0
- tests/v1/product/barcode_reader/__init__.py +0 -0
- tests/v1/product/barcode_reader/test_barcode_reader_v1.py +47 -0
- tests/v1/product/barcode_reader/test_barcode_reader_v1_regression.py +26 -0
- tests/v1/product/cropper/__init__.py +0 -0
- tests/v1/product/cropper/test_cropper_v1.py +66 -0
- tests/v1/product/cropper/test_cropper_v1_regression.py +26 -0
- tests/v1/product/custom/__init__.py +0 -0
- tests/v1/product/custom/test_custom_v1.py +124 -0
- tests/v1/product/custom/test_custom_v1_line_items.py +75 -0
- tests/v1/product/custom/test_custom_v1_v2.py +124 -0
- tests/v1/product/financial_document/__init__.py +0 -0
- tests/v1/product/financial_document/test_financial_document_v1.py +121 -0
- tests/v1/product/financial_document/test_financial_document_v1_regression.py +31 -0
- tests/v1/product/fr/__init__.py +0 -0
- tests/v1/product/fr/bank_account_details/__init__.py +0 -0
- tests/v1/product/fr/bank_account_details/test_bank_account_details_v1.py +50 -0
- tests/v1/product/fr/bank_account_details/test_bank_account_details_v1_regression.py +31 -0
- tests/v1/product/fr/bank_account_details/test_bank_account_details_v2.py +54 -0
- tests/v1/product/fr/bank_account_details/test_bank_account_details_v2_regression.py +31 -0
- tests/v1/product/fr/carte_grise/__init__.py +0 -0
- tests/v1/product/fr/carte_grise/test_carte_grise_v1.py +86 -0
- tests/v1/product/fr/carte_grise/test_carte_grise_v1_regression.py +26 -0
- tests/v1/product/fr/id_card/__init__.py +0 -0
- tests/v1/product/fr/id_card/test_id_card_v1.py +75 -0
- tests/v1/product/fr/id_card/test_id_card_v1_regression.py +26 -0
- tests/v1/product/fr/id_card/test_id_card_v2.py +80 -0
- tests/v1/product/fr/id_card/test_id_card_v2_regression.py +26 -0
- tests/v1/product/generated/__init__.py +0 -0
- tests/v1/product/generated/test_generated_v1.py +949 -0
- tests/v1/product/international_id/__init__.py +0 -0
- tests/v1/product/international_id/test_international_id_v2.py +61 -0
- tests/v1/product/invoice/__init__.py +0 -0
- tests/v1/product/invoice/test_invoice_v4.py +70 -0
- tests/v1/product/invoice/test_invoice_v4_regression.py +26 -0
- tests/v1/product/invoice_splitter/__init__.py +0 -0
- tests/v1/product/invoice_splitter/test_invoice_splitter_v1.py +46 -0
- tests/v1/product/invoice_splitter/test_invoice_splitter_v1_regression.py +25 -0
- tests/v1/product/multi_receipts_detector/__init__.py +0 -0
- tests/v1/product/multi_receipts_detector/test_multi_receipts_detector_v1.py +48 -0
- tests/v1/product/multi_receipts_detector/test_multi_receipts_detector_v1_regression.py +31 -0
- tests/v1/product/passport/__init__.py +0 -0
- tests/v1/product/passport/test_passport_v1.py +56 -0
- tests/v1/product/passport/test_passport_v1_regression.py +26 -0
- tests/v1/product/receipt/__init__.py +0 -0
- tests/v1/product/receipt/test_receipt_v5.py +59 -0
- tests/v1/product/receipt/test_receipt_v5_regression.py +26 -0
- tests/v1/product/us/__init__.py +0 -0
- tests/v1/product/us/bank_check/__init__.py +0 -0
- tests/v1/product/us/bank_check/test_bank_check_v1.py +71 -0
- tests/v1/product/us/bank_check/test_bank_check_v1_regression.py +26 -0
- tests/v1/test_cli.py +211 -0
- tests/v1/test_client.py +172 -0
- tests/v1/workflows/__init__.py +0 -0
- tests/v1/workflows/test_workflow.py +81 -0
- tests/v1/workflows/test_workflow_integration.py +111 -0
- tests/v2/__init__.py +0 -0
- tests/v2/file_operations/__init__.py +0 -0
- tests/v2/file_operations/test_crop_operation.py +71 -0
- tests/v2/file_operations/test_crop_operation_integration.py +67 -0
- tests/v2/file_operations/test_split_operation.py +57 -0
- tests/v2/file_operations/test_split_operation_integration.py +68 -0
- tests/v2/input/__init__.py +0 -0
- tests/v2/input/test_inference_parameters.py +85 -0
- tests/v2/input/test_local_response.py +51 -0
- tests/v2/parsing/__init__.py +0 -0
- tests/v2/parsing/test_job_response.py +61 -0
- tests/v2/product/__init__.py +0 -0
- tests/v2/product/classification/__init__.py +0 -0
- tests/v2/product/classification/test_classification_integration.py +36 -0
- tests/v2/product/classification/test_classification_response.py +50 -0
- tests/v2/product/crop/__init__.py +0 -0
- tests/v2/product/crop/test_crop_integration.py +32 -0
- tests/v2/product/crop/test_crop_response.py +97 -0
- tests/v2/product/extraction/__init__.py +0 -0
- tests/v2/product/extraction/test_extraction_response.py +341 -0
- tests/v2/product/ocr/__init__.py +0 -0
- tests/v2/product/ocr/test_ocr_integration.py +35 -0
- tests/v2/product/ocr/test_ocr_response.py +86 -0
- tests/v2/product/split/__init__.py +0 -0
- tests/v2/product/split/test_split_integration.py +32 -0
- tests/v2/product/split/test_split_response.py +80 -0
- tests/v2/product/utils.py +21 -0
- tests/v2/search/__init__.py +0 -0
- tests/v2/search/test_search_models.py +33 -0
- tests/v2/test_base_classes.py +49 -0
- tests/v2/test_client.py +340 -0
- tests/v2/test_client_integration.py +355 -0
docs/conf.py
ADDED
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
# Configuration file for the Sphinx documentation builder.
|
|
2
|
+
#
|
|
3
|
+
# This file only contains a selection of the most common options. For a full
|
|
4
|
+
# list see the documentation:
|
|
5
|
+
# https://www.sphinx-doc.org/en/master/usage/configuration.html
|
|
6
|
+
|
|
7
|
+
# -- Path setup --------------------------------------------------------------
|
|
8
|
+
|
|
9
|
+
# If extensions (or modules to document with autodoc) are in another directory,
|
|
10
|
+
# add these directories to sys.path here. If the directory is relative to the
|
|
11
|
+
# documentation root, use os.path.abspath to make it absolute, like shown here.
|
|
12
|
+
#
|
|
13
|
+
# import os
|
|
14
|
+
# import sys
|
|
15
|
+
# sys.path.insert(0, os.path.abspath('.'))
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
# -- Project information -----------------------------------------------------
|
|
19
|
+
|
|
20
|
+
project = "Mindee Python Client"
|
|
21
|
+
copyright = "2022, Mindee"
|
|
22
|
+
author = "Mindee"
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
# -- General configuration ---------------------------------------------------
|
|
26
|
+
|
|
27
|
+
# Add any Sphinx extension module names here, as strings. They can be
|
|
28
|
+
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
|
|
29
|
+
# ones.
|
|
30
|
+
extensions = [
|
|
31
|
+
"sphinx.ext.autodoc",
|
|
32
|
+
"sphinx_autodoc_typehints",
|
|
33
|
+
]
|
|
34
|
+
|
|
35
|
+
# Add any paths that contain templates here, relative to this directory.
|
|
36
|
+
templates_path = ["_templates"]
|
|
37
|
+
|
|
38
|
+
# List of patterns, relative to source directory, that match files and
|
|
39
|
+
# directories to ignore when looking for source files.
|
|
40
|
+
# This pattern also affects html_static_path and html_extra_path.
|
|
41
|
+
exclude_patterns = ["_build", "Thumbs.db", ".DS_Store"]
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
# -- Options for HTML output -------------------------------------------------
|
|
45
|
+
|
|
46
|
+
# The theme to use for HTML and HTML Help pages. See the documentation for
|
|
47
|
+
# a list of builtin themes.
|
|
48
|
+
#
|
|
49
|
+
html_theme = "sphinx_rtd_theme"
|
|
50
|
+
html_theme_options = {
|
|
51
|
+
"body_max_width": "none",
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
# Add any paths that contain custom static files (such as style sheets) here,
|
|
55
|
+
# relative to this directory. They are copied after the builtin static files,
|
|
56
|
+
# so a file named "default.css" will overwrite the builtin "default.css".
|
|
57
|
+
html_static_path = []
|
|
58
|
+
|
|
59
|
+
# A list of paths that contain extra files not directly related to the documentation.
|
|
60
|
+
html_extra_path = ["extras"]
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
# -- autodoc -----------------------------------------------------------------
|
|
64
|
+
|
|
65
|
+
autodoc_member_order = "groupwise"
|
|
66
|
+
# autodoc_class_signature = "separated" # breaks new syntaxes
|
|
67
|
+
|
|
68
|
+
# Hides prepended module names wich caused some classes to overflow on the right of the screen.
|
|
69
|
+
add_module_names = False
|
|
70
|
+
autodoc_typehints = "description"
|
|
71
|
+
autodoc_default_options = {"exclude-members": "__init__"}
|
|
72
|
+
|
|
73
|
+
# -- autodoc-typehints -------------------------------------------------------
|
|
74
|
+
|
|
75
|
+
typehints_defaults = "comma"
|
|
76
|
+
suppress_warnings = ["ref.python"]
|
mindee/__init__.py
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
from mindee.client_options.polling_options import PollingOptions
|
|
2
|
+
from mindee.input import PageOptions
|
|
3
|
+
from mindee.input.base_64_input import Base64Input
|
|
4
|
+
from mindee.input.bytes_input import BytesInput
|
|
5
|
+
from mindee.input.file_input import FileInput
|
|
6
|
+
from mindee.input.local_input_source import LocalInputSource
|
|
7
|
+
from mindee.input.local_response import LocalResponse
|
|
8
|
+
from mindee.input.path_input import PathInput
|
|
9
|
+
from mindee.input.url_input_source import URLInputSource
|
|
10
|
+
from mindee.v1 import product
|
|
11
|
+
from mindee.v2.parsing.job.job_response import JobResponse
|
|
12
|
+
from mindee.v2.product.classification.classification_response import (
|
|
13
|
+
ClassificationResponse,
|
|
14
|
+
)
|
|
15
|
+
from mindee.v2.product.classification.params.classification_parameters import (
|
|
16
|
+
ClassificationParameters,
|
|
17
|
+
)
|
|
18
|
+
from mindee.v2.product.crop.crop_response import CropResponse
|
|
19
|
+
from mindee.v2.product.crop.params.crop_parameters import CropParameters
|
|
20
|
+
from mindee.v2.product.extraction.extraction_response import ExtractionResponse
|
|
21
|
+
from mindee.v2.product.extraction.extraction_result import ExtractionResult
|
|
22
|
+
from mindee.v2.product.extraction.params.extraction_parameters import (
|
|
23
|
+
ExtractionParameters,
|
|
24
|
+
)
|
|
25
|
+
from mindee.v2.product.ocr.ocr_response import OCRResponse
|
|
26
|
+
from mindee.v2.product.ocr.params.ocr_parameters import OCRParameters
|
|
27
|
+
from mindee.v2.product.split.params.split_parameters import SplitParameters
|
|
28
|
+
from mindee.v2.product.split.split_response import SplitResponse
|
|
29
|
+
|
|
30
|
+
__all__ = [
|
|
31
|
+
"Base64Input",
|
|
32
|
+
"BytesInput",
|
|
33
|
+
"ClassificationParameters",
|
|
34
|
+
"ClassificationResponse",
|
|
35
|
+
"CropParameters",
|
|
36
|
+
"CropResponse",
|
|
37
|
+
"ExtractionParameters",
|
|
38
|
+
"ExtractionResponse",
|
|
39
|
+
"ExtractionResult",
|
|
40
|
+
"FileInput",
|
|
41
|
+
"JobResponse",
|
|
42
|
+
"LocalInputSource",
|
|
43
|
+
"LocalResponse",
|
|
44
|
+
"OCRParameters",
|
|
45
|
+
"OCRResponse",
|
|
46
|
+
"PageOptions",
|
|
47
|
+
"PathInput",
|
|
48
|
+
"PollingOptions",
|
|
49
|
+
"SplitParameters",
|
|
50
|
+
"SplitResponse",
|
|
51
|
+
"URLInputSource",
|
|
52
|
+
"product",
|
|
53
|
+
]
|
mindee/__main__.py
ADDED
mindee/cli.py
ADDED
mindee/client_mixin.py
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
from mindee.error import MindeeClientError
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class ClientMixin:
|
|
5
|
+
"""Mixin for clients V1 & V2 common static methods."""
|
|
6
|
+
|
|
7
|
+
@staticmethod
|
|
8
|
+
def _validate_async_params(
|
|
9
|
+
initial_delay_sec: float, delay_sec: float, max_retries: int
|
|
10
|
+
) -> None:
|
|
11
|
+
min_delay = 1
|
|
12
|
+
min_initial_delay = 1
|
|
13
|
+
min_retries = 1
|
|
14
|
+
if delay_sec < min_delay:
|
|
15
|
+
raise MindeeClientError(
|
|
16
|
+
f"Cannot set auto-parsing delay to less than {min_delay} second(s)."
|
|
17
|
+
)
|
|
18
|
+
if initial_delay_sec < min_initial_delay:
|
|
19
|
+
raise MindeeClientError(
|
|
20
|
+
f"Cannot set initial parsing delay to less than {min_initial_delay} second(s)."
|
|
21
|
+
)
|
|
22
|
+
if max_retries < min_retries:
|
|
23
|
+
raise MindeeClientError(f"Cannot set retries to less than {min_retries}.")
|
|
File without changes
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
class PollingOptions:
|
|
2
|
+
"""Options for asynchronous polling."""
|
|
3
|
+
|
|
4
|
+
initial_delay_sec: float
|
|
5
|
+
"""Initial delay before the first polling attempt."""
|
|
6
|
+
delay_sec: float
|
|
7
|
+
"""Delay between each polling attempt."""
|
|
8
|
+
max_retries: int
|
|
9
|
+
"""Total number of polling attempts."""
|
|
10
|
+
|
|
11
|
+
def __init__(
|
|
12
|
+
self,
|
|
13
|
+
initial_delay_sec: float = 2,
|
|
14
|
+
delay_sec: float = 1.5,
|
|
15
|
+
max_retries: int = 80,
|
|
16
|
+
):
|
|
17
|
+
self.initial_delay_sec = initial_delay_sec
|
|
18
|
+
self.delay_sec = delay_sec
|
|
19
|
+
self.max_retries = max_retries
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
from mindee.dependencies.checkers import PILLOW_AVAILABLE, PYPDFIUM2_AVAILABLE
|
|
2
|
+
from mindee.dependencies.decorators import requires_pillow, requires_pypdfium2
|
|
3
|
+
|
|
4
|
+
__all__ = [
|
|
5
|
+
"PILLOW_AVAILABLE",
|
|
6
|
+
"PYPDFIUM2_AVAILABLE",
|
|
7
|
+
"requires_pillow",
|
|
8
|
+
"requires_pypdfium2",
|
|
9
|
+
]
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
from mindee.error.mindee_dependency_error import MindeeDependencyError
|
|
2
|
+
|
|
3
|
+
try:
|
|
4
|
+
import PIL # noqa: F401 #pylint: disable=unused-import
|
|
5
|
+
|
|
6
|
+
PILLOW_AVAILABLE = True
|
|
7
|
+
except ImportError:
|
|
8
|
+
PILLOW_AVAILABLE = False
|
|
9
|
+
|
|
10
|
+
try:
|
|
11
|
+
import pypdfium2 # noqa: F401 #pylint: disable=unused-import
|
|
12
|
+
|
|
13
|
+
PYPDFIUM2_AVAILABLE = True
|
|
14
|
+
except ImportError:
|
|
15
|
+
PYPDFIUM2_AVAILABLE = False
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def require_pillow() -> None:
|
|
19
|
+
"""Raises a clear error if Pillow is not installed."""
|
|
20
|
+
if not PILLOW_AVAILABLE:
|
|
21
|
+
raise MindeeDependencyError(
|
|
22
|
+
"This feature requires the 'Pillow' library. "
|
|
23
|
+
"Install it directly or run `pip install mindee` instead of `mindee-lite`."
|
|
24
|
+
)
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def require_pypdfium2() -> None:
|
|
28
|
+
"""Raises a clear error if PyPDFium2 is not installed."""
|
|
29
|
+
if not PYPDFIUM2_AVAILABLE:
|
|
30
|
+
raise MindeeDependencyError(
|
|
31
|
+
"This feature requires the 'PyPDFium2' library. "
|
|
32
|
+
"Install it directly or run `pip install mindee` instead of `mindee-lite`."
|
|
33
|
+
)
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import functools
|
|
2
|
+
from collections.abc import Callable
|
|
3
|
+
from typing import ParamSpec, TypeVar
|
|
4
|
+
|
|
5
|
+
from mindee.dependencies.checkers import require_pillow, require_pypdfium2
|
|
6
|
+
|
|
7
|
+
P = ParamSpec("P")
|
|
8
|
+
R = TypeVar("R")
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def requires_pillow(func: Callable[P, R]) -> Callable[P, R]:
|
|
12
|
+
"""Decorator to enforce Pillow availability on a function/method."""
|
|
13
|
+
|
|
14
|
+
@functools.wraps(func)
|
|
15
|
+
def wrapper(*args: P.args, **kwargs: P.kwargs) -> R:
|
|
16
|
+
require_pillow()
|
|
17
|
+
return func(*args, **kwargs)
|
|
18
|
+
|
|
19
|
+
return wrapper
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def requires_pypdfium2(func: Callable[P, R]) -> Callable[P, R]:
|
|
23
|
+
"""Decorator to enforce PyPDFium2 availability on a function/method."""
|
|
24
|
+
|
|
25
|
+
@functools.wraps(func)
|
|
26
|
+
def wrapper(*args: P.args, **kwargs: P.kwargs) -> R:
|
|
27
|
+
require_pypdfium2()
|
|
28
|
+
return func(*args, **kwargs)
|
|
29
|
+
|
|
30
|
+
return wrapper
|
mindee/error/__init__.py
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
from mindee.error.geometry_error import MindeeGeometryError
|
|
2
|
+
from mindee.error.mimetype_error import MimeTypeError
|
|
3
|
+
from mindee.error.mindee_error import (
|
|
4
|
+
MindeeClientError,
|
|
5
|
+
MindeeError,
|
|
6
|
+
)
|
|
7
|
+
from mindee.error.mindee_http_error import (
|
|
8
|
+
MindeeHTTPClientError,
|
|
9
|
+
MindeeHTTPError,
|
|
10
|
+
MindeeHTTPServerError,
|
|
11
|
+
handle_error,
|
|
12
|
+
)
|
|
13
|
+
from mindee.error.mindee_image_error import MindeeImageError
|
|
14
|
+
from mindee.error.mindee_pdf_error import MindeePDFError
|
|
15
|
+
|
|
16
|
+
__all__ = [
|
|
17
|
+
"MimeTypeError",
|
|
18
|
+
"MindeeClientError",
|
|
19
|
+
"MindeeError",
|
|
20
|
+
"MindeeGeometryError",
|
|
21
|
+
"MindeeHTTPClientError",
|
|
22
|
+
"MindeeHTTPError",
|
|
23
|
+
"MindeeHTTPServerError",
|
|
24
|
+
"MindeeImageError",
|
|
25
|
+
"MindeePDFError",
|
|
26
|
+
"handle_error",
|
|
27
|
+
]
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
class MindeeError(RuntimeError):
|
|
2
|
+
"""A generic exception relating to various HTTP errors."""
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
class MindeeClientError(MindeeError):
|
|
6
|
+
"""
|
|
7
|
+
An exception relating to document parsing errors.
|
|
8
|
+
|
|
9
|
+
Not to be confused with `MindeeHTTPClientError`.
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class MindeeSourceError(MindeeError):
|
|
14
|
+
"""An exception relating to document loading."""
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
from mindee.error.mindee_error import MindeeError
|
|
2
|
+
from mindee.parsing.common.string_dict import StringDict
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
class MindeeHTTPError(RuntimeError):
|
|
6
|
+
"""An exception relating to HTTP calls."""
|
|
7
|
+
|
|
8
|
+
status_code: int
|
|
9
|
+
api_code: str | None
|
|
10
|
+
api_details: str | None
|
|
11
|
+
api_message: str | None
|
|
12
|
+
|
|
13
|
+
def __init__(self, http_error: StringDict, url: str, code: int) -> None:
|
|
14
|
+
"""
|
|
15
|
+
Base exception for HTTP calls.
|
|
16
|
+
|
|
17
|
+
:param http_error: formatted & parsed error
|
|
18
|
+
:param url: url/endpoint the exception was raised on
|
|
19
|
+
:param code: HTTP code for the error
|
|
20
|
+
"""
|
|
21
|
+
self.status_code = code
|
|
22
|
+
self.api_code = http_error.get("code", None) if "code" in http_error else None
|
|
23
|
+
self.api_details = (
|
|
24
|
+
http_error.get("details", None) if "details" in http_error else None
|
|
25
|
+
)
|
|
26
|
+
self.api_message = (
|
|
27
|
+
http_error.get("message", None) if "message" in http_error else None
|
|
28
|
+
)
|
|
29
|
+
super().__init__(
|
|
30
|
+
f"{url} {self.status_code} HTTP error: {self.api_details} - {self.api_message}"
|
|
31
|
+
)
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def create_error_obj(response: StringDict | str) -> StringDict:
|
|
35
|
+
"""
|
|
36
|
+
Creates an error object based on a requests' payload.
|
|
37
|
+
|
|
38
|
+
:param response: response as sent by the server, as a dict.
|
|
39
|
+
In _very_ rare instances, this can be an html string.
|
|
40
|
+
"""
|
|
41
|
+
if not isinstance(response, str):
|
|
42
|
+
if "api_request" in response and "error" in response["api_request"]:
|
|
43
|
+
return response["api_request"]["error"]
|
|
44
|
+
raise MindeeError(f"Could not build specific HTTP exception from '{response}'")
|
|
45
|
+
error_dict = {}
|
|
46
|
+
if "Maximum pdf pages" in response:
|
|
47
|
+
error_dict = {
|
|
48
|
+
"code": "TooManyPages",
|
|
49
|
+
"message": "Maximum amound of pdf pages reached.",
|
|
50
|
+
"details": response,
|
|
51
|
+
}
|
|
52
|
+
elif "Max file size is" in response:
|
|
53
|
+
error_dict = {
|
|
54
|
+
"code": "FileTooLarge",
|
|
55
|
+
"message": "Maximum file size reached.",
|
|
56
|
+
"details": response,
|
|
57
|
+
}
|
|
58
|
+
elif "Invalid file type" in response:
|
|
59
|
+
error_dict = {
|
|
60
|
+
"code": "InvalidFiletype",
|
|
61
|
+
"message": "Invalid file type.",
|
|
62
|
+
"details": response,
|
|
63
|
+
}
|
|
64
|
+
elif "Gateway timeout" in response:
|
|
65
|
+
error_dict = {
|
|
66
|
+
"code": "RequestTimeout",
|
|
67
|
+
"message": "Request timed out.",
|
|
68
|
+
"details": response,
|
|
69
|
+
}
|
|
70
|
+
elif "Too Many Requests" in response:
|
|
71
|
+
error_dict = {
|
|
72
|
+
"code": "TooManyRequests",
|
|
73
|
+
"message": "Too Many Requests.",
|
|
74
|
+
"details": response,
|
|
75
|
+
}
|
|
76
|
+
else:
|
|
77
|
+
error_dict = {
|
|
78
|
+
"code": "UnknownError",
|
|
79
|
+
"message": "Server sent back an unexpected reply.",
|
|
80
|
+
"details": response,
|
|
81
|
+
}
|
|
82
|
+
return error_dict
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
class MindeeHTTPClientError(MindeeHTTPError):
|
|
86
|
+
"""API Client HTTP exception."""
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
class MindeeHTTPServerError(MindeeHTTPError):
|
|
90
|
+
"""API Server HTTP exception."""
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
def handle_error(url: str, response: StringDict) -> MindeeHTTPError:
|
|
94
|
+
"""
|
|
95
|
+
Creates an appropriate HTTP error exception, based on retrieved HTTP error code.
|
|
96
|
+
|
|
97
|
+
:param url: url of the product
|
|
98
|
+
:param response: StringDict
|
|
99
|
+
"""
|
|
100
|
+
error_obj = create_error_obj(response)
|
|
101
|
+
if not isinstance(response, str) and ( # type: ignore
|
|
102
|
+
"status_code" in response
|
|
103
|
+
and (
|
|
104
|
+
isinstance(response["status_code"], int)
|
|
105
|
+
or response["status_code"].isdigit()
|
|
106
|
+
)
|
|
107
|
+
):
|
|
108
|
+
code = int(response["status_code"])
|
|
109
|
+
else:
|
|
110
|
+
code = 500
|
|
111
|
+
if 400 <= code <= 499:
|
|
112
|
+
return MindeeHTTPClientError(error_obj, url, code)
|
|
113
|
+
if 500 <= code <= 599:
|
|
114
|
+
return MindeeHTTPServerError(error_obj, url, code)
|
|
115
|
+
return MindeeHTTPError(error_obj, url, code)
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
from mindee.geometry.bbox import BBox, get_bbox
|
|
2
|
+
from mindee.geometry.minmax import MinMax, get_min_max_x, get_min_max_y
|
|
3
|
+
from mindee.geometry.point import Point, Points
|
|
4
|
+
from mindee.geometry.polygon import (
|
|
5
|
+
Polygon,
|
|
6
|
+
merge_polygons,
|
|
7
|
+
)
|
|
8
|
+
from mindee.geometry.polygon_utils import get_centroid, is_point_in_x, is_point_in_y
|
|
9
|
+
from mindee.geometry.quadrilateral import (
|
|
10
|
+
Quadrilateral,
|
|
11
|
+
get_bounding_box,
|
|
12
|
+
quadrilateral_from_prediction,
|
|
13
|
+
)
|
|
14
|
+
|
|
15
|
+
__all__ = [
|
|
16
|
+
"BBox",
|
|
17
|
+
"MinMax",
|
|
18
|
+
"Point",
|
|
19
|
+
"Points",
|
|
20
|
+
"Polygon",
|
|
21
|
+
"Quadrilateral",
|
|
22
|
+
"get_bbox",
|
|
23
|
+
"get_bounding_box",
|
|
24
|
+
"get_centroid",
|
|
25
|
+
"get_min_max_x",
|
|
26
|
+
"get_min_max_y",
|
|
27
|
+
"is_point_in_x",
|
|
28
|
+
"is_point_in_y",
|
|
29
|
+
"merge_polygons",
|
|
30
|
+
"quadrilateral_from_prediction",
|
|
31
|
+
]
|
mindee/geometry/bbox.py
ADDED
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
from typing import NamedTuple
|
|
2
|
+
|
|
3
|
+
from mindee.geometry.point import Points
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class BBox(NamedTuple):
|
|
7
|
+
"""Contains exactly 4 coordinates."""
|
|
8
|
+
|
|
9
|
+
x_min: float
|
|
10
|
+
"""Minimum X coordinate."""
|
|
11
|
+
y_min: float
|
|
12
|
+
"""Minimum Y coordinate."""
|
|
13
|
+
x_max: float
|
|
14
|
+
"""Maximum X coordinate."""
|
|
15
|
+
y_max: float
|
|
16
|
+
"""Maximum Y coordinate."""
|
|
17
|
+
|
|
18
|
+
@property
|
|
19
|
+
def width(self) -> float:
|
|
20
|
+
"""The width of the BBox."""
|
|
21
|
+
return self.x_max - self.x_min
|
|
22
|
+
|
|
23
|
+
@property
|
|
24
|
+
def height(self) -> float:
|
|
25
|
+
"""The height of the BBox."""
|
|
26
|
+
return self.y_max - self.y_min
|
|
27
|
+
|
|
28
|
+
@property
|
|
29
|
+
def area(self) -> float:
|
|
30
|
+
"""The area of the BBox."""
|
|
31
|
+
return self.width * self.height
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def get_bbox(points: Points) -> BBox:
|
|
35
|
+
"""
|
|
36
|
+
Given a sequence of points, calculate a bbox that encompasses all points.
|
|
37
|
+
|
|
38
|
+
:param points: Polygon to process.
|
|
39
|
+
:return: A bbox that encompasses all points
|
|
40
|
+
"""
|
|
41
|
+
y_min = min(v[1] for v in points)
|
|
42
|
+
y_max = max(v[1] for v in points)
|
|
43
|
+
x_min = min(v[0] for v in points)
|
|
44
|
+
x_max = max(v[0] for v in points)
|
|
45
|
+
return BBox(x_min, y_min, x_max, y_max)
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def merge_bbox(bbox_1: BBox, bbox_2: BBox) -> BBox:
|
|
49
|
+
"""Merges two BBox."""
|
|
50
|
+
return BBox(
|
|
51
|
+
min(bbox_1.x_min, bbox_2.x_min),
|
|
52
|
+
min(bbox_1.y_min, bbox_2.y_min),
|
|
53
|
+
max(bbox_1.x_max, bbox_2.x_max),
|
|
54
|
+
max(bbox_1.y_max, bbox_2.y_max),
|
|
55
|
+
)
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def extend_bbox(bbox: BBox, points: Points) -> BBox:
|
|
59
|
+
"""
|
|
60
|
+
Given a BBox and a sequence of points, calculate the surrounding bbox that encompasses all.
|
|
61
|
+
|
|
62
|
+
:param bbox: initial BBox to extend.
|
|
63
|
+
:param points: Sequence of points to process. Accepts polygons and similar
|
|
64
|
+
"""
|
|
65
|
+
all_points = []
|
|
66
|
+
for point in points:
|
|
67
|
+
all_points.append(point)
|
|
68
|
+
|
|
69
|
+
y_min = min(v[1] for v in all_points)
|
|
70
|
+
y_max = max(v[1] for v in all_points)
|
|
71
|
+
x_min = min(v[0] for v in all_points)
|
|
72
|
+
x_max = max(v[0] for v in all_points)
|
|
73
|
+
return merge_bbox(bbox, BBox(x_min, y_min, x_max, y_max))
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
from typing import NamedTuple
|
|
2
|
+
|
|
3
|
+
from mindee.geometry.point import Points
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class MinMax(NamedTuple):
|
|
7
|
+
"""A set of minimum and maximum values."""
|
|
8
|
+
|
|
9
|
+
min: float
|
|
10
|
+
"""Minimum"""
|
|
11
|
+
max: float
|
|
12
|
+
"""Maximum"""
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def get_min_max_y(points: Points) -> MinMax:
|
|
16
|
+
"""
|
|
17
|
+
Get the maximum and minimum Y value given a sequence of points.
|
|
18
|
+
|
|
19
|
+
:param points: List of points
|
|
20
|
+
"""
|
|
21
|
+
y_coords = [y for _, y in points]
|
|
22
|
+
return MinMax(min=min(y_coords), max=max(y_coords))
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def get_min_max_x(points: Points) -> MinMax:
|
|
26
|
+
"""
|
|
27
|
+
Get the maximum and minimum Y value given a sequence of points.
|
|
28
|
+
|
|
29
|
+
:param points: List of points
|
|
30
|
+
"""
|
|
31
|
+
x_coords = [x for x, _ in points]
|
|
32
|
+
return MinMax(min=min(x_coords), max=max(x_coords))
|
mindee/geometry/point.py
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
from collections.abc import Sequence
|
|
2
|
+
from typing import NamedTuple
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
class Point(NamedTuple):
|
|
6
|
+
"""A relative set of coordinates (X, Y) on the document."""
|
|
7
|
+
|
|
8
|
+
x: float
|
|
9
|
+
"""X coordinate"""
|
|
10
|
+
y: float
|
|
11
|
+
"""Y coordinate"""
|
|
12
|
+
|
|
13
|
+
def __str__(self) -> str:
|
|
14
|
+
return f"({self.x},{self.y})"
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
Points = Sequence[Point]
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
from collections.abc import Sequence
|
|
2
|
+
|
|
3
|
+
from mindee.error.geometry_error import MindeeGeometryError
|
|
4
|
+
from mindee.geometry.minmax import get_min_max_x, get_min_max_y
|
|
5
|
+
from mindee.geometry.point import Point
|
|
6
|
+
from mindee.geometry.polygon_utils import get_centroid, is_point_in_x, is_point_in_y
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class Polygon(list[Point]):
|
|
10
|
+
"""
|
|
11
|
+
Contains any number of vertex coordinates (Points).
|
|
12
|
+
|
|
13
|
+
Inherits from base class ``list`` so is compatible with type ``Points``.
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
def __init__(self, vertices: list | None = None):
|
|
17
|
+
# we should NOT allow the creation of invalid polygons, but it would be a breaking change
|
|
18
|
+
if not vertices:
|
|
19
|
+
vertices = []
|
|
20
|
+
else:
|
|
21
|
+
vertices = [Point(point[0], point[1]) for point in vertices]
|
|
22
|
+
super().__init__(vertices)
|
|
23
|
+
|
|
24
|
+
def _raise_if_invalid(self) -> None:
|
|
25
|
+
if len(self) < 3:
|
|
26
|
+
raise MindeeGeometryError("A polygon must have at least 3 vertices")
|
|
27
|
+
|
|
28
|
+
@property
|
|
29
|
+
def centroid(self) -> Point:
|
|
30
|
+
"""The central point (centroid) of the polygon."""
|
|
31
|
+
self._raise_if_invalid()
|
|
32
|
+
return get_centroid(self)
|
|
33
|
+
|
|
34
|
+
def is_point_in_x(self, point: Point) -> bool:
|
|
35
|
+
"""
|
|
36
|
+
Determine if the Point is in the Polygon's X-axis.
|
|
37
|
+
|
|
38
|
+
:param point: Point to compare
|
|
39
|
+
"""
|
|
40
|
+
self._raise_if_invalid()
|
|
41
|
+
min_x, max_x = get_min_max_x(self)
|
|
42
|
+
return is_point_in_x(point, min_x, max_x)
|
|
43
|
+
|
|
44
|
+
def is_point_in_y(self, point: Point) -> bool:
|
|
45
|
+
"""
|
|
46
|
+
Determine if the Point is in the Polygon's Y-axis.
|
|
47
|
+
|
|
48
|
+
:param point: Point to compare
|
|
49
|
+
"""
|
|
50
|
+
self._raise_if_invalid()
|
|
51
|
+
min_y, max_y = get_min_max_y(self)
|
|
52
|
+
return is_point_in_y(point, min_y, max_y)
|
|
53
|
+
|
|
54
|
+
def __str__(self):
|
|
55
|
+
return "(" + ", ".join(str(p) for p in self) + ")"
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def merge_polygons(vertices: Sequence[Polygon]) -> Polygon:
|
|
59
|
+
"""
|
|
60
|
+
Given a sequence of polygons, calculate a polygon box that encompasses all polygons.
|
|
61
|
+
|
|
62
|
+
:param vertices: List of polygons
|
|
63
|
+
:return: A bounding box that encompasses all polygons
|
|
64
|
+
"""
|
|
65
|
+
y_min = min(y for v in vertices for _, y in v)
|
|
66
|
+
y_max = max(y for v in vertices for _, y in v)
|
|
67
|
+
x_min = min(x for v in vertices for x, _ in v)
|
|
68
|
+
x_max = max(x for v in vertices for x, _ in v)
|
|
69
|
+
return Polygon(
|
|
70
|
+
[
|
|
71
|
+
Point(x_min, y_min),
|
|
72
|
+
Point(x_max, y_min),
|
|
73
|
+
Point(x_max, y_max),
|
|
74
|
+
Point(x_min, y_max),
|
|
75
|
+
]
|
|
76
|
+
)
|