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
|
@@ -30,11 +30,19 @@ class VideoMeta(models.Model):
|
|
|
30
30
|
Links to hardware (processor, endoscope), center, import details, and FFmpeg technical specs.
|
|
31
31
|
"""
|
|
32
32
|
|
|
33
|
-
processor = models.ForeignKey(
|
|
34
|
-
|
|
33
|
+
processor = models.ForeignKey(
|
|
34
|
+
"EndoscopyProcessor", on_delete=models.CASCADE, blank=True, null=True
|
|
35
|
+
)
|
|
36
|
+
endoscope = models.ForeignKey(
|
|
37
|
+
"Endoscope", on_delete=models.CASCADE, blank=True, null=True
|
|
38
|
+
)
|
|
35
39
|
center = models.ForeignKey("Center", on_delete=models.CASCADE)
|
|
36
|
-
import_meta = models.OneToOneField(
|
|
37
|
-
|
|
40
|
+
import_meta = models.OneToOneField(
|
|
41
|
+
"VideoImportMeta", on_delete=models.CASCADE, blank=True, null=True
|
|
42
|
+
)
|
|
43
|
+
ffmpeg_meta = models.OneToOneField(
|
|
44
|
+
"FFMpegMeta", on_delete=models.CASCADE, blank=True, null=True
|
|
45
|
+
)
|
|
38
46
|
|
|
39
47
|
if TYPE_CHECKING:
|
|
40
48
|
processor: models.ForeignKey["EndoscopyProcessor|None"]
|
|
@@ -47,21 +55,27 @@ class VideoMeta(models.Model):
|
|
|
47
55
|
def center_safe(self) -> "Center":
|
|
48
56
|
center = self.center
|
|
49
57
|
if not center:
|
|
50
|
-
raise Center.DoesNotExist(
|
|
58
|
+
raise Center.DoesNotExist(
|
|
59
|
+
"Center does not exist for this VideoMeta instance."
|
|
60
|
+
)
|
|
51
61
|
return center
|
|
52
62
|
|
|
53
63
|
@property
|
|
54
64
|
def processor_safe(self) -> "EndoscopyProcessor":
|
|
55
65
|
processor = self.processor
|
|
56
66
|
if not processor:
|
|
57
|
-
raise EndoscopyProcessor.DoesNotExist(
|
|
67
|
+
raise EndoscopyProcessor.DoesNotExist(
|
|
68
|
+
"EndoscopyProcessor does not exist for this VideoMeta instance."
|
|
69
|
+
)
|
|
58
70
|
return processor
|
|
59
71
|
|
|
60
72
|
@property
|
|
61
73
|
def ffmpeg_meta_safe(self) -> "FFMpegMeta":
|
|
62
74
|
ffmpeg_meta = self.ffmpeg_meta
|
|
63
75
|
if not ffmpeg_meta:
|
|
64
|
-
raise FFMpegMeta.DoesNotExist(
|
|
76
|
+
raise FFMpegMeta.DoesNotExist(
|
|
77
|
+
"FFMpegMeta does not exist for this VideoMeta instance."
|
|
78
|
+
)
|
|
65
79
|
return ffmpeg_meta
|
|
66
80
|
|
|
67
81
|
@classmethod
|
|
@@ -88,14 +102,27 @@ class VideoMeta(models.Model):
|
|
|
88
102
|
meta.initialize_ffmpeg_meta(video_path)
|
|
89
103
|
except Exception as e:
|
|
90
104
|
# Re-raise exceptions from ffmpeg meta initialization
|
|
91
|
-
logger.error(
|
|
92
|
-
|
|
105
|
+
logger.error(
|
|
106
|
+
"Failed during FFMpegMeta initialization within create_from_file for %s: %s",
|
|
107
|
+
video_path.name,
|
|
108
|
+
e,
|
|
109
|
+
exc_info=True,
|
|
110
|
+
)
|
|
111
|
+
raise RuntimeError(
|
|
112
|
+
f"Failed to initialize FFMpeg metadata for {video_path.name}"
|
|
113
|
+
) from e
|
|
93
114
|
|
|
94
115
|
if save_instance:
|
|
95
116
|
meta.save() # This ensures VideoImportMeta is created too
|
|
96
|
-
logger.info(
|
|
117
|
+
logger.info(
|
|
118
|
+
"Created and saved VideoMeta instance PK %s from %s",
|
|
119
|
+
meta.pk,
|
|
120
|
+
video_path.name,
|
|
121
|
+
)
|
|
97
122
|
else:
|
|
98
|
-
logger.info(
|
|
123
|
+
logger.info(
|
|
124
|
+
"Instantiated VideoMeta from %s (not saved yet)", video_path.name
|
|
125
|
+
)
|
|
99
126
|
|
|
100
127
|
return meta
|
|
101
128
|
|
|
@@ -129,10 +156,17 @@ class VideoMeta(models.Model):
|
|
|
129
156
|
Raises RuntimeError if FFMpegMeta creation fails.
|
|
130
157
|
"""
|
|
131
158
|
if self.ffmpeg_meta:
|
|
132
|
-
logger.debug(
|
|
159
|
+
logger.debug(
|
|
160
|
+
"FFMpegMeta already exists for VideoMeta PK %s. Skipping initialization.",
|
|
161
|
+
self.pk,
|
|
162
|
+
)
|
|
133
163
|
return
|
|
134
164
|
|
|
135
|
-
logger.info(
|
|
165
|
+
logger.info(
|
|
166
|
+
"Initializing FFMpegMeta for VideoMeta PK %s from %s",
|
|
167
|
+
self.pk if self.pk else "(unsaved)",
|
|
168
|
+
video_path.name,
|
|
169
|
+
)
|
|
136
170
|
try:
|
|
137
171
|
# FFMpegMeta.create_from_file now raises exceptions on failure
|
|
138
172
|
ffmpeg_instance = FFMpegMeta.create_from_file(video_path)
|
|
@@ -142,27 +176,43 @@ class VideoMeta(models.Model):
|
|
|
142
176
|
# Otherwise, the link will be saved when VideoMeta itself is saved.
|
|
143
177
|
if self.pk:
|
|
144
178
|
self.save(update_fields=["ffmpeg_meta"])
|
|
145
|
-
logger.info(
|
|
179
|
+
logger.info(
|
|
180
|
+
"Successfully created and linked FFMpegMeta PK %s",
|
|
181
|
+
self.ffmpeg_meta_safe.pk,
|
|
182
|
+
)
|
|
146
183
|
|
|
147
184
|
except Exception as e:
|
|
148
185
|
# Log the error and re-raise it
|
|
149
|
-
logger.error(
|
|
150
|
-
|
|
186
|
+
logger.error(
|
|
187
|
+
"Failed to create or link FFMpegMeta from %s: %s",
|
|
188
|
+
video_path,
|
|
189
|
+
e,
|
|
190
|
+
exc_info=True,
|
|
191
|
+
)
|
|
192
|
+
raise RuntimeError(
|
|
193
|
+
f"Failed to create FFMpeg metadata from {video_path}"
|
|
194
|
+
) from e
|
|
151
195
|
|
|
152
196
|
def update_meta(self, video_path: Path):
|
|
153
197
|
"""
|
|
154
198
|
Updates the FFMpeg metadata from the file, replacing existing data.
|
|
155
199
|
Raises RuntimeError if FFMpegMeta creation fails.
|
|
156
200
|
"""
|
|
157
|
-
logger.info(
|
|
201
|
+
logger.info(
|
|
202
|
+
"Updating FFMpegMeta for VideoMeta PK %s from %s", self.pk, video_path.name
|
|
203
|
+
)
|
|
158
204
|
existing_ffmpeg_pk = None
|
|
159
205
|
if self.ffmpeg_meta:
|
|
160
206
|
existing_ffmpeg_pk = self.ffmpeg_meta.pk
|
|
161
|
-
logger.debug(
|
|
207
|
+
logger.debug(
|
|
208
|
+
"Deleting existing FFMpegMeta PK %s before update.", existing_ffmpeg_pk
|
|
209
|
+
)
|
|
162
210
|
# Nullify the relation first before deleting the related object
|
|
163
211
|
self.ffmpeg_meta = None
|
|
164
212
|
self.save(update_fields=["ffmpeg_meta"]) # Save the null relation
|
|
165
|
-
FFMpegMeta.objects.filter(
|
|
213
|
+
FFMpegMeta.objects.filter(
|
|
214
|
+
pk=existing_ffmpeg_pk
|
|
215
|
+
).delete() # Delete the old object
|
|
166
216
|
|
|
167
217
|
# initialize_ffmpeg_meta handles creation, linking, saving the link, and raises exceptions
|
|
168
218
|
self.initialize_ffmpeg_meta(video_path)
|
|
@@ -179,7 +229,9 @@ class VideoMeta(models.Model):
|
|
|
179
229
|
def fps(self) -> Optional[float]:
|
|
180
230
|
"""Returns the frame rate (FPS) from the linked FFMpegMeta."""
|
|
181
231
|
if not self.ffmpeg_meta:
|
|
182
|
-
logger.warning(
|
|
232
|
+
logger.warning(
|
|
233
|
+
"FFMpegMeta not linked for VideoMeta PK %s. Cannot get FPS.", self.pk
|
|
234
|
+
)
|
|
183
235
|
return None
|
|
184
236
|
return self.ffmpeg_meta.fps
|
|
185
237
|
|
|
@@ -201,7 +253,12 @@ class VideoMeta(models.Model):
|
|
|
201
253
|
@property
|
|
202
254
|
def frame_count(self) -> Optional[int]:
|
|
203
255
|
"""Calculates frame count based on duration and FPS from FFMpegMeta."""
|
|
204
|
-
if
|
|
256
|
+
if (
|
|
257
|
+
self.ffmpeg_meta
|
|
258
|
+
and self.ffmpeg_meta.duration is not None
|
|
259
|
+
and self.ffmpeg_meta.fps is not None
|
|
260
|
+
and self.ffmpeg_meta.fps > 0
|
|
261
|
+
):
|
|
205
262
|
return int(self.ffmpeg_meta.duration * self.ffmpeg_meta.fps)
|
|
206
263
|
return None
|
|
207
264
|
|
|
@@ -214,17 +271,29 @@ class FFMpegMeta(models.Model):
|
|
|
214
271
|
width = models.IntegerField(null=True, blank=True)
|
|
215
272
|
height = models.IntegerField(null=True, blank=True)
|
|
216
273
|
duration = models.FloatField(null=True, blank=True) # Duration in seconds
|
|
217
|
-
frame_rate_num = models.IntegerField(
|
|
218
|
-
|
|
274
|
+
frame_rate_num = models.IntegerField(
|
|
275
|
+
null=True, blank=True
|
|
276
|
+
) # Numerator for frame rate
|
|
277
|
+
frame_rate_den = models.IntegerField(
|
|
278
|
+
null=True, blank=True
|
|
279
|
+
) # Denominator for frame rate
|
|
219
280
|
codec_name = models.CharField(max_length=50, null=True, blank=True)
|
|
220
281
|
pixel_format = models.CharField(max_length=50, null=True, blank=True)
|
|
221
|
-
bit_rate = models.BigIntegerField(
|
|
222
|
-
|
|
282
|
+
bit_rate = models.BigIntegerField(
|
|
283
|
+
null=True, blank=True
|
|
284
|
+
) # Bit rate in bits per second
|
|
285
|
+
raw_probe_data = models.JSONField(
|
|
286
|
+
null=True, blank=True
|
|
287
|
+
) # Store the full JSON output for debugging or future use
|
|
223
288
|
|
|
224
289
|
@property
|
|
225
290
|
def fps(self) -> Optional[float]:
|
|
226
291
|
"""Calculates and returns the frames per second (FPS) if possible."""
|
|
227
|
-
if
|
|
292
|
+
if (
|
|
293
|
+
self.frame_rate_num is not None
|
|
294
|
+
and self.frame_rate_den is not None
|
|
295
|
+
and self.frame_rate_den != 0
|
|
296
|
+
):
|
|
228
297
|
return self.frame_rate_num / self.frame_rate_den
|
|
229
298
|
return None
|
|
230
299
|
|
|
@@ -236,17 +305,30 @@ class FFMpegMeta(models.Model):
|
|
|
236
305
|
"""
|
|
237
306
|
logger.info("Running ffprobe on %s", file_path)
|
|
238
307
|
try:
|
|
239
|
-
probe_data = ffmpeg_wrapper.get_stream_info(
|
|
308
|
+
probe_data = ffmpeg_wrapper.get_stream_info(
|
|
309
|
+
file_path
|
|
310
|
+
) # Use the new utility
|
|
240
311
|
except Exception as probe_err:
|
|
241
|
-
logger.error(
|
|
242
|
-
|
|
312
|
+
logger.error(
|
|
313
|
+
"ffprobe execution failed for %s: %s",
|
|
314
|
+
file_path,
|
|
315
|
+
probe_err,
|
|
316
|
+
exc_info=True,
|
|
317
|
+
)
|
|
318
|
+
raise RuntimeError(
|
|
319
|
+
f"ffprobe execution failed for {file_path}"
|
|
320
|
+
) from probe_err
|
|
243
321
|
|
|
244
322
|
if not probe_data or "streams" not in probe_data:
|
|
245
|
-
logger.error(
|
|
323
|
+
logger.error(
|
|
324
|
+
"Failed to get valid stream info from ffprobe for %s", file_path
|
|
325
|
+
)
|
|
246
326
|
# Raise exception instead of returning None
|
|
247
327
|
raise RuntimeError(f"Invalid stream info from ffprobe for {file_path}")
|
|
248
328
|
|
|
249
|
-
video_stream = next(
|
|
329
|
+
video_stream = next(
|
|
330
|
+
(s for s in probe_data["streams"] if s.get("codec_type") == "video"), None
|
|
331
|
+
)
|
|
250
332
|
|
|
251
333
|
if not video_stream:
|
|
252
334
|
logger.warning("No video stream found in ffprobe output for %s", file_path)
|
|
@@ -258,7 +340,11 @@ class FFMpegMeta(models.Model):
|
|
|
258
340
|
height = video_stream.get("height")
|
|
259
341
|
duration_str = video_stream.get("duration")
|
|
260
342
|
# --- FIX: Handle potential format key ---
|
|
261
|
-
if
|
|
343
|
+
if (
|
|
344
|
+
duration_str is None
|
|
345
|
+
and "format" in probe_data
|
|
346
|
+
and "duration" in probe_data["format"]
|
|
347
|
+
):
|
|
262
348
|
duration_str = probe_data["format"]["duration"]
|
|
263
349
|
logger.debug("Using duration from format block: %s", duration_str)
|
|
264
350
|
# --- End Fix ---
|
|
@@ -278,17 +364,25 @@ class FFMpegMeta(models.Model):
|
|
|
278
364
|
frame_rate_num = int(num_str)
|
|
279
365
|
frame_rate_den = int(den_str)
|
|
280
366
|
if frame_rate_den == 0: # Avoid division by zero
|
|
281
|
-
logger.warning(
|
|
367
|
+
logger.warning(
|
|
368
|
+
"Invalid frame rate denominator (0) for %s", file_path
|
|
369
|
+
)
|
|
282
370
|
frame_rate_num, frame_rate_den = None, None
|
|
283
371
|
except ValueError:
|
|
284
|
-
logger.warning(
|
|
372
|
+
logger.warning(
|
|
373
|
+
"Could not parse frame rate '%s' for %s", frame_rate_str, file_path
|
|
374
|
+
)
|
|
285
375
|
frame_rate_num, frame_rate_den = None, None
|
|
286
376
|
|
|
287
377
|
codec_name = video_stream.get("codec_name")
|
|
288
378
|
pixel_format = video_stream.get("pix_fmt")
|
|
289
379
|
bit_rate_str = video_stream.get("bit_rate")
|
|
290
380
|
# --- FIX: Handle potential format key for bit_rate ---
|
|
291
|
-
if
|
|
381
|
+
if (
|
|
382
|
+
bit_rate_str is None
|
|
383
|
+
and "format" in probe_data
|
|
384
|
+
and "bit_rate" in probe_data["format"]
|
|
385
|
+
):
|
|
292
386
|
bit_rate_str = probe_data["format"]["bit_rate"]
|
|
293
387
|
logger.debug("Using bit_rate from format block: %s", bit_rate_str)
|
|
294
388
|
# --- End Fix ---
|
|
@@ -306,12 +400,23 @@ class FFMpegMeta(models.Model):
|
|
|
306
400
|
bit_rate=bit_rate,
|
|
307
401
|
raw_probe_data=probe_data,
|
|
308
402
|
)
|
|
309
|
-
logger.info(
|
|
403
|
+
logger.info(
|
|
404
|
+
"Successfully created FFMpegMeta for %s (ID: %d)",
|
|
405
|
+
file_path.name,
|
|
406
|
+
instance.pk,
|
|
407
|
+
)
|
|
310
408
|
return instance
|
|
311
409
|
except Exception as e:
|
|
312
|
-
logger.error(
|
|
410
|
+
logger.error(
|
|
411
|
+
"Error creating FFMpegMeta DB record from %s: %s",
|
|
412
|
+
file_path.name,
|
|
413
|
+
e,
|
|
414
|
+
exc_info=True,
|
|
415
|
+
)
|
|
313
416
|
# Raise exception instead of returning None
|
|
314
|
-
raise RuntimeError(
|
|
417
|
+
raise RuntimeError(
|
|
418
|
+
f"Database error creating FFMpegMeta for {file_path.name}"
|
|
419
|
+
) from e
|
|
315
420
|
|
|
316
421
|
def __str__(self):
|
|
317
422
|
"""Returns a string summary of the FFmpeg metadata."""
|
|
@@ -345,7 +450,9 @@ class VideoImportMeta(models.Model):
|
|
|
345
450
|
result_html = ""
|
|
346
451
|
|
|
347
452
|
result_html += f"Video anonymized: {self.video_anonymized}\n"
|
|
348
|
-
result_html +=
|
|
453
|
+
result_html += (
|
|
454
|
+
f"Video patient data detected: {self.video_patient_data_detected}\n"
|
|
455
|
+
)
|
|
349
456
|
result_html += f"Outside detected: {self.outside_detected}\n"
|
|
350
457
|
result_html += f"Patient data removed: {self.patient_data_removed}\n"
|
|
351
458
|
result_html += f"Outside removed: {self.outside_removed}\n"
|
|
@@ -19,7 +19,11 @@ if TYPE_CHECKING:
|
|
|
19
19
|
from .video_prediction_meta import VideoPredictionMeta
|
|
20
20
|
|
|
21
21
|
|
|
22
|
-
def apply_running_mean_logic(
|
|
22
|
+
def apply_running_mean_logic(
|
|
23
|
+
instance: "VideoPredictionMeta",
|
|
24
|
+
confidence_array: np.ndarray,
|
|
25
|
+
window_size_in_seconds: Optional[float] = None,
|
|
26
|
+
) -> np.ndarray:
|
|
23
27
|
"""
|
|
24
28
|
Apply a running mean filter to the confidence array for smoothing.
|
|
25
29
|
"""
|
|
@@ -27,7 +31,9 @@ def apply_running_mean_logic(instance: "VideoPredictionMeta", confidence_array:
|
|
|
27
31
|
fps = video_obj.get_fps()
|
|
28
32
|
|
|
29
33
|
if fps is None or fps <= 0:
|
|
30
|
-
logger.warning(
|
|
34
|
+
logger.warning(
|
|
35
|
+
f"Invalid FPS ({fps}) for {video_obj}. Cannot apply running mean. Returning original array."
|
|
36
|
+
)
|
|
31
37
|
return confidence_array
|
|
32
38
|
|
|
33
39
|
if window_size_in_seconds is None:
|
|
@@ -52,14 +58,18 @@ def apply_running_mean_logic(instance: "VideoPredictionMeta", confidence_array:
|
|
|
52
58
|
running_mean = running_mean[start_index:end_index]
|
|
53
59
|
|
|
54
60
|
if running_mean.shape != confidence_array.shape:
|
|
55
|
-
logger.warning(
|
|
61
|
+
logger.warning(
|
|
62
|
+
f"Running mean output shape {running_mean.shape} differs from input {confidence_array.shape}. Check padding/slicing."
|
|
63
|
+
)
|
|
56
64
|
# Return original array on shape mismatch to avoid downstream errors
|
|
57
65
|
return confidence_array
|
|
58
66
|
|
|
59
67
|
return running_mean
|
|
60
68
|
|
|
61
69
|
|
|
62
|
-
def calculate_prediction_array_logic(
|
|
70
|
+
def calculate_prediction_array_logic(
|
|
71
|
+
instance: "VideoPredictionMeta", window_size_in_seconds: Optional[float] = None
|
|
72
|
+
) -> Optional[np.ndarray]:
|
|
63
73
|
"""
|
|
64
74
|
Fetches predictions, applies smoothing, and returns the binary prediction array.
|
|
65
75
|
Does not save the array itself.
|
|
@@ -72,19 +82,29 @@ def calculate_prediction_array_logic(instance: "VideoPredictionMeta", window_siz
|
|
|
72
82
|
num_frames = video_obj.frame_count
|
|
73
83
|
|
|
74
84
|
if num_frames is None or num_frames <= 0:
|
|
75
|
-
logger.warning(
|
|
85
|
+
logger.warning(
|
|
86
|
+
f"Cannot calculate prediction array for {video_obj} with invalid frame count ({num_frames})."
|
|
87
|
+
)
|
|
76
88
|
return None
|
|
77
89
|
|
|
78
90
|
if not label_list:
|
|
79
|
-
logger.warning(
|
|
91
|
+
logger.warning(
|
|
92
|
+
f"No labels found for model {model_meta}. Cannot calculate prediction array."
|
|
93
|
+
)
|
|
80
94
|
return None
|
|
81
95
|
|
|
82
96
|
prediction_array = np.zeros((num_frames, len(label_list)))
|
|
83
97
|
|
|
84
|
-
base_pred_qs = ImageClassificationAnnotation.objects.filter(
|
|
98
|
+
base_pred_qs = ImageClassificationAnnotation.objects.filter(
|
|
99
|
+
model_meta=model_meta, frame__video_file=video_obj
|
|
100
|
+
)
|
|
85
101
|
|
|
86
102
|
for i, label in enumerate(label_list):
|
|
87
|
-
predictions =
|
|
103
|
+
predictions = (
|
|
104
|
+
base_pred_qs.filter(label=label)
|
|
105
|
+
.order_by("frame__frame_number")
|
|
106
|
+
.values_list("frame__frame_number", "confidence")
|
|
107
|
+
)
|
|
88
108
|
|
|
89
109
|
# Initialize with 0.5 (neutral confidence)
|
|
90
110
|
confidences = np.full(num_frames, 0.5)
|
|
@@ -94,12 +114,18 @@ def calculate_prediction_array_logic(instance: "VideoPredictionMeta", window_siz
|
|
|
94
114
|
confidences[frame_num] = confidence
|
|
95
115
|
found_predictions = True
|
|
96
116
|
else:
|
|
97
|
-
logger.warning(
|
|
117
|
+
logger.warning(
|
|
118
|
+
f"Prediction found for out-of-bounds frame number {frame_num} (max: {num_frames - 1}). Skipping."
|
|
119
|
+
)
|
|
98
120
|
|
|
99
121
|
if not found_predictions:
|
|
100
|
-
logger.warning(
|
|
122
|
+
logger.warning(
|
|
123
|
+
f"No predictions found for label '{label.name}' in {video_obj}. Using default confidence."
|
|
124
|
+
)
|
|
101
125
|
|
|
102
|
-
smooth_confidences = apply_running_mean_logic(
|
|
126
|
+
smooth_confidences = apply_running_mean_logic(
|
|
127
|
+
instance, confidences, window_size_in_seconds
|
|
128
|
+
)
|
|
103
129
|
# Threshold smoothed confidences
|
|
104
130
|
binary_predictions = smooth_confidences > 0.5
|
|
105
131
|
prediction_array[:, i] = binary_predictions
|
|
@@ -107,7 +133,9 @@ def calculate_prediction_array_logic(instance: "VideoPredictionMeta", window_siz
|
|
|
107
133
|
return prediction_array
|
|
108
134
|
|
|
109
135
|
|
|
110
|
-
def create_video_segments_for_label_logic(
|
|
136
|
+
def create_video_segments_for_label_logic(
|
|
137
|
+
instance: "VideoPredictionMeta", segments: List[Tuple[int, int]], label: "Label"
|
|
138
|
+
):
|
|
111
139
|
"""
|
|
112
140
|
Creates LabelVideoSegment instances for the given label and segments.
|
|
113
141
|
"""
|
|
@@ -128,18 +156,29 @@ def create_video_segments_for_label_logic(instance: "VideoPredictionMeta", segme
|
|
|
128
156
|
}
|
|
129
157
|
# Check for existence before creating the object instance
|
|
130
158
|
if not LabelVideoSegment.objects.filter(
|
|
131
|
-
video_file=video_obj,
|
|
159
|
+
video_file=video_obj,
|
|
160
|
+
prediction_meta=instance,
|
|
161
|
+
label=label,
|
|
162
|
+
start_frame_number=start_frame,
|
|
163
|
+
end_frame_number=end_frame,
|
|
132
164
|
).exists():
|
|
133
165
|
segments_to_create.append(LabelVideoSegment(**segment_data))
|
|
134
166
|
|
|
135
167
|
if segments_to_create:
|
|
136
168
|
LabelVideoSegment.objects.bulk_create(segments_to_create)
|
|
137
|
-
logger.info(
|
|
169
|
+
logger.info(
|
|
170
|
+
f"Created {len(segments_to_create)} video segments for label '{label.name}' in {video_obj}."
|
|
171
|
+
)
|
|
138
172
|
else:
|
|
139
|
-
logger.info(
|
|
173
|
+
logger.info(
|
|
174
|
+
f"No new video segments needed for label '{label.name}' in {video_obj}."
|
|
175
|
+
)
|
|
140
176
|
|
|
141
177
|
|
|
142
|
-
def create_video_segments_logic(
|
|
178
|
+
def create_video_segments_logic(
|
|
179
|
+
instance: "VideoPredictionMeta",
|
|
180
|
+
segment_length_threshold_in_s: Optional[float] = None,
|
|
181
|
+
):
|
|
143
182
|
"""
|
|
144
183
|
Generates LabelVideoSegments based on the stored prediction array.
|
|
145
184
|
"""
|
|
@@ -150,7 +189,9 @@ def create_video_segments_logic(instance: "VideoPredictionMeta", segment_length_
|
|
|
150
189
|
fps = video_obj.get_fps()
|
|
151
190
|
|
|
152
191
|
if fps is None or fps <= 0:
|
|
153
|
-
logger.warning(
|
|
192
|
+
logger.warning(
|
|
193
|
+
f"Cannot create video segments for {video_obj} with invalid FPS ({fps})."
|
|
194
|
+
)
|
|
154
195
|
return
|
|
155
196
|
|
|
156
197
|
min_frame_length = int(segment_length_threshold_in_s * fps)
|
|
@@ -167,17 +208,25 @@ def create_video_segments_logic(instance: "VideoPredictionMeta", segment_length_
|
|
|
167
208
|
instance.calculate_prediction_array() # This will save the array internally
|
|
168
209
|
prediction_array = instance.get_prediction_array() # Fetch again
|
|
169
210
|
if prediction_array is None:
|
|
170
|
-
logger.error(
|
|
211
|
+
logger.error(
|
|
212
|
+
f"Failed to get or calculate prediction array for {instance}. Cannot create segments."
|
|
213
|
+
)
|
|
171
214
|
return
|
|
172
215
|
|
|
173
216
|
if prediction_array.shape[1] != len(label_list):
|
|
174
|
-
logger.warning(
|
|
217
|
+
logger.warning(
|
|
218
|
+
f"Prediction array shape {prediction_array.shape} incompatible with label list length {len(label_list)} for {instance}."
|
|
219
|
+
)
|
|
175
220
|
return
|
|
176
221
|
|
|
177
|
-
logger.info(
|
|
222
|
+
logger.info(
|
|
223
|
+
f"Creating video segments for {instance} (min length: {min_frame_length} frames)..."
|
|
224
|
+
)
|
|
178
225
|
for i, label in enumerate(label_list):
|
|
179
226
|
binary_predictions = prediction_array[:, i].astype(bool)
|
|
180
|
-
segments = find_segments_in_prediction_array(
|
|
227
|
+
segments = find_segments_in_prediction_array(
|
|
228
|
+
binary_predictions, min_frame_length
|
|
229
|
+
)
|
|
181
230
|
if segments:
|
|
182
231
|
create_video_segments_for_label_logic(instance, segments, label)
|
|
183
232
|
logger.info(f"Finished creating video segments for {instance}.")
|