endoreg-db 0.8.9.2__py3-none-any.whl → 0.8.9.10__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.
Potentially problematic release.
This version of endoreg-db might be problematic. Click here for more details.
- endoreg_db/admin.py +10 -5
- endoreg_db/apps.py +4 -7
- endoreg_db/authz/auth.py +1 -0
- endoreg_db/authz/backends.py +1 -1
- endoreg_db/authz/management/commands/list_routes.py +2 -0
- endoreg_db/authz/middleware.py +8 -7
- endoreg_db/authz/permissions.py +21 -10
- endoreg_db/authz/policy.py +14 -19
- endoreg_db/authz/views_auth.py +14 -10
- endoreg_db/codemods/rename_datetime_fields.py +8 -1
- endoreg_db/exceptions.py +5 -2
- endoreg_db/forms/__init__.py +0 -1
- endoreg_db/forms/examination_form.py +4 -3
- endoreg_db/forms/patient_finding_intervention_form.py +30 -8
- endoreg_db/forms/patient_form.py +9 -13
- endoreg_db/forms/questionnaires/__init__.py +1 -1
- endoreg_db/forms/settings/__init__.py +4 -1
- endoreg_db/forms/unit.py +2 -1
- endoreg_db/helpers/count_db.py +17 -14
- endoreg_db/helpers/default_objects.py +2 -1
- endoreg_db/helpers/download_segmentation_model.py +4 -3
- endoreg_db/helpers/interact.py +0 -5
- endoreg_db/helpers/test_video_helper.py +33 -25
- endoreg_db/import_files/__init__.py +1 -1
- endoreg_db/import_files/context/__init__.py +1 -1
- endoreg_db/import_files/context/default_sensitive_meta.py +11 -9
- endoreg_db/import_files/context/ensure_center.py +4 -4
- endoreg_db/import_files/context/file_lock.py +3 -3
- endoreg_db/import_files/context/import_context.py +11 -12
- endoreg_db/import_files/context/validate_directories.py +1 -0
- endoreg_db/import_files/file_storage/create_report_file.py +57 -34
- endoreg_db/import_files/file_storage/create_video_file.py +64 -35
- endoreg_db/import_files/file_storage/sensitive_meta_storage.py +5 -2
- endoreg_db/import_files/file_storage/state_management.py +89 -122
- endoreg_db/import_files/file_storage/storage.py +5 -1
- endoreg_db/import_files/processing/report_processing/report_anonymization.py +24 -19
- endoreg_db/import_files/processing/sensitive_meta_adapter.py +3 -3
- endoreg_db/import_files/processing/video_processing/video_anonymization.py +18 -18
- endoreg_db/import_files/pseudonymization/k_anonymity.py +8 -9
- endoreg_db/import_files/pseudonymization/k_pseudonymity.py +16 -5
- endoreg_db/import_files/report_import_service.py +36 -30
- endoreg_db/import_files/video_import_service.py +27 -23
- endoreg_db/logger_conf.py +56 -40
- endoreg_db/management/__init__.py +1 -1
- endoreg_db/management/commands/__init__.py +1 -1
- endoreg_db/management/commands/check_auth.py +45 -38
- endoreg_db/management/commands/create_model_meta_from_huggingface.py +53 -2
- endoreg_db/management/commands/create_multilabel_model_meta.py +54 -19
- endoreg_db/management/commands/fix_missing_patient_data.py +105 -71
- endoreg_db/management/commands/fix_video_paths.py +75 -54
- endoreg_db/management/commands/import_report.py +1 -3
- endoreg_db/management/commands/list_routes.py +2 -0
- endoreg_db/management/commands/load_ai_model_data.py +8 -2
- endoreg_db/management/commands/load_ai_model_label_data.py +0 -1
- endoreg_db/management/commands/load_center_data.py +3 -3
- endoreg_db/management/commands/load_distribution_data.py +35 -38
- endoreg_db/management/commands/load_endoscope_data.py +0 -3
- endoreg_db/management/commands/load_examination_data.py +20 -4
- endoreg_db/management/commands/load_finding_data.py +18 -3
- endoreg_db/management/commands/load_gender_data.py +17 -24
- endoreg_db/management/commands/load_green_endoscopy_wuerzburg_data.py +95 -85
- endoreg_db/management/commands/load_information_source.py +0 -3
- endoreg_db/management/commands/load_lab_value_data.py +14 -3
- endoreg_db/management/commands/load_legacy_data.py +303 -0
- endoreg_db/management/commands/load_name_data.py +1 -2
- endoreg_db/management/commands/load_pdf_type_data.py +4 -8
- endoreg_db/management/commands/load_profession_data.py +0 -1
- endoreg_db/management/commands/load_report_reader_flag_data.py +0 -4
- endoreg_db/management/commands/load_requirement_data.py +6 -2
- endoreg_db/management/commands/load_unit_data.py +0 -4
- endoreg_db/management/commands/load_user_groups.py +5 -7
- endoreg_db/management/commands/model_input.py +169 -0
- endoreg_db/management/commands/register_ai_model.py +22 -16
- endoreg_db/management/commands/setup_endoreg_db.py +110 -32
- endoreg_db/management/commands/storage_management.py +14 -8
- endoreg_db/management/commands/summarize_db_content.py +154 -63
- endoreg_db/management/commands/train_image_multilabel_model.py +144 -0
- endoreg_db/management/commands/validate_video_files.py +82 -50
- endoreg_db/management/commands/video_validation.py +4 -6
- endoreg_db/migrations/0001_initial.py +112 -63
- endoreg_db/models/__init__.py +8 -0
- endoreg_db/models/administration/ai/active_model.py +5 -5
- endoreg_db/models/administration/ai/ai_model.py +41 -18
- endoreg_db/models/administration/ai/model_type.py +1 -0
- endoreg_db/models/administration/case/case.py +22 -22
- endoreg_db/models/administration/center/__init__.py +5 -5
- endoreg_db/models/administration/center/center.py +6 -2
- endoreg_db/models/administration/center/center_resource.py +18 -4
- endoreg_db/models/administration/center/center_shift.py +3 -1
- endoreg_db/models/administration/center/center_waste.py +6 -2
- endoreg_db/models/administration/person/__init__.py +1 -1
- endoreg_db/models/administration/person/employee/__init__.py +1 -1
- endoreg_db/models/administration/person/employee/employee_type.py +3 -1
- endoreg_db/models/administration/person/examiner/__init__.py +1 -1
- endoreg_db/models/administration/person/examiner/examiner.py +10 -2
- endoreg_db/models/administration/person/names/first_name.py +6 -4
- endoreg_db/models/administration/person/names/last_name.py +4 -3
- endoreg_db/models/administration/person/patient/__init__.py +1 -1
- endoreg_db/models/administration/person/patient/patient.py +0 -1
- endoreg_db/models/administration/person/patient/patient_external_id.py +0 -1
- endoreg_db/models/administration/person/person.py +1 -1
- endoreg_db/models/administration/product/__init__.py +7 -6
- endoreg_db/models/administration/product/product.py +6 -2
- endoreg_db/models/administration/product/product_group.py +9 -7
- endoreg_db/models/administration/product/product_material.py +9 -2
- endoreg_db/models/administration/product/reference_product.py +64 -15
- endoreg_db/models/administration/qualification/qualification.py +3 -1
- endoreg_db/models/administration/shift/shift.py +3 -1
- endoreg_db/models/administration/shift/shift_type.py +12 -4
- endoreg_db/models/aidataset/__init__.py +5 -0
- endoreg_db/models/aidataset/aidataset.py +193 -0
- endoreg_db/models/label/__init__.py +1 -1
- endoreg_db/models/label/label.py +10 -2
- endoreg_db/models/label/label_set.py +3 -1
- endoreg_db/models/label/label_video_segment/_create_from_video.py +6 -2
- endoreg_db/models/label/label_video_segment/label_video_segment.py +148 -44
- endoreg_db/models/media/__init__.py +12 -5
- endoreg_db/models/media/frame/__init__.py +1 -1
- endoreg_db/models/media/frame/frame.py +34 -8
- endoreg_db/models/media/pdf/__init__.py +2 -1
- endoreg_db/models/media/pdf/raw_pdf.py +11 -4
- endoreg_db/models/media/pdf/report_file.py +6 -2
- endoreg_db/models/media/pdf/report_reader/__init__.py +3 -3
- endoreg_db/models/media/pdf/report_reader/report_reader_flag.py +15 -5
- endoreg_db/models/media/video/create_from_file.py +20 -41
- endoreg_db/models/media/video/pipe_1.py +75 -30
- endoreg_db/models/media/video/pipe_2.py +37 -12
- endoreg_db/models/media/video/video_file.py +36 -24
- endoreg_db/models/media/video/video_file_ai.py +235 -70
- endoreg_db/models/media/video/video_file_anonymize.py +240 -65
- endoreg_db/models/media/video/video_file_frames/_bulk_create_frames.py +6 -1
- endoreg_db/models/media/video/video_file_frames/_create_frame_object.py +3 -1
- endoreg_db/models/media/video/video_file_frames/_delete_frames.py +30 -9
- endoreg_db/models/media/video/video_file_frames/_extract_frames.py +95 -29
- endoreg_db/models/media/video/video_file_frames/_get_frame.py +13 -3
- endoreg_db/models/media/video/video_file_frames/_get_frame_path.py +4 -1
- endoreg_db/models/media/video/video_file_frames/_get_frame_paths.py +15 -3
- endoreg_db/models/media/video/video_file_frames/_get_frame_range.py +15 -3
- endoreg_db/models/media/video/video_file_frames/_get_frames.py +7 -2
- endoreg_db/models/media/video/video_file_frames/_initialize_frames.py +109 -23
- endoreg_db/models/media/video/video_file_frames/_manage_frame_range.py +111 -27
- endoreg_db/models/media/video/video_file_frames/_mark_frames_extracted_status.py +46 -13
- endoreg_db/models/media/video/video_file_io.py +85 -33
- endoreg_db/models/media/video/video_file_meta/__init__.py +6 -6
- endoreg_db/models/media/video/video_file_meta/get_crop_template.py +17 -4
- endoreg_db/models/media/video/video_file_meta/get_endo_roi.py +28 -7
- endoreg_db/models/media/video/video_file_meta/get_fps.py +46 -13
- endoreg_db/models/media/video/video_file_meta/initialize_video_specs.py +81 -20
- endoreg_db/models/media/video/video_file_meta/text_meta.py +61 -20
- endoreg_db/models/media/video/video_file_meta/video_meta.py +40 -12
- endoreg_db/models/media/video/video_file_segments.py +118 -27
- endoreg_db/models/media/video/video_metadata.py +25 -6
- endoreg_db/models/media/video/video_processing.py +54 -15
- endoreg_db/models/medical/__init__.py +3 -13
- endoreg_db/models/medical/contraindication/__init__.py +3 -1
- endoreg_db/models/medical/disease.py +18 -6
- endoreg_db/models/medical/event.py +6 -2
- endoreg_db/models/medical/examination/__init__.py +5 -1
- endoreg_db/models/medical/examination/examination.py +22 -6
- endoreg_db/models/medical/examination/examination_indication.py +23 -7
- endoreg_db/models/medical/examination/examination_time.py +6 -2
- endoreg_db/models/medical/finding/__init__.py +3 -1
- endoreg_db/models/medical/finding/finding.py +37 -12
- endoreg_db/models/medical/finding/finding_classification.py +27 -8
- endoreg_db/models/medical/finding/finding_intervention.py +19 -6
- endoreg_db/models/medical/finding/finding_type.py +3 -1
- endoreg_db/models/medical/hardware/__init__.py +1 -1
- endoreg_db/models/medical/hardware/endoscope.py +14 -2
- endoreg_db/models/medical/laboratory/__init__.py +1 -1
- endoreg_db/models/medical/laboratory/lab_value.py +139 -39
- endoreg_db/models/medical/medication/__init__.py +7 -3
- endoreg_db/models/medical/medication/medication.py +3 -1
- endoreg_db/models/medical/medication/medication_indication.py +3 -1
- endoreg_db/models/medical/medication/medication_indication_type.py +11 -3
- endoreg_db/models/medical/medication/medication_intake_time.py +3 -1
- endoreg_db/models/medical/medication/medication_schedule.py +3 -1
- endoreg_db/models/medical/patient/__init__.py +2 -10
- endoreg_db/models/medical/patient/medication_examples.py +3 -14
- endoreg_db/models/medical/patient/patient_disease.py +17 -5
- endoreg_db/models/medical/patient/patient_event.py +12 -4
- endoreg_db/models/medical/patient/patient_examination.py +52 -15
- endoreg_db/models/medical/patient/patient_examination_indication.py +15 -4
- endoreg_db/models/medical/patient/patient_finding.py +105 -29
- endoreg_db/models/medical/patient/patient_finding_classification.py +41 -12
- endoreg_db/models/medical/patient/patient_finding_intervention.py +11 -3
- endoreg_db/models/medical/patient/patient_lab_sample.py +6 -2
- endoreg_db/models/medical/patient/patient_lab_value.py +42 -10
- endoreg_db/models/medical/patient/patient_medication.py +25 -7
- endoreg_db/models/medical/patient/patient_medication_schedule.py +34 -10
- endoreg_db/models/metadata/model_meta.py +40 -12
- endoreg_db/models/metadata/model_meta_logic.py +51 -16
- endoreg_db/models/metadata/sensitive_meta.py +65 -28
- endoreg_db/models/metadata/sensitive_meta_logic.py +28 -26
- endoreg_db/models/metadata/video_meta.py +146 -39
- endoreg_db/models/metadata/video_prediction_logic.py +70 -21
- endoreg_db/models/metadata/video_prediction_meta.py +80 -27
- endoreg_db/models/operation_log.py +63 -0
- endoreg_db/models/other/__init__.py +10 -10
- endoreg_db/models/other/distribution/__init__.py +9 -7
- endoreg_db/models/other/distribution/base_value_distribution.py +3 -1
- endoreg_db/models/other/distribution/date_value_distribution.py +19 -5
- endoreg_db/models/other/distribution/multiple_categorical_value_distribution.py +3 -1
- endoreg_db/models/other/distribution/numeric_value_distribution.py +34 -9
- endoreg_db/models/other/emission/__init__.py +1 -1
- endoreg_db/models/other/emission/emission_factor.py +9 -3
- endoreg_db/models/other/information_source.py +15 -5
- endoreg_db/models/other/material.py +3 -1
- endoreg_db/models/other/transport_route.py +3 -1
- endoreg_db/models/other/unit.py +6 -2
- endoreg_db/models/report/report.py +0 -1
- endoreg_db/models/requirement/requirement.py +84 -27
- endoreg_db/models/requirement/requirement_error.py +5 -6
- endoreg_db/models/requirement/requirement_evaluation/__init__.py +1 -1
- endoreg_db/models/requirement/requirement_evaluation/evaluate_with_dependencies.py +8 -8
- endoreg_db/models/requirement/requirement_evaluation/get_values.py +3 -3
- endoreg_db/models/requirement/requirement_evaluation/requirement_type_parser.py +24 -8
- endoreg_db/models/requirement/requirement_operator.py +28 -8
- endoreg_db/models/requirement/requirement_set.py +34 -11
- endoreg_db/models/state/__init__.py +1 -0
- endoreg_db/models/state/audit_ledger.py +9 -2
- endoreg_db/models/{media → state}/processing_history/__init__.py +1 -3
- endoreg_db/models/state/processing_history/processing_history.py +136 -0
- endoreg_db/models/state/raw_pdf.py +0 -1
- endoreg_db/models/state/video.py +2 -4
- endoreg_db/models/utils.py +4 -2
- endoreg_db/queries/__init__.py +2 -6
- endoreg_db/queries/annotations/__init__.py +1 -3
- endoreg_db/queries/annotations/legacy.py +37 -26
- endoreg_db/root_urls.py +3 -4
- endoreg_db/schemas/examination_evaluation.py +3 -0
- endoreg_db/serializers/Frames_NICE_and_PARIS_classifications.py +249 -163
- endoreg_db/serializers/__init__.py +2 -8
- endoreg_db/serializers/administration/__init__.py +1 -2
- endoreg_db/serializers/administration/ai/__init__.py +0 -1
- endoreg_db/serializers/administration/ai/active_model.py +3 -1
- endoreg_db/serializers/administration/ai/ai_model.py +5 -3
- endoreg_db/serializers/administration/ai/model_type.py +3 -1
- endoreg_db/serializers/administration/center.py +7 -2
- endoreg_db/serializers/administration/gender.py +4 -2
- endoreg_db/serializers/anonymization.py +13 -13
- endoreg_db/serializers/evaluation/examination_evaluation.py +0 -1
- endoreg_db/serializers/examination/__init__.py +1 -1
- endoreg_db/serializers/examination/base.py +12 -13
- endoreg_db/serializers/examination/dropdown.py +6 -7
- endoreg_db/serializers/examination_serializer.py +3 -6
- endoreg_db/serializers/finding/__init__.py +1 -1
- endoreg_db/serializers/finding/finding.py +14 -7
- endoreg_db/serializers/finding_classification/__init__.py +3 -3
- endoreg_db/serializers/finding_classification/choice.py +3 -3
- endoreg_db/serializers/finding_classification/classification.py +2 -4
- endoreg_db/serializers/label_video_segment/__init__.py +5 -3
- endoreg_db/serializers/{label → label_video_segment}/image_classification_annotation.py +5 -5
- endoreg_db/serializers/label_video_segment/label/__init__.py +6 -0
- endoreg_db/serializers/{label → label_video_segment/label}/label.py +1 -1
- endoreg_db/serializers/label_video_segment/label_video_segment.py +338 -228
- endoreg_db/serializers/meta/__init__.py +1 -2
- endoreg_db/serializers/meta/sensitive_meta_detail.py +28 -13
- endoreg_db/serializers/meta/sensitive_meta_update.py +51 -46
- endoreg_db/serializers/meta/sensitive_meta_verification.py +19 -16
- endoreg_db/serializers/misc/__init__.py +2 -2
- endoreg_db/serializers/misc/file_overview.py +11 -7
- endoreg_db/serializers/misc/stats.py +10 -8
- endoreg_db/serializers/misc/translatable_field_mix_in.py +6 -6
- endoreg_db/serializers/misc/upload_job.py +32 -29
- endoreg_db/serializers/patient/__init__.py +2 -1
- endoreg_db/serializers/patient/patient.py +32 -15
- endoreg_db/serializers/patient/patient_dropdown.py +11 -3
- endoreg_db/serializers/patient_examination/__init__.py +1 -1
- endoreg_db/serializers/patient_examination/patient_examination.py +67 -40
- endoreg_db/serializers/patient_finding/__init__.py +1 -1
- endoreg_db/serializers/patient_finding/patient_finding.py +2 -1
- endoreg_db/serializers/patient_finding/patient_finding_classification.py +17 -9
- endoreg_db/serializers/patient_finding/patient_finding_detail.py +26 -17
- endoreg_db/serializers/patient_finding/patient_finding_intervention.py +7 -5
- endoreg_db/serializers/patient_finding/patient_finding_list.py +10 -11
- endoreg_db/serializers/patient_finding/patient_finding_write.py +36 -27
- endoreg_db/serializers/pdf/__init__.py +1 -3
- endoreg_db/serializers/requirements/requirement_schema.py +1 -6
- endoreg_db/serializers/sensitive_meta_serializer.py +100 -81
- endoreg_db/serializers/video/__init__.py +2 -2
- endoreg_db/serializers/video/{segmentation.py → video_file.py} +66 -47
- endoreg_db/serializers/video/video_file_brief.py +6 -2
- endoreg_db/serializers/video/video_file_detail.py +36 -23
- endoreg_db/serializers/video/video_file_list.py +4 -2
- endoreg_db/serializers/video/video_processing_history.py +54 -50
- endoreg_db/services/__init__.py +1 -1
- endoreg_db/services/anonymization.py +2 -2
- endoreg_db/services/examination_evaluation.py +40 -17
- endoreg_db/services/model_meta_from_hf.py +76 -0
- endoreg_db/services/polling_coordinator.py +101 -70
- endoreg_db/services/pseudonym_service.py +27 -22
- endoreg_db/services/report_import.py +6 -3
- endoreg_db/services/segment_sync.py +75 -59
- endoreg_db/services/video_import.py +6 -7
- endoreg_db/urls/__init__.py +2 -2
- endoreg_db/urls/ai.py +7 -25
- endoreg_db/urls/anonymization.py +61 -15
- endoreg_db/urls/auth.py +4 -4
- endoreg_db/urls/classification.py +4 -9
- endoreg_db/urls/examination.py +27 -18
- endoreg_db/urls/media.py +27 -34
- endoreg_db/urls/patient.py +11 -7
- endoreg_db/urls/requirements.py +3 -1
- endoreg_db/urls/root_urls.py +2 -3
- endoreg_db/urls/stats.py +24 -16
- endoreg_db/urls/upload.py +3 -11
- endoreg_db/utils/__init__.py +14 -15
- endoreg_db/utils/ai/__init__.py +1 -1
- endoreg_db/utils/ai/data_loader_for_model_input.py +262 -0
- endoreg_db/utils/ai/data_loader_for_model_training.py +262 -0
- endoreg_db/utils/ai/get.py +2 -1
- endoreg_db/utils/ai/inference_dataset.py +14 -15
- endoreg_db/utils/ai/model_training/config.py +117 -0
- endoreg_db/utils/ai/model_training/dataset.py +74 -0
- endoreg_db/utils/ai/model_training/losses.py +68 -0
- endoreg_db/utils/ai/model_training/metrics.py +78 -0
- endoreg_db/utils/ai/model_training/model_backbones.py +155 -0
- endoreg_db/utils/ai/model_training/model_gastronet_resnet.py +118 -0
- endoreg_db/utils/ai/model_training/trainer_gastronet_multilabel.py +771 -0
- endoreg_db/utils/ai/multilabel_classification_net.py +21 -6
- endoreg_db/utils/ai/predict.py +4 -4
- endoreg_db/utils/ai/preprocess.py +19 -11
- endoreg_db/utils/calc_duration_seconds.py +4 -4
- endoreg_db/utils/case_generator/lab_sample_factory.py +3 -4
- endoreg_db/utils/check_video_files.py +74 -47
- endoreg_db/utils/cropping.py +10 -9
- endoreg_db/utils/dataloader.py +11 -3
- endoreg_db/utils/dates.py +3 -4
- endoreg_db/utils/defaults/set_default_center.py +7 -6
- endoreg_db/utils/env.py +6 -2
- endoreg_db/utils/extract_specific_frames.py +24 -9
- endoreg_db/utils/file_operations.py +30 -18
- endoreg_db/utils/fix_video_path_direct.py +57 -41
- endoreg_db/utils/frame_anonymization_utils.py +157 -157
- endoreg_db/utils/hashs.py +3 -18
- endoreg_db/utils/links/requirement_link.py +96 -52
- endoreg_db/utils/ocr.py +30 -25
- endoreg_db/utils/operation_log.py +61 -0
- endoreg_db/utils/parse_and_generate_yaml.py +12 -13
- endoreg_db/utils/paths.py +6 -6
- endoreg_db/utils/permissions.py +40 -24
- endoreg_db/utils/pipelines/process_video_dir.py +50 -26
- endoreg_db/utils/product/sum_emissions.py +5 -3
- endoreg_db/utils/product/sum_weights.py +4 -2
- endoreg_db/utils/pydantic_models/__init__.py +3 -4
- endoreg_db/utils/requirement_operator_logic/_old/lab_value_operators.py +207 -107
- endoreg_db/utils/requirement_operator_logic/_old/model_evaluators.py +252 -65
- endoreg_db/utils/requirement_operator_logic/new_operator_logic.py +27 -10
- endoreg_db/utils/setup_config.py +21 -5
- endoreg_db/utils/storage.py +3 -1
- endoreg_db/utils/translation.py +19 -15
- endoreg_db/utils/uuid.py +1 -0
- endoreg_db/utils/validate_endo_roi.py +12 -4
- endoreg_db/utils/validate_subcategory_dict.py +26 -24
- endoreg_db/utils/validate_video_detailed.py +207 -149
- endoreg_db/utils/video/__init__.py +7 -3
- endoreg_db/utils/video/extract_frames.py +30 -18
- endoreg_db/utils/video/names.py +11 -6
- endoreg_db/utils/video/streaming_processor.py +175 -101
- endoreg_db/utils/video/video_splitter.py +30 -19
- endoreg_db/views/Frames_NICE_and_PARIS_classifications_views.py +59 -50
- endoreg_db/views/__init__.py +0 -20
- endoreg_db/views/anonymization/__init__.py +6 -2
- endoreg_db/views/anonymization/media_management.py +2 -6
- endoreg_db/views/anonymization/overview.py +34 -1
- endoreg_db/views/anonymization/validate.py +79 -18
- endoreg_db/views/auth/__init__.py +1 -1
- endoreg_db/views/auth/keycloak.py +16 -14
- endoreg_db/views/examination/__init__.py +12 -15
- endoreg_db/views/examination/examination.py +5 -5
- endoreg_db/views/examination/examination_manifest_cache.py +5 -5
- endoreg_db/views/examination/get_finding_classification_choices.py +8 -5
- endoreg_db/views/examination/get_finding_classifications.py +9 -7
- endoreg_db/views/examination/get_findings.py +8 -10
- endoreg_db/views/examination/get_instruments.py +3 -2
- endoreg_db/views/examination/get_interventions.py +1 -1
- endoreg_db/views/finding/__init__.py +2 -2
- endoreg_db/views/finding/finding.py +58 -54
- endoreg_db/views/finding/get_classifications.py +1 -1
- endoreg_db/views/finding/get_interventions.py +1 -1
- endoreg_db/views/finding_classification/__init__.py +5 -5
- endoreg_db/views/finding_classification/finding_classification.py +5 -6
- endoreg_db/views/finding_classification/get_classification_choices.py +3 -4
- endoreg_db/views/media/__init__.py +13 -13
- endoreg_db/views/media/pdf_media.py +9 -9
- endoreg_db/views/media/sensitive_metadata.py +10 -7
- endoreg_db/views/media/video_media.py +4 -4
- endoreg_db/views/meta/__init__.py +1 -1
- endoreg_db/views/meta/sensitive_meta_list.py +20 -22
- endoreg_db/views/meta/sensitive_meta_verification.py +14 -11
- endoreg_db/views/misc/__init__.py +6 -34
- endoreg_db/views/misc/center.py +2 -1
- endoreg_db/views/misc/csrf.py +2 -1
- endoreg_db/views/misc/gender.py +2 -1
- endoreg_db/views/misc/stats.py +141 -106
- endoreg_db/views/patient/__init__.py +1 -3
- endoreg_db/views/patient/patient.py +141 -99
- endoreg_db/views/patient_examination/__init__.py +5 -5
- endoreg_db/views/patient_examination/patient_examination.py +43 -42
- endoreg_db/views/patient_examination/patient_examination_create.py +10 -15
- endoreg_db/views/patient_examination/patient_examination_detail.py +12 -15
- endoreg_db/views/patient_examination/patient_examination_list.py +21 -17
- endoreg_db/views/patient_examination/video.py +114 -80
- endoreg_db/views/patient_finding/__init__.py +1 -1
- endoreg_db/views/patient_finding/patient_finding.py +17 -10
- endoreg_db/views/patient_finding/patient_finding_optimized.py +127 -95
- endoreg_db/views/patient_finding_classification/__init__.py +1 -1
- endoreg_db/views/patient_finding_classification/pfc_create.py +35 -27
- endoreg_db/views/report/reimport.py +1 -1
- endoreg_db/views/report/report_stream.py +5 -8
- endoreg_db/views/requirement/__init__.py +2 -1
- endoreg_db/views/requirement/evaluate.py +7 -9
- endoreg_db/views/requirement/lookup.py +2 -3
- endoreg_db/views/requirement/lookup_store.py +0 -1
- endoreg_db/views/requirement/requirement_utils.py +2 -4
- endoreg_db/views/stats/__init__.py +4 -4
- endoreg_db/views/stats/stats_views.py +152 -115
- endoreg_db/views/video/__init__.py +18 -27
- endoreg_db/views/{ai → video/ai}/__init__.py +2 -2
- endoreg_db/views/{ai → video/ai}/label.py +20 -16
- endoreg_db/views/video/correction.py +5 -6
- endoreg_db/views/video/reimport.py +134 -99
- endoreg_db/views/video/segments_crud.py +134 -44
- endoreg_db/views/video/video_apply_mask.py +13 -12
- endoreg_db/views/video/video_correction.py +2 -1
- endoreg_db/views/video/video_download_processed.py +15 -15
- endoreg_db/views/video/video_meta_stats.py +7 -6
- endoreg_db/views/video/video_processing_history.py +3 -2
- endoreg_db/views/video/video_remove_frames.py +13 -12
- endoreg_db/views/video/video_stream.py +110 -82
- {endoreg_db-0.8.9.2.dist-info → endoreg_db-0.8.9.10.dist-info}/METADATA +9 -3
- {endoreg_db-0.8.9.2.dist-info → endoreg_db-0.8.9.10.dist-info}/RECORD +434 -431
- endoreg_db/management/commands/import_fallback_video.py +0 -203
- endoreg_db/management/commands/import_video.py +0 -422
- endoreg_db/management/commands/import_video_with_classification.py +0 -367
- endoreg_db/models/media/processing_history/processing_history.py +0 -96
- endoreg_db/serializers/label/__init__.py +0 -7
- endoreg_db/serializers/label_video_segment/_lvs_create.py +0 -149
- endoreg_db/serializers/label_video_segment/_lvs_update.py +0 -138
- endoreg_db/serializers/label_video_segment/_lvs_validate.py +0 -149
- endoreg_db/serializers/label_video_segment/label_video_segment_annotation.py +0 -99
- endoreg_db/serializers/label_video_segment/label_video_segment_update.py +0 -163
- endoreg_db/services/__old/pdf_import.py +0 -1487
- endoreg_db/services/__old/video_import.py +0 -1306
- endoreg_db/tasks/upload_tasks.py +0 -216
- endoreg_db/tasks/video_ingest.py +0 -161
- endoreg_db/tasks/video_processing_tasks.py +0 -327
- endoreg_db/views/misc/translation.py +0 -182
- {endoreg_db-0.8.9.2.dist-info → endoreg_db-0.8.9.10.dist-info}/WHEEL +0 -0
- {endoreg_db-0.8.9.2.dist-info → endoreg_db-0.8.9.10.dist-info}/licenses/LICENSE +0 -0
|
@@ -122,7 +122,9 @@ def _infer_output_classes(weights_path: Path) -> Optional[int]:
|
|
|
122
122
|
return None
|
|
123
123
|
|
|
124
124
|
|
|
125
|
-
def _build_label_mapping(
|
|
125
|
+
def _build_label_mapping(
|
|
126
|
+
source_labels: List[str], target_labels: List[str]
|
|
127
|
+
) -> Dict[str, List[str]]:
|
|
126
128
|
if source_labels == target_labels:
|
|
127
129
|
return {label: [label] for label in target_labels}
|
|
128
130
|
|
|
@@ -142,7 +144,9 @@ def _build_label_mapping(source_labels: List[str], target_labels: List[str]) ->
|
|
|
142
144
|
return mapping
|
|
143
145
|
|
|
144
146
|
|
|
145
|
-
def _remap_prediction_dict(
|
|
147
|
+
def _remap_prediction_dict(
|
|
148
|
+
predictions: Dict[str, Any], mapping: Dict[str, List[str]]
|
|
149
|
+
) -> Dict[str, Any]:
|
|
146
150
|
remapped: Dict[str, Any] = {}
|
|
147
151
|
for target, sources in mapping.items():
|
|
148
152
|
values: List[Any] = []
|
|
@@ -167,7 +171,9 @@ def _remap_prediction_dict(predictions: Dict[str, Any], mapping: Dict[str, List[
|
|
|
167
171
|
return remapped
|
|
168
172
|
|
|
169
173
|
|
|
170
|
-
def _extract_text_from_video_frames(
|
|
174
|
+
def _extract_text_from_video_frames(
|
|
175
|
+
video: "VideoFile", frame_fraction: float = 0.001, cap: int = 15
|
|
176
|
+
) -> Optional[Dict[str, str]]:
|
|
171
177
|
"""
|
|
172
178
|
Extracts text from a sample of video frames using OCR based on processor ROIs.
|
|
173
179
|
Requires frames to be extracted. Raises ValueError on pre-condition failure.
|
|
@@ -178,31 +184,47 @@ def _extract_text_from_video_frames(video: "VideoFile", frame_fraction: float =
|
|
|
178
184
|
- Post-condition: No state changes.
|
|
179
185
|
"""
|
|
180
186
|
from endoreg_db.utils.ocr import (
|
|
181
|
-
extract_text_from_rois,
|
|
182
|
-
)
|
|
187
|
+
extract_text_from_rois, # Local import for dependency isolation
|
|
188
|
+
)
|
|
183
189
|
|
|
184
190
|
state = video.get_or_create_state() # Use State helper
|
|
185
191
|
# --- Pre-condition Check ---
|
|
186
192
|
if not state.frames_extracted:
|
|
187
193
|
# Raise exception
|
|
188
|
-
raise ValueError(
|
|
194
|
+
raise ValueError(
|
|
195
|
+
f"Frames not extracted for video {video.video_hash}. Cannot extract text."
|
|
196
|
+
)
|
|
189
197
|
# --- End Pre-condition Check ---
|
|
190
198
|
|
|
191
199
|
processor: Optional["EndoscopyProcessor"] = video.processor
|
|
192
200
|
if not processor:
|
|
193
201
|
# Raise exception
|
|
194
|
-
raise ValueError(
|
|
202
|
+
raise ValueError(
|
|
203
|
+
f"Processor not set for video {video.video_hash}. Cannot extract text."
|
|
204
|
+
)
|
|
195
205
|
|
|
196
206
|
try:
|
|
197
207
|
frame_paths = video.get_frame_paths() # Use Frame helper
|
|
198
208
|
except Exception as e:
|
|
199
|
-
logger.error(
|
|
200
|
-
|
|
209
|
+
logger.error(
|
|
210
|
+
"Error getting frame paths for video %s: %s",
|
|
211
|
+
video.video_hash,
|
|
212
|
+
e,
|
|
213
|
+
exc_info=True,
|
|
214
|
+
)
|
|
215
|
+
raise RuntimeError(
|
|
216
|
+
f"Could not get frame paths for video {video.video_hash}"
|
|
217
|
+
) from e
|
|
201
218
|
|
|
202
219
|
n_frames = len(frame_paths)
|
|
203
220
|
if n_frames == 0:
|
|
204
|
-
logger.warning(
|
|
205
|
-
|
|
221
|
+
logger.warning(
|
|
222
|
+
"No frame paths found for video %s during text extraction.",
|
|
223
|
+
video.video_hash,
|
|
224
|
+
)
|
|
225
|
+
return (
|
|
226
|
+
None # Return None if no frames, not an error condition for this function
|
|
227
|
+
)
|
|
206
228
|
|
|
207
229
|
# Determine number of frames to process
|
|
208
230
|
n_frames_to_process = max(1, int(frame_fraction * n_frames))
|
|
@@ -212,7 +234,7 @@ def _extract_text_from_video_frames(video: "VideoFile", frame_fraction: float =
|
|
|
212
234
|
"Processing %d frames (out of %d) for text extraction from video %s.",
|
|
213
235
|
n_frames_to_process,
|
|
214
236
|
n_frames,
|
|
215
|
-
video.
|
|
237
|
+
video.video_hash,
|
|
216
238
|
)
|
|
217
239
|
|
|
218
240
|
# Select evenly spaced frames
|
|
@@ -230,7 +252,13 @@ def _extract_text_from_video_frames(video: "VideoFile", frame_fraction: float =
|
|
|
230
252
|
rois_texts[roi].append(text)
|
|
231
253
|
except Exception as e:
|
|
232
254
|
# Log error but continue processing other frames
|
|
233
|
-
logger.error(
|
|
255
|
+
logger.error(
|
|
256
|
+
"Error extracting text from frame %s for video %s: %s",
|
|
257
|
+
frame_path,
|
|
258
|
+
video.video_hash,
|
|
259
|
+
e,
|
|
260
|
+
exc_info=True,
|
|
261
|
+
)
|
|
234
262
|
errors_encountered = True # Flag that an error occurred
|
|
235
263
|
|
|
236
264
|
# Determine the most frequent text for each ROI
|
|
@@ -247,17 +275,24 @@ def _extract_text_from_video_frames(video: "VideoFile", frame_fraction: float =
|
|
|
247
275
|
else:
|
|
248
276
|
most_frequent_texts[roi] = None
|
|
249
277
|
except Exception as e:
|
|
250
|
-
logger.error(
|
|
278
|
+
logger.error(
|
|
279
|
+
"Error finding most common text for ROI %s: %s", roi, e, exc_info=True
|
|
280
|
+
)
|
|
251
281
|
most_frequent_texts[roi] = None
|
|
252
282
|
|
|
253
283
|
if errors_encountered:
|
|
254
|
-
logger.warning(
|
|
284
|
+
logger.warning(
|
|
285
|
+
"Errors occurred during text extraction for some frames of video %s. Results may be incomplete.",
|
|
286
|
+
video.video_hash,
|
|
287
|
+
)
|
|
255
288
|
|
|
256
289
|
if not most_frequent_texts:
|
|
257
|
-
logger.info("No text extracted for any ROI for video %s.", video.
|
|
290
|
+
logger.info("No text extracted for any ROI for video %s.", video.video_hash)
|
|
258
291
|
return None # Return None if no text found
|
|
259
292
|
|
|
260
|
-
logger.info(
|
|
293
|
+
logger.info(
|
|
294
|
+
"Extracted text for video %s: %s", video.video_hash, most_frequent_texts
|
|
295
|
+
)
|
|
261
296
|
return most_frequent_texts
|
|
262
297
|
|
|
263
298
|
|
|
@@ -282,16 +317,26 @@ def _predict_video_pipeline(
|
|
|
282
317
|
from ...administration.ai import AiModel
|
|
283
318
|
|
|
284
319
|
try:
|
|
285
|
-
from ....utils.ai import
|
|
320
|
+
from ....utils.ai import (
|
|
321
|
+
Classifier,
|
|
322
|
+
InferenceDataset,
|
|
323
|
+
MultiLabelClassificationNet,
|
|
324
|
+
)
|
|
286
325
|
from ....utils.ai.postprocess import (
|
|
287
326
|
concat_pred_dicts,
|
|
288
327
|
find_true_pred_sequences,
|
|
289
328
|
make_smooth_preds,
|
|
290
329
|
)
|
|
291
330
|
except ImportError as e:
|
|
292
|
-
logger.error(
|
|
331
|
+
logger.error(
|
|
332
|
+
"Failed to import endo_ai components: %s. Prediction unavailable.",
|
|
333
|
+
e,
|
|
334
|
+
exc_info=True,
|
|
335
|
+
)
|
|
293
336
|
# Raise exception
|
|
294
|
-
raise ImportError(
|
|
337
|
+
raise ImportError(
|
|
338
|
+
"Failed to import required AI components for prediction."
|
|
339
|
+
) from e
|
|
295
340
|
|
|
296
341
|
if not test_run and GLOBAL_TEST_RUN:
|
|
297
342
|
test_run = True
|
|
@@ -302,48 +347,72 @@ def _predict_video_pipeline(
|
|
|
302
347
|
# --- Pre-condition Check ---
|
|
303
348
|
if not state.frames_extracted:
|
|
304
349
|
# Raise exception
|
|
305
|
-
raise ValueError(
|
|
350
|
+
raise ValueError(
|
|
351
|
+
f"Frames not extracted for video {video.video_hash}. Prediction aborted."
|
|
352
|
+
)
|
|
306
353
|
# --- End Pre-condition Check ---
|
|
307
354
|
|
|
308
355
|
# Frame directory check
|
|
309
356
|
frame_dir = video.get_frame_dir_path() # Use IO helper
|
|
310
357
|
if not frame_dir or not frame_dir.exists() or not any(frame_dir.iterdir()):
|
|
311
358
|
# Raise exception
|
|
312
|
-
raise FileNotFoundError(
|
|
359
|
+
raise FileNotFoundError(
|
|
360
|
+
f"Frame directory {frame_dir} is empty or does not exist for video {video.video_hash}. Prediction aborted."
|
|
361
|
+
)
|
|
313
362
|
|
|
314
363
|
model: Optional[AiModel] = model_meta.model
|
|
315
364
|
if not model:
|
|
316
365
|
# Raise exception
|
|
317
|
-
raise ValueError(
|
|
366
|
+
raise ValueError(
|
|
367
|
+
f"Model not found in ModelMeta {model_meta.name} (Version: {model_meta.version}) for video {video.video_hash}. Prediction aborted."
|
|
368
|
+
)
|
|
318
369
|
|
|
319
370
|
# Ensure weights file exists
|
|
320
371
|
try:
|
|
321
372
|
weights_path = Path(model_meta.weights.path)
|
|
322
373
|
if not weights_path.exists():
|
|
323
374
|
# Raise exception
|
|
324
|
-
raise FileNotFoundError(
|
|
375
|
+
raise FileNotFoundError(
|
|
376
|
+
f"Model weights file {weights_path} not found for {model_meta.name} (Video: {video.video_hash}). Prediction aborted."
|
|
377
|
+
)
|
|
325
378
|
except Exception as e:
|
|
326
|
-
logger.error(
|
|
327
|
-
|
|
379
|
+
logger.error(
|
|
380
|
+
"Error accessing model weights path for %s (Video: %s): %s",
|
|
381
|
+
model_meta.name,
|
|
382
|
+
video.video_hash,
|
|
383
|
+
e,
|
|
384
|
+
exc_info=True,
|
|
385
|
+
)
|
|
386
|
+
raise RuntimeError(
|
|
387
|
+
f"Error accessing model weights for {model_meta.name}"
|
|
388
|
+
) from e
|
|
328
389
|
|
|
329
390
|
# Get or create VideoPredictionMeta
|
|
330
391
|
try:
|
|
331
|
-
_video_prediction_meta, created = VideoPredictionMeta.objects.get_or_create(
|
|
392
|
+
_video_prediction_meta, created = VideoPredictionMeta.objects.get_or_create(
|
|
393
|
+
video_file=video, model_meta=model_meta
|
|
394
|
+
)
|
|
332
395
|
if created:
|
|
333
396
|
logger.info(
|
|
334
397
|
"Created new VideoPredictionMeta for video %s, model %s.",
|
|
335
|
-
video.
|
|
398
|
+
video.video_hash,
|
|
336
399
|
model_meta.name,
|
|
337
400
|
)
|
|
338
401
|
else:
|
|
339
402
|
logger.info(
|
|
340
403
|
"Found existing VideoPredictionMeta for video %s, model %s.",
|
|
341
|
-
video.
|
|
404
|
+
video.video_hash,
|
|
342
405
|
model_meta.name,
|
|
343
406
|
)
|
|
344
407
|
# video_prediction_meta.save() # Save is handled by get_or_create
|
|
345
408
|
except Exception as e:
|
|
346
|
-
logger.error(
|
|
409
|
+
logger.error(
|
|
410
|
+
"Failed to get or create VideoPredictionMeta for video %s, model %s: %s",
|
|
411
|
+
video.video_hash,
|
|
412
|
+
model_meta.name,
|
|
413
|
+
e,
|
|
414
|
+
exc_info=True,
|
|
415
|
+
)
|
|
347
416
|
# Raise exception
|
|
348
417
|
raise RuntimeError("Failed to get or create VideoPredictionMeta") from e
|
|
349
418
|
|
|
@@ -351,7 +420,7 @@ def _predict_video_pipeline(
|
|
|
351
420
|
logger.info(
|
|
352
421
|
"Detected stub weights at %s for video %s; skipping model inference and returning empty predictions.",
|
|
353
422
|
weights_path,
|
|
354
|
-
video.
|
|
423
|
+
video.video_hash,
|
|
355
424
|
)
|
|
356
425
|
return {}
|
|
357
426
|
|
|
@@ -363,33 +432,56 @@ def _predict_video_pipeline(
|
|
|
363
432
|
dataset_model_class = datasets.get(dataset_name)
|
|
364
433
|
if not dataset_model_class:
|
|
365
434
|
# Raise exception
|
|
366
|
-
raise ValueError(
|
|
435
|
+
raise ValueError(
|
|
436
|
+
f"Dataset class '{dataset_name}' not found for video {video.video_hash}. Prediction aborted."
|
|
437
|
+
)
|
|
367
438
|
|
|
368
439
|
try:
|
|
369
440
|
paths = video.get_frame_paths() # Use Frame helper
|
|
370
441
|
if not paths:
|
|
371
|
-
raise FileNotFoundError(
|
|
442
|
+
raise FileNotFoundError(
|
|
443
|
+
f"No frame paths returned by get_frame_paths for {frame_dir} (Video: {video.video_hash})"
|
|
444
|
+
)
|
|
372
445
|
except Exception as e:
|
|
373
|
-
logger.error(
|
|
446
|
+
logger.error(
|
|
447
|
+
"Error listing or getting frame files from %s for video %s: %s",
|
|
448
|
+
frame_dir,
|
|
449
|
+
video.video_hash,
|
|
450
|
+
e,
|
|
451
|
+
exc_info=True,
|
|
452
|
+
)
|
|
374
453
|
raise RuntimeError(f"Error getting frame paths from {frame_dir}") from e
|
|
375
454
|
|
|
376
|
-
logger.info(
|
|
455
|
+
logger.info(
|
|
456
|
+
"Found %d frame files in %s for video %s.",
|
|
457
|
+
len(paths),
|
|
458
|
+
frame_dir,
|
|
459
|
+
video.video_hash,
|
|
460
|
+
)
|
|
377
461
|
|
|
378
462
|
crop_template = video.get_crop_template() # Use Meta helper
|
|
379
463
|
string_paths = [p.as_posix() for p in paths]
|
|
380
464
|
crops = [crop_template] * len(paths) # Assuming same crop for all frames
|
|
381
465
|
|
|
382
466
|
if test_run:
|
|
383
|
-
logger.info(
|
|
467
|
+
logger.info(
|
|
468
|
+
"TEST RUN: Using first %d frames for video %s.",
|
|
469
|
+
n_test_frames,
|
|
470
|
+
video.video_hash,
|
|
471
|
+
)
|
|
384
472
|
string_paths = string_paths[:n_test_frames]
|
|
385
473
|
crops = crops[:n_test_frames]
|
|
386
474
|
if not string_paths:
|
|
387
475
|
# Raise exception
|
|
388
|
-
raise ValueError(
|
|
476
|
+
raise ValueError(
|
|
477
|
+
f"Not enough frames ({len(paths)}) for test run (required {n_test_frames}) for video {video.video_hash}."
|
|
478
|
+
)
|
|
389
479
|
|
|
390
480
|
label_names = _resolve_label_names(model_meta)
|
|
391
481
|
if not label_names:
|
|
392
|
-
raise ValueError(
|
|
482
|
+
raise ValueError(
|
|
483
|
+
f"Label set '{getattr(model_meta.labelset, 'name', 'unknown')}' has no labels configured."
|
|
484
|
+
)
|
|
393
485
|
|
|
394
486
|
outputs_hint = _infer_output_classes(weights_path)
|
|
395
487
|
|
|
@@ -428,7 +520,12 @@ def _predict_video_pipeline(
|
|
|
428
520
|
try:
|
|
429
521
|
ds_config = model_meta.get_inference_dataset_config()
|
|
430
522
|
ds = dataset_model_class(string_paths, crops, config=ds_config)
|
|
431
|
-
logger.info(
|
|
523
|
+
logger.info(
|
|
524
|
+
"Created dataset '%s' with %d items for video %s.",
|
|
525
|
+
dataset_name,
|
|
526
|
+
len(ds),
|
|
527
|
+
video.video_hash,
|
|
528
|
+
)
|
|
432
529
|
if len(ds) > 0:
|
|
433
530
|
sample = ds[0] # Get a sample for debugging shape
|
|
434
531
|
logger.debug("Sample shape: %s", getattr(sample, "shape", None))
|
|
@@ -451,7 +548,13 @@ def _predict_video_pipeline(
|
|
|
451
548
|
"labels": network_labels,
|
|
452
549
|
}
|
|
453
550
|
except Exception as e:
|
|
454
|
-
logger.error(
|
|
551
|
+
logger.error(
|
|
552
|
+
"Failed to create dataset '%s' for video %s: %s",
|
|
553
|
+
dataset_name,
|
|
554
|
+
video.video_hash,
|
|
555
|
+
e,
|
|
556
|
+
exc_info=True,
|
|
557
|
+
)
|
|
455
558
|
# Raise exception
|
|
456
559
|
raise RuntimeError(f"Failed to create dataset '{dataset_name}'") from e
|
|
457
560
|
|
|
@@ -469,11 +572,11 @@ def _predict_video_pipeline(
|
|
|
469
572
|
**load_kwargs,
|
|
470
573
|
)
|
|
471
574
|
ai_model_instance = ai_model_instance.to(device)
|
|
472
|
-
logger.info("Loaded model on GPU for video %s.", video.
|
|
575
|
+
logger.info("Loaded model on GPU for video %s.", video.video_hash)
|
|
473
576
|
except RuntimeError as cuda_err:
|
|
474
577
|
logger.warning(
|
|
475
578
|
"GPU loading failed for video %s: %s. Falling back to CPU.",
|
|
476
|
-
video.
|
|
579
|
+
video.video_hash,
|
|
477
580
|
cuda_err,
|
|
478
581
|
)
|
|
479
582
|
device = torch.device("cpu")
|
|
@@ -483,10 +586,13 @@ def _predict_video_pipeline(
|
|
|
483
586
|
**load_kwargs,
|
|
484
587
|
)
|
|
485
588
|
ai_model_instance = ai_model_instance.to(device)
|
|
486
|
-
logger.info("Loaded model on CPU for video %s.", video.
|
|
589
|
+
logger.info("Loaded model on CPU for video %s.", video.video_hash)
|
|
487
590
|
else:
|
|
488
591
|
# No CUDA available, load directly on CPU
|
|
489
|
-
logger.info(
|
|
592
|
+
logger.info(
|
|
593
|
+
"CUDA not available. Loading model on CPU for video %s.",
|
|
594
|
+
video.video_hash,
|
|
595
|
+
)
|
|
490
596
|
device = torch.device("cpu")
|
|
491
597
|
ai_model_instance = MultiLabelClassificationNet.load_from_checkpoint(
|
|
492
598
|
checkpoint_path=weights_path.as_posix(),
|
|
@@ -496,28 +602,49 @@ def _predict_video_pipeline(
|
|
|
496
602
|
ai_model_instance = ai_model_instance.to(device)
|
|
497
603
|
|
|
498
604
|
_ = ai_model_instance.eval() # Set to evaluation mode
|
|
499
|
-
classifier = Classifier(
|
|
500
|
-
|
|
605
|
+
classifier = Classifier(
|
|
606
|
+
ai_model_instance, config=classifier_config or {}, verbose=True
|
|
607
|
+
)
|
|
608
|
+
logger.info(
|
|
609
|
+
"AI model loaded successfully for video %s from %s.",
|
|
610
|
+
video.video_hash,
|
|
611
|
+
weights_path,
|
|
612
|
+
)
|
|
501
613
|
except Exception as e:
|
|
502
|
-
logger.error(
|
|
614
|
+
logger.error(
|
|
615
|
+
"Failed to load AI model for video %s from %s: %s",
|
|
616
|
+
video.video_hash,
|
|
617
|
+
weights_path,
|
|
618
|
+
e,
|
|
619
|
+
exc_info=True,
|
|
620
|
+
)
|
|
503
621
|
# Raise exception
|
|
504
622
|
raise RuntimeError(f"Failed to load AI model from {weights_path}") from e
|
|
505
623
|
|
|
506
624
|
# --- Inference ---
|
|
507
625
|
try:
|
|
508
|
-
logger.info(
|
|
626
|
+
logger.info(
|
|
627
|
+
"Starting inference on %d frames for video %s...",
|
|
628
|
+
len(string_paths),
|
|
629
|
+
video.video_hash,
|
|
630
|
+
)
|
|
509
631
|
predictions = classifier.pipe(string_paths, crops)
|
|
510
|
-
logger.info("Inference completed for video %s.", video.
|
|
632
|
+
logger.info("Inference completed for video %s.", video.video_hash)
|
|
511
633
|
except Exception as e:
|
|
512
|
-
logger.error(
|
|
634
|
+
logger.error(
|
|
635
|
+
"Inference failed for video %s: %s", video.video_hash, e, exc_info=True
|
|
636
|
+
)
|
|
513
637
|
# CUDA-OOM Fallback: Speicher freigeben und CPU versuchen
|
|
514
638
|
try:
|
|
515
639
|
import gc
|
|
516
640
|
|
|
517
641
|
import torch
|
|
518
642
|
|
|
519
|
-
is_oom = isinstance(
|
|
520
|
-
|
|
643
|
+
is_oom = isinstance(
|
|
644
|
+
e, (getattr(torch.cuda, "OutOfMemoryError", RuntimeError), RuntimeError)
|
|
645
|
+
) and (
|
|
646
|
+
"out of memory" in str(e).lower()
|
|
647
|
+
or "cuda out of memory" in str(e).lower()
|
|
521
648
|
)
|
|
522
649
|
except Exception:
|
|
523
650
|
is_oom = False
|
|
@@ -526,7 +653,9 @@ def _predict_video_pipeline(
|
|
|
526
653
|
import torch # ensure available in this scope
|
|
527
654
|
|
|
528
655
|
if torch.cuda.is_available() and is_oom:
|
|
529
|
-
logger.warning(
|
|
656
|
+
logger.warning(
|
|
657
|
+
"CUDA OOM detected. Freeing CUDA cache and retrying on CPU…"
|
|
658
|
+
)
|
|
530
659
|
try:
|
|
531
660
|
torch.cuda.empty_cache()
|
|
532
661
|
gc.collect()
|
|
@@ -537,9 +666,17 @@ def _predict_video_pipeline(
|
|
|
537
666
|
_ = ai_model_instance.cpu()
|
|
538
667
|
classifier = Classifier(ai_model_instance, verbose=True)
|
|
539
668
|
predictions = classifier.pipe(string_paths, crops)
|
|
540
|
-
logger.info(
|
|
669
|
+
logger.info(
|
|
670
|
+
"Inference completed on CPU after CUDA OOM for video %s.",
|
|
671
|
+
video.video_hash,
|
|
672
|
+
)
|
|
541
673
|
except Exception as e2:
|
|
542
|
-
logger.error(
|
|
674
|
+
logger.error(
|
|
675
|
+
"CPU fallback inference failed for video %s: %s",
|
|
676
|
+
video.video_hash,
|
|
677
|
+
e2,
|
|
678
|
+
exc_info=True,
|
|
679
|
+
)
|
|
543
680
|
# Raise exception
|
|
544
681
|
raise RuntimeError("Inference failed") from e2
|
|
545
682
|
else:
|
|
@@ -554,10 +691,13 @@ def _predict_video_pipeline(
|
|
|
554
691
|
|
|
555
692
|
# --- Post-processing ---
|
|
556
693
|
try:
|
|
557
|
-
logger.info("Post-processing predictions for video %s...", video.
|
|
694
|
+
logger.info("Post-processing predictions for video %s...", video.video_hash)
|
|
558
695
|
readable_predictions = [classifier.readable(p) for p in predictions]
|
|
559
696
|
if label_mapping:
|
|
560
|
-
readable_predictions = [
|
|
697
|
+
readable_predictions = [
|
|
698
|
+
_remap_prediction_dict(prediction, label_mapping)
|
|
699
|
+
for prediction in readable_predictions
|
|
700
|
+
]
|
|
561
701
|
|
|
562
702
|
merged_predictions = concat_pred_dicts(readable_predictions)
|
|
563
703
|
|
|
@@ -565,7 +705,7 @@ def _predict_video_pipeline(
|
|
|
565
705
|
if not fps:
|
|
566
706
|
logger.warning(
|
|
567
707
|
"Video FPS is unknown for %s. Smoothing/sequence calculations might be inaccurate. Using default 30 FPS.",
|
|
568
|
-
video.
|
|
708
|
+
video.video_hash,
|
|
569
709
|
)
|
|
570
710
|
fps = 30 # Default FPS if unknown
|
|
571
711
|
|
|
@@ -580,7 +720,9 @@ def _predict_video_pipeline(
|
|
|
580
720
|
|
|
581
721
|
binary_smooth_merged_predictions = {}
|
|
582
722
|
for key in smooth_merged_predictions.keys():
|
|
583
|
-
binary_smooth_merged_predictions[key] =
|
|
723
|
+
binary_smooth_merged_predictions[key] = (
|
|
724
|
+
smooth_merged_predictions[key] > binarize_threshold
|
|
725
|
+
)
|
|
584
726
|
|
|
585
727
|
sequences = {}
|
|
586
728
|
for label, prediction_array in binary_smooth_merged_predictions.items():
|
|
@@ -588,13 +730,18 @@ def _predict_video_pipeline(
|
|
|
588
730
|
|
|
589
731
|
logger.info(
|
|
590
732
|
"Post-processing completed for video %s. Found sequences for labels: %s",
|
|
591
|
-
video.
|
|
733
|
+
video.video_hash,
|
|
592
734
|
list(sequences.keys()),
|
|
593
735
|
)
|
|
594
736
|
return sequences if sequences is not None else {}
|
|
595
737
|
|
|
596
738
|
except Exception as e:
|
|
597
|
-
logger.error(
|
|
739
|
+
logger.error(
|
|
740
|
+
"Post-processing failed for video %s: %s",
|
|
741
|
+
video.video_hash,
|
|
742
|
+
e,
|
|
743
|
+
exc_info=True,
|
|
744
|
+
)
|
|
598
745
|
# Raise exception
|
|
599
746
|
raise RuntimeError("Post-processing failed") from e
|
|
600
747
|
|
|
@@ -617,14 +764,26 @@ def _predict_video_entry(
|
|
|
617
764
|
ai_model = AiModel.objects.get(name=model_name)
|
|
618
765
|
if not model_meta_version:
|
|
619
766
|
model_meta = ai_model.get_latest_version()
|
|
620
|
-
logger.info(
|
|
767
|
+
logger.info(
|
|
768
|
+
"Using latest ModelMeta version %s for model %s.",
|
|
769
|
+
model_meta_version,
|
|
770
|
+
model_name,
|
|
771
|
+
)
|
|
621
772
|
else:
|
|
622
773
|
model_meta = ai_model.get_version(model_meta_version)
|
|
623
|
-
logger.info(
|
|
774
|
+
logger.info(
|
|
775
|
+
"Using specified ModelMeta version %s for model %s.",
|
|
776
|
+
model_meta_version,
|
|
777
|
+
model_name,
|
|
778
|
+
)
|
|
624
779
|
|
|
625
|
-
logger.info(
|
|
780
|
+
logger.info(
|
|
781
|
+
"Using ModelMeta: %s (Version: %s)", model_meta.name, model_meta.version
|
|
782
|
+
)
|
|
626
783
|
except ModelMeta.DoesNotExist:
|
|
627
|
-
logger.error(
|
|
784
|
+
logger.error(
|
|
785
|
+
"ModelMeta '%s' (Version: %s) not found.", model_name, model_meta_version
|
|
786
|
+
)
|
|
628
787
|
raise
|
|
629
788
|
|
|
630
789
|
# --- Explicitly pass only the arguments expected by _predict_video_pipeline ---
|
|
@@ -643,15 +802,21 @@ def _predict_video_entry(
|
|
|
643
802
|
return predicted_sequences, model_meta
|
|
644
803
|
|
|
645
804
|
|
|
646
|
-
def _extract_text_information(
|
|
805
|
+
def _extract_text_information(
|
|
806
|
+
video: "VideoFile", frame_fraction: float = 0.001, cap: int = 15
|
|
807
|
+
) -> Optional[Dict[str, str]]:
|
|
647
808
|
"""Facade function to call the text extraction logic."""
|
|
648
|
-
logger.info("Attempting text extraction for video %s.", video.
|
|
809
|
+
logger.info("Attempting text extraction for video %s.", video.video_hash)
|
|
649
810
|
|
|
650
|
-
extracted_data = _extract_text_from_video_frames(
|
|
811
|
+
extracted_data = _extract_text_from_video_frames(
|
|
812
|
+
video=video, frame_fraction=frame_fraction, cap=cap
|
|
813
|
+
)
|
|
651
814
|
|
|
652
815
|
if extracted_data is not None:
|
|
653
|
-
logger.info("Text extraction successful for video %s.", video.
|
|
816
|
+
logger.info("Text extraction successful for video %s.", video.video_hash)
|
|
654
817
|
else:
|
|
655
|
-
logger.warning(
|
|
818
|
+
logger.warning(
|
|
819
|
+
"Text extraction returned no data for video %s.", video.video_hash
|
|
820
|
+
)
|
|
656
821
|
|
|
657
822
|
return extracted_data
|