descriptron-core 2.1.0__tar.gz → 2.1.2__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (136) hide show
  1. descriptron_core-2.1.2/NOTICE +59 -0
  2. {descriptron_core-2.1.0 → descriptron_core-2.1.2}/PKG-INFO +2 -1
  3. descriptron_core-2.1.2/pyproject.toml +82 -0
  4. {descriptron_core-2.1.0 → descriptron_core-2.1.2}/src/descriptron_core/tools/descriptron_shape_stats.py +28 -9
  5. descriptron_core-2.1.2/src/descriptron_core/tools/validate_centerline_lengths.py +71 -0
  6. descriptron_core-2.1.2/src/descriptron_core/tools/validate_texture_glcm.py +68 -0
  7. {descriptron_core-2.1.0 → descriptron_core-2.1.2}/.gitignore +0 -0
  8. {descriptron_core-2.1.0 → descriptron_core-2.1.2}/LICENSE +0 -0
  9. /descriptron_core-2.1.0/NOTICE → /descriptron_core-2.1.2/NOTICE.bak_pre_212_20260929_1100 +0 -0
  10. {descriptron_core-2.1.0 → descriptron_core-2.1.2}/README.md +0 -0
  11. {descriptron_core-2.1.0 → descriptron_core-2.1.2}/pyproject.toml.bak_pre_203_20260924_145320 +0 -0
  12. {descriptron_core-2.1.0 → descriptron_core-2.1.2}/pyproject.toml.bak_pre_204_20260924_165335 +0 -0
  13. {descriptron_core-2.1.0 → descriptron_core-2.1.2}/pyproject.toml.bak_pre_210_20260925_103754 +0 -0
  14. /descriptron_core-2.1.0/pyproject.toml → /descriptron_core-2.1.2/pyproject.toml.bak_pre_211_20260925_114912 +0 -0
  15. {descriptron_core-2.1.0 → descriptron_core-2.1.2}/pyproject.toml.bak_pre_keys_20260924_165335 +0 -0
  16. {descriptron_core-2.1.0 → descriptron_core-2.1.2}/src/descriptron_core/__init__.py +0 -0
  17. {descriptron_core-2.1.0 → descriptron_core-2.1.2}/src/descriptron_core/cli.py +0 -0
  18. {descriptron_core-2.1.0 → descriptron_core-2.1.2}/src/descriptron_core/cli.py.bak_pre_keys_20260924_165335 +0 -0
  19. {descriptron_core-2.1.0 → descriptron_core-2.1.2}/src/descriptron_core/data/biorag_prompts/biorag_system_prompts_v1.txt +0 -0
  20. {descriptron_core-2.1.0 → descriptron_core-2.1.2}/src/descriptron_core/data/biorag_prompts/biorag_system_prompts_v2.txt +0 -0
  21. {descriptron_core-2.1.0 → descriptron_core-2.1.2}/src/descriptron_core/data/biorag_prompts/diaphorina_user_prompts.txt +0 -0
  22. {descriptron_core-2.1.0 → descriptron_core-2.1.2}/src/descriptron_core/data/biorag_prompts/ontologies/aism.obo +0 -0
  23. {descriptron_core-2.1.0 → descriptron_core-2.1.2}/src/descriptron_core/data/biorag_prompts/ontologies/pato.obo +0 -0
  24. {descriptron_core-2.1.0 → descriptron_core-2.1.2}/src/descriptron_core/data/biorag_prompts/schemas/sdd_1.1/SDD.xsd +0 -0
  25. {descriptron_core-2.1.0 → descriptron_core-2.1.2}/src/descriptron_core/data/biorag_prompts/schemas/sdd_1.1/SDD_EnumLib.xsd +0 -0
  26. {descriptron_core-2.1.0 → descriptron_core-2.1.2}/src/descriptron_core/data/biorag_prompts/schemas/sdd_1.1/SDD_TypeLib.xsd +0 -0
  27. {descriptron_core-2.1.0 → descriptron_core-2.1.2}/src/descriptron_core/data/biorag_prompts/schemas/sdd_1.1/UBIF_BasePattern.xsd +0 -0
  28. {descriptron_core-2.1.0 → descriptron_core-2.1.2}/src/descriptron_core/data/biorag_prompts/schemas/sdd_1.1/UBIF_CoreExtensions.xsd +0 -0
  29. {descriptron_core-2.1.0 → descriptron_core-2.1.2}/src/descriptron_core/data/biorag_prompts/schemas/sdd_1.1/UBIF_CoreOntology.xsd +0 -0
  30. {descriptron_core-2.1.0 → descriptron_core-2.1.2}/src/descriptron_core/data/biorag_prompts/schemas/sdd_1.1/UBIF_EnumLib.xsd +0 -0
  31. {descriptron_core-2.1.0 → descriptron_core-2.1.2}/src/descriptron_core/data/biorag_prompts/schemas/sdd_1.1/UBIF_LinneanCore.xsd +0 -0
  32. {descriptron_core-2.1.0 → descriptron_core-2.1.2}/src/descriptron_core/data/biorag_prompts/schemas/sdd_1.1/UBIF_TypeLib.xsd +0 -0
  33. {descriptron_core-2.1.0 → descriptron_core-2.1.2}/src/descriptron_core/data/biorag_prompts/schemas/sdd_1.1/w3c-schemata/xml.xsd +0 -0
  34. {descriptron_core-2.1.0 → descriptron_core-2.1.2}/src/descriptron_core/data/biorag_prompts/schemas/taxpub/sample_treatment_deposit_example.xml +0 -0
  35. {descriptron_core-2.1.0 → descriptron_core-2.1.2}/src/descriptron_core/data/biorag_prompts/schemas/taxpub/sample_zookeys_26056_tp.xml +0 -0
  36. {descriptron_core-2.1.0 → descriptron_core-2.1.2}/src/descriptron_core/data/biorag_prompts/schemas/taxpub/tax-treatment-NS0-v1_flat.dtd +0 -0
  37. {descriptron_core-2.1.0 → descriptron_core-2.1.2}/src/descriptron_core/data/biorag_prompts/taxon_profiles/TEMPLATE_taxon_profile.yaml +0 -0
  38. {descriptron_core-2.1.0 → descriptron_core-2.1.2}/src/descriptron_core/data/biorag_prompts/taxon_profiles/diaphorina_taxon_profile.yaml +0 -0
  39. {descriptron_core-2.1.0 → descriptron_core-2.1.2}/src/descriptron_core/tools/FHS_and_CLAHE_V21.py +0 -0
  40. {descriptron_core-2.1.0 → descriptron_core-2.1.2}/src/descriptron_core/tools/biorag_add_discrete_characters_v1.py +0 -0
  41. {descriptron_core-2.1.0 → descriptron_core-2.1.2}/src/descriptron_core/tools/biorag_annotation_reorient_v1.py +0 -0
  42. {descriptron_core-2.1.0 → descriptron_core-2.1.2}/src/descriptron_core/tools/biorag_annotation_screen_v1.py +0 -0
  43. {descriptron_core-2.1.0 → descriptron_core-2.1.2}/src/descriptron_core/tools/biorag_autapomorphy_v1.py +0 -0
  44. {descriptron_core-2.1.0 → descriptron_core-2.1.2}/src/descriptron_core/tools/biorag_calibrate_v1.py +0 -0
  45. {descriptron_core-2.1.0 → descriptron_core-2.1.2}/src/descriptron_core/tools/biorag_calibration_figures_v1.py +0 -0
  46. {descriptron_core-2.1.0 → descriptron_core-2.1.2}/src/descriptron_core/tools/biorag_character_gate_v1.py +0 -0
  47. {descriptron_core-2.1.0 → descriptron_core-2.1.2}/src/descriptron_core/tools/biorag_character_reliability_v1.py +0 -0
  48. {descriptron_core-2.1.0 → descriptron_core-2.1.2}/src/descriptron_core/tools/biorag_character_robustness_v1.py +0 -0
  49. {descriptron_core-2.1.0 → descriptron_core-2.1.2}/src/descriptron_core/tools/biorag_collaborator_forms_v1.py +0 -0
  50. {descriptron_core-2.1.0 → descriptron_core-2.1.2}/src/descriptron_core/tools/biorag_computed_sets_v1.py +0 -0
  51. {descriptron_core-2.1.0 → descriptron_core-2.1.2}/src/descriptron_core/tools/biorag_confabulation_checker_v2.py +0 -0
  52. {descriptron_core-2.1.0 → descriptron_core-2.1.2}/src/descriptron_core/tools/biorag_congruence_compare_v1.py +0 -0
  53. {descriptron_core-2.1.0 → descriptron_core-2.1.2}/src/descriptron_core/tools/biorag_description_refiner_v1.py +0 -0
  54. {descriptron_core-2.1.0 → descriptron_core-2.1.2}/src/descriptron_core/tools/biorag_descriptive_scoring_v1.py +0 -0
  55. {descriptron_core-2.1.0 → descriptron_core-2.1.2}/src/descriptron_core/tools/biorag_feature_policy.py +0 -0
  56. {descriptron_core-2.1.0 → descriptron_core-2.1.2}/src/descriptron_core/tools/biorag_frame_orientation_calibrate_v1.py +0 -0
  57. {descriptron_core-2.1.0 → descriptron_core-2.1.2}/src/descriptron_core/tools/biorag_frame_orientation_v1.py +0 -0
  58. {descriptron_core-2.1.0 → descriptron_core-2.1.2}/src/descriptron_core/tools/biorag_gate_compare_v1.py +0 -0
  59. {descriptron_core-2.1.0 → descriptron_core-2.1.2}/src/descriptron_core/tools/biorag_graph_identify_v1.py +0 -0
  60. {descriptron_core-2.1.0 → descriptron_core-2.1.2}/src/descriptron_core/tools/biorag_graph_support_figure_v1.py +0 -0
  61. {descriptron_core-2.1.0 → descriptron_core-2.1.2}/src/descriptron_core/tools/biorag_homology_frame_v1.py +0 -0
  62. {descriptron_core-2.1.0 → descriptron_core-2.1.2}/src/descriptron_core/tools/biorag_instrument_compare_v3.py +0 -0
  63. {descriptron_core-2.1.0 → descriptron_core-2.1.2}/src/descriptron_core/tools/biorag_key_builder_v1.py +0 -0
  64. {descriptron_core-2.1.0 → descriptron_core-2.1.2}/src/descriptron_core/tools/biorag_key_feature_filter_v2.py +0 -0
  65. {descriptron_core-2.1.0 → descriptron_core-2.1.2}/src/descriptron_core/tools/biorag_key_fuzzy_figures_v1.py +0 -0
  66. {descriptron_core-2.1.0 → descriptron_core-2.1.2}/src/descriptron_core/tools/biorag_key_fuzzy_v1.py +0 -0
  67. {descriptron_core-2.1.0 → descriptron_core-2.1.2}/src/descriptron_core/tools/biorag_key_holdout_v1.py +0 -0
  68. {descriptron_core-2.1.0 → descriptron_core-2.1.2}/src/descriptron_core/tools/biorag_key_jackknife_support_v1.py +0 -0
  69. {descriptron_core-2.1.0 → descriptron_core-2.1.2}/src/descriptron_core/tools/biorag_label_conflict_check_v1.py +0 -0
  70. {descriptron_core-2.1.0 → descriptron_core-2.1.2}/src/descriptron_core/tools/biorag_llm_backend.py +0 -0
  71. {descriptron_core-2.1.0 → descriptron_core-2.1.2}/src/descriptron_core/tools/biorag_make_taxon_profile_v1.py +0 -0
  72. {descriptron_core-2.1.0 → descriptron_core-2.1.2}/src/descriptron_core/tools/biorag_methods_summary_figure_v1.py +0 -0
  73. {descriptron_core-2.1.0 → descriptron_core-2.1.2}/src/descriptron_core/tools/biorag_novelty_score_v1.py +0 -0
  74. {descriptron_core-2.1.0 → descriptron_core-2.1.2}/src/descriptron_core/tools/biorag_ontology_annotator_v2.py +0 -0
  75. {descriptron_core-2.1.0 → descriptron_core-2.1.2}/src/descriptron_core/tools/biorag_outgroup_batch_check_v1.py +0 -0
  76. {descriptron_core-2.1.0 → descriptron_core-2.1.2}/src/descriptron_core/tools/biorag_outline_shape_v1.py +0 -0
  77. {descriptron_core-2.1.0 → descriptron_core-2.1.2}/src/descriptron_core/tools/biorag_prompts/biorag_system_prompts_v1.txt +0 -0
  78. {descriptron_core-2.1.0 → descriptron_core-2.1.2}/src/descriptron_core/tools/biorag_prompts/biorag_system_prompts_v2.txt +0 -0
  79. {descriptron_core-2.1.0 → descriptron_core-2.1.2}/src/descriptron_core/tools/biorag_prompts/diaphorina_user_prompts.txt +0 -0
  80. {descriptron_core-2.1.0 → descriptron_core-2.1.2}/src/descriptron_core/tools/biorag_prompts/ontologies/aism.obo +0 -0
  81. {descriptron_core-2.1.0 → descriptron_core-2.1.2}/src/descriptron_core/tools/biorag_prompts/ontologies/pato.obo +0 -0
  82. {descriptron_core-2.1.0 → descriptron_core-2.1.2}/src/descriptron_core/tools/biorag_prompts/schemas/sdd_1.1/SDD.xsd +0 -0
  83. {descriptron_core-2.1.0 → descriptron_core-2.1.2}/src/descriptron_core/tools/biorag_prompts/schemas/sdd_1.1/SDD_EnumLib.xsd +0 -0
  84. {descriptron_core-2.1.0 → descriptron_core-2.1.2}/src/descriptron_core/tools/biorag_prompts/schemas/sdd_1.1/SDD_TypeLib.xsd +0 -0
  85. {descriptron_core-2.1.0 → descriptron_core-2.1.2}/src/descriptron_core/tools/biorag_prompts/schemas/sdd_1.1/UBIF_BasePattern.xsd +0 -0
  86. {descriptron_core-2.1.0 → descriptron_core-2.1.2}/src/descriptron_core/tools/biorag_prompts/schemas/sdd_1.1/UBIF_CoreExtensions.xsd +0 -0
  87. {descriptron_core-2.1.0 → descriptron_core-2.1.2}/src/descriptron_core/tools/biorag_prompts/schemas/sdd_1.1/UBIF_CoreOntology.xsd +0 -0
  88. {descriptron_core-2.1.0 → descriptron_core-2.1.2}/src/descriptron_core/tools/biorag_prompts/schemas/sdd_1.1/UBIF_EnumLib.xsd +0 -0
  89. {descriptron_core-2.1.0 → descriptron_core-2.1.2}/src/descriptron_core/tools/biorag_prompts/schemas/sdd_1.1/UBIF_LinneanCore.xsd +0 -0
  90. {descriptron_core-2.1.0 → descriptron_core-2.1.2}/src/descriptron_core/tools/biorag_prompts/schemas/sdd_1.1/UBIF_TypeLib.xsd +0 -0
  91. {descriptron_core-2.1.0 → descriptron_core-2.1.2}/src/descriptron_core/tools/biorag_prompts/schemas/sdd_1.1/w3c-schemata/xml.xsd +0 -0
  92. {descriptron_core-2.1.0 → descriptron_core-2.1.2}/src/descriptron_core/tools/biorag_prompts/schemas/taxpub/sample_treatment_deposit_example.xml +0 -0
  93. {descriptron_core-2.1.0 → descriptron_core-2.1.2}/src/descriptron_core/tools/biorag_prompts/schemas/taxpub/sample_zookeys_26056_tp.xml +0 -0
  94. {descriptron_core-2.1.0 → descriptron_core-2.1.2}/src/descriptron_core/tools/biorag_prompts/schemas/taxpub/tax-treatment-NS0-v1_flat.dtd +0 -0
  95. {descriptron_core-2.1.0 → descriptron_core-2.1.2}/src/descriptron_core/tools/biorag_prompts/taxon_profiles/TEMPLATE_taxon_profile.yaml +0 -0
  96. {descriptron_core-2.1.0 → descriptron_core-2.1.2}/src/descriptron_core/tools/biorag_prompts/taxon_profiles/diaphorina_taxon_profile.yaml +0 -0
  97. {descriptron_core-2.1.0 → descriptron_core-2.1.2}/src/descriptron_core/tools/biorag_rebuild_downstream_v1.sh +0 -0
  98. {descriptron_core-2.1.0 → descriptron_core-2.1.2}/src/descriptron_core/tools/biorag_reliability_figures_v1.py +0 -0
  99. {descriptron_core-2.1.0 → descriptron_core-2.1.2}/src/descriptron_core/tools/biorag_rename_species_v1.py +0 -0
  100. {descriptron_core-2.1.0 → descriptron_core-2.1.2}/src/descriptron_core/tools/biorag_rerun_descriptive_tables_v1.sh +0 -0
  101. {descriptron_core-2.1.0 → descriptron_core-2.1.2}/src/descriptron_core/tools/biorag_retest_compare_v1.py +0 -0
  102. {descriptron_core-2.1.0 → descriptron_core-2.1.2}/src/descriptron_core/tools/biorag_specimen_id.py +0 -0
  103. {descriptron_core-2.1.0 → descriptron_core-2.1.2}/src/descriptron_core/tools/biorag_subjective_checks_v1.py +0 -0
  104. {descriptron_core-2.1.0 → descriptron_core-2.1.2}/src/descriptron_core/tools/biorag_type_material_v1.py +0 -0
  105. {descriptron_core-2.1.0 → descriptron_core-2.1.2}/src/descriptron_core/tools/biorag_vlm_characters_v1.py +0 -0
  106. {descriptron_core-2.1.0 → descriptron_core-2.1.2}/src/descriptron_core/tools/biorag_vlm_heatmap_figures_v1.py +0 -0
  107. {descriptron_core-2.1.0 → descriptron_core-2.1.2}/src/descriptron_core/tools/biorag_vlm_reliability_figure_v1.py +0 -0
  108. {descriptron_core-2.1.0 → descriptron_core-2.1.2}/src/descriptron_core/tools/biorag_vlm_roi_figures_v1.py +0 -0
  109. {descriptron_core-2.1.0 → descriptron_core-2.1.2}/src/descriptron_core/tools/biosyslit_rag_retrieval.py +0 -0
  110. {descriptron_core-2.1.0 → descriptron_core-2.1.2}/src/descriptron_core/tools/biosyslit_rag_retrieval_v2.py +0 -0
  111. {descriptron_core-2.1.0 → descriptron_core-2.1.2}/src/descriptron_core/tools/build_monograph_exports_v1.py +0 -0
  112. {descriptron_core-2.1.0 → descriptron_core-2.1.2}/src/descriptron_core/tools/build_species_treatment_docx_v2.py +0 -0
  113. {descriptron_core-2.1.0 → descriptron_core-2.1.2}/src/descriptron_core/tools/coco_combiner_V13.py +0 -0
  114. {descriptron_core-2.1.0 → descriptron_core-2.1.2}/src/descriptron_core/tools/coco_converter_v24.py +0 -0
  115. {descriptron_core-2.1.0 → descriptron_core-2.1.2}/src/descriptron_core/tools/color_extraction_with_names-v8.py +0 -0
  116. {descriptron_core-2.1.0 → descriptron_core-2.1.2}/src/descriptron_core/tools/color_phenomics_homology_v2_1.py +0 -0
  117. {descriptron_core-2.1.0 → descriptron_core-2.1.2}/src/descriptron_core/tools/compile_specimen_data.py +0 -0
  118. {descriptron_core-2.1.0 → descriptron_core-2.1.2}/src/descriptron_core/tools/descriptron_centerline.py +0 -0
  119. {descriptron_core-2.1.0 → descriptron_core-2.1.2}/src/descriptron_core/tools/descriptron_coco_tools.py +0 -0
  120. {descriptron_core-2.1.0 → descriptron_core-2.1.2}/src/descriptron_core/tools/descriptron_convert.py +0 -0
  121. {descriptron_core-2.1.0 → descriptron_core-2.1.2}/src/descriptron_core/tools/descriptron_credentials.py +0 -0
  122. {descriptron_core-2.1.0 → descriptron_core-2.1.2}/src/descriptron_core/tools/descriptron_joints.py +0 -0
  123. {descriptron_core-2.1.0 → descriptron_core-2.1.2}/src/descriptron_core/tools/descriptron_metadata.py +0 -0
  124. {descriptron_core-2.1.0 → descriptron_core-2.1.2}/src/descriptron_core/tools/descriptron_rosetta.py +0 -0
  125. {descriptron_core-2.1.0 → descriptron_core-2.1.2}/src/descriptron_core/tools/landmark_gpa_V1.py +0 -0
  126. {descriptron_core-2.1.0 → descriptron_core-2.1.2}/src/descriptron_core/tools/landmark_gpa_V2.py +0 -0
  127. {descriptron_core-2.1.0 → descriptron_core-2.1.2}/src/descriptron_core/tools/make_reproducibility_bundle.py +0 -0
  128. {descriptron_core-2.1.0 → descriptron_core-2.1.2}/src/descriptron_core/tools/run_full_pipeline_v2.py +0 -0
  129. {descriptron_core-2.1.0 → descriptron_core-2.1.2}/src/descriptron_core/tools/semi_landmark_and_kpts_procrustesV34_GPA.py +0 -0
  130. {descriptron_core-2.1.0 → descriptron_core-2.1.2}/src/descriptron_core/tools/semi_landmark_and_kpts_procrustesV42_GPA.py +0 -0
  131. {descriptron_core-2.1.0 → descriptron_core-2.1.2}/src/descriptron_core/tools/texture_phenomics_from_coco.py +0 -0
  132. {descriptron_core-2.1.0 → descriptron_core-2.1.2}/src/descriptron_core/tools/texture_phenomics_homology.py +0 -0
  133. {descriptron_core-2.1.0 → descriptron_core-2.1.2}/src/descriptron_core/tools/validate_pipeline_gm_vs_geomorph.py +0 -0
  134. {descriptron_core-2.1.0 → descriptron_core-2.1.2}/src/descriptron_core/tools/validate_semilandmarks_vs_geomorph.py +0 -0
  135. {descriptron_core-2.1.0 → descriptron_core-2.1.2}/src/descriptron_core/tools/validate_shape_stats_vs_geomorph.py +0 -0
  136. {descriptron_core-2.1.0 → descriptron_core-2.1.2}/src/descriptron_core/tools/validate_shape_stats_vs_rrpp.py +0 -0
@@ -0,0 +1,59 @@
1
+ Descriptron
2
+ Copyright 2024-2026 Alex R. Van Dam (ORCID 0000-0002-1966-0338)
3
+
4
+ This product includes software developed by Alex R. Van Dam
5
+ https://github.com/alexrvandam/Descriptron
6
+
7
+ Licensed under the Apache License, Version 2.0 (see LICENSE). Under Section
8
+ 4(d) of that licence, this NOTICE file must be included with any
9
+ redistribution of this software or of any work derived from it.
10
+
11
+ REQUIRED CITATION
12
+ -----------------
13
+ Anyone who uses Descriptron, or any software derived from it (including
14
+ descriptron-core, descriptron-vision, descriptron-gui and descriptron-mcp), in
15
+ work that is published, presented or distributed must cite:
16
+
17
+ 1. The software (Descriptron v2), by the DOI of the version used:
18
+ Van Dam, A. R. (2026). Descriptron: morphology-driven species descriptions,
19
+ keys and delimitation for dark taxa (Version 2.1.1). Zenodo.
20
+ https://doi.org/10.5281/zenodo.22958109
21
+ (all versions: https://doi.org/10.5281/zenodo.17077224;
22
+ repository: https://github.com/alexrvandam/Descriptron)
23
+
24
+ 2. The first Descriptron paper:
25
+ Van Dam, A. R. & Štarhová Serbina, L. (2026). Descriptron: Artificial
26
+ intelligence for automating taxonomic species descriptions with a
27
+ user-friendly software package. Systematic Entomology, 51(1), e70005.
28
+ https://doi.org/10.1111/syen.70005
29
+
30
+ 3. Once it is published, the Descriptron v2 paper as well. Its reference
31
+ will be added here and in CITATION.cff on publication.
32
+
33
+ BibTeX:
34
+
35
+ @software{vandam_2026_descriptron_v211,
36
+ author = {Van Dam, Alex R.},
37
+ title = {Descriptron: morphology-driven species descriptions, keys and
38
+ delimitation for dark taxa},
39
+ version = {v2.1.1},
40
+ year = {2026},
41
+ publisher = {Zenodo},
42
+ doi = {10.5281/zenodo.22958109},
43
+ url = {https://doi.org/10.5281/zenodo.22958109}
44
+ }
45
+
46
+ @article{vandam2026descriptron,
47
+ author = {Van Dam, Alex R. and Štarhová Serbina, Liliya},
48
+ title = {Descriptron: Artificial intelligence for automating taxonomic species
49
+ descriptions with a user-friendly software package},
50
+ journal = {Systematic Entomology},
51
+ volume = {51},
52
+ number = {1},
53
+ pages = {e70005},
54
+ year = {2026},
55
+ doi = {10.1111/syen.70005}
56
+ }
57
+
58
+ The same information is in CITATION.cff, which GitHub shows as
59
+ "Cite this repository".
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: descriptron-core
3
- Version: 2.1.0
3
+ Version: 2.1.2
4
4
  Summary: Evidence-tiered species descriptions, keys and audits from phenomic matrices
5
5
  Project-URL: Homepage, https://github.com/alexrvandam/Descriptron
6
6
  Project-URL: Repository, https://github.com/alexrvandam/Descriptron
@@ -9,6 +9,7 @@ Author: Alex R. Van Dam
9
9
  License: Apache-2.0
10
10
  License-File: LICENSE
11
11
  License-File: NOTICE
12
+ License-File: NOTICE.bak_pre_212_20260929_1100
12
13
  Keywords: COCO,morphometrics,phenomics,species description,taxonomy
13
14
  Classifier: Intended Audience :: Science/Research
14
15
  Classifier: License :: OSI Approved :: Apache Software License
@@ -0,0 +1,82 @@
1
+ [build-system]
2
+ requires = ["hatchling"]
3
+ build-backend = "hatchling.build"
4
+
5
+ [project]
6
+ name = "descriptron-core"
7
+ version = "2.1.2"
8
+ description = "Evidence-tiered species descriptions, keys and audits from phenomic matrices"
9
+ readme = "README.md"
10
+ requires-python = ">=3.9"
11
+ license = { text = "Apache-2.0" }
12
+ authors = [{ name = "Alex R. Van Dam" }]
13
+ keywords = ["taxonomy", "morphometrics", "phenomics", "species description", "COCO"]
14
+ classifiers = [
15
+ "Programming Language :: Python :: 3",
16
+ "Topic :: Scientific/Engineering :: Bio-Informatics",
17
+ "Topic :: Scientific/Engineering :: Image Processing",
18
+ "Intended Audience :: Science/Research",
19
+ "License :: OSI Approved :: Apache Software License",
20
+ "Operating System :: OS Independent",
21
+ ]
22
+
23
+ # Derived from the imports of the 62 programs in this package, not from memory:
24
+ # `python -m descriptron_core.cli --list` names them. No torch, no CUDA, no
25
+ # compiler — this installs in about a minute on Linux, macOS and Windows, and
26
+ # with the deposited COCO files it reproduces every analysis in the methods paper.
27
+ dependencies = [
28
+ "numpy>=1.23",
29
+ "pandas>=1.5",
30
+ "scipy>=1.9",
31
+ "matplotlib>=3.6",
32
+ "scikit-learn>=1.2",
33
+ "scikit-image>=0.20",
34
+ "opencv-python-headless>=4.7", # headless: the analysis half never opens a window
35
+ "pillow>=9",
36
+ "pycocotools>=2.0.6",
37
+ "seaborn>=0.12",
38
+ "statsmodels>=0.14",
39
+ "scikit-posthocs>=0.7",
40
+ "umap-learn>=0.5",
41
+ "networkx>=3",
42
+ "pyefd>=1.6",
43
+ "biopython>=1.81",
44
+ "PyYAML>=6",
45
+ "lxml>=4.9",
46
+ "openpyxl>=3.1",
47
+ "python-docx>=1.0", # treatment and monograph .docx export
48
+ ]
49
+
50
+ [project.optional-dependencies]
51
+ # Only needed to *write* treatments. The audit, key, matrix, novelty scoring and
52
+ # delimitation are all deterministic and need no model at all.
53
+ llm = ["anthropic>=0.40"]
54
+ # literature retrieval (the RAG in BioRAG): BioSysLit treatments from Zenodo and your
55
+ # own PDFs. Keyword retrieval needs only the first two; sentence-embedding retrieval
56
+ # (`index --embeddings`) also needs sentence-transformers + faiss-cpu, which pull in torch.
57
+ rag = ["requests>=2.28", "pdfplumber>=0.10"]
58
+ rag-embeddings = ["requests>=2.28", "pdfplumber>=0.10", "sentence-transformers>=2.2", "faiss-cpu>=1.7"]
59
+ dev = ["pytest>=7", "build>=1", "twine>=5"]
60
+
61
+ [project.scripts]
62
+ descriptron = "descriptron_core.cli:main"
63
+ biorag-pipeline = "descriptron_core.cli:run_pipeline"
64
+ biorag-key = "descriptron_core.cli:run_key_builder"
65
+ biorag-audit = "descriptron_core.cli:run_confabulation_checker"
66
+ biorag-novelty = "descriptron_core.cli:run_novelty_score"
67
+ biorag-calibrate = "descriptron_core.cli:run_calibrate"
68
+ descriptron-keys = "descriptron_core.cli:run_keys"
69
+
70
+ [project.urls]
71
+ Homepage = "https://github.com/alexrvandam/Descriptron"
72
+ Repository = "https://github.com/alexrvandam/Descriptron"
73
+ Issues = "https://github.com/alexrvandam/Descriptron/issues"
74
+
75
+ [tool.hatch.build.targets.wheel]
76
+ packages = ["src/descriptron_core"]
77
+
78
+ # tools/ and data/ sit inside the package directory, so `packages` already ships
79
+ # them; a force-include here would add every file a second time and the build
80
+ # fails with "a second file is being added to the wheel archive at the same path".
81
+ [tool.hatch.build]
82
+ artifacts = ["src/descriptron_core/tools/**", "src/descriptron_core/data/**"]
@@ -618,33 +618,52 @@ def main(argv=None):
618
618
 
619
619
  def safe_name(c):
620
620
  return re.sub(r"\W+", "_", c)
621
+
622
+ # specimens with a value for every variable of a model: a blank group (an unidentified specimen) or a blank
623
+ # factor must not become a level of its own. The GPA above still uses every specimen; unidentified ones are
624
+ # what the assignment is for.
625
+ def complete(cols):
626
+ return [i for i in range(len(meta))
627
+ if all(str(data[c][i]).strip().lower() not in ("", "na", "nan", "none") for c in cols if c in data)]
628
+
629
+ def sub(idx):
630
+ return {c: [v[i] for i in idx] for c, v in data.items()}
631
+
632
+ def left_out(idx):
633
+ n = len(meta) - len(idx)
634
+ return [f"{n} specimen(s) without a value for a model variable left out of this analysis."] if n else []
621
635
  if "anova" in a.analyses:
622
636
  default = " + ".join([c for c in [group_col] + factors + conts if c])
623
637
  f = a.formula or default
624
638
  if f:
625
- design = Design(f, data, kinds)
626
- tab = procrustes_anova(Y, design, a.iterations, rng)
639
+ used = [c for c in kinds if c != "logCS" and re.search(r"(?<![\w])" + re.escape(c) + r"(?![\w])", f)]
640
+ idx = complete(used)
641
+ design = Design(f, sub(idx), kinds)
642
+ tab = procrustes_anova(Y[idx], design, a.iterations, rng)
627
643
  write_rows(out / "procrustes_anova.csv", tab)
628
- report += ["## Procrustes ANOVA (RRPP, type I SS)", f"shape ~ {f}", _md(tab), ""]
644
+ report += ["## Procrustes ANOVA (RRPP, type I SS)", f"shape ~ {f}", *left_out(idx), _md(tab), ""]
629
645
  if "allometry" in a.analyses and group_col:
630
- design = Design(f"logCS*{group_col}", data, kinds)
631
- tab = procrustes_anova(Y, design, a.iterations, rng)
646
+ idx = complete([group_col])
647
+ design = Design(f"logCS*{group_col}", sub(idx), kinds)
648
+ tab = procrustes_anova(Y[idx], design, a.iterations, rng)
632
649
  write_rows(out / "allometry_homogeneity_of_slopes.csv", tab)
633
650
  inter = next(r for r in tab if r["term"] == f"logCS:{group_col}")
634
651
  report += ["## Allometry: homogeneity of slopes", f"shape ~ logCS * {group_col}; the interaction tests whether groups "
635
- f"differ in allometric slope (P = {inter['P']:.3g}).", _md(tab), ""]
652
+ f"differ in allometric slope (P = {inter['P']:.3g}).", *left_out(idx), _md(tab), ""]
636
653
  if "trajectory" in a.analyses and group_col and (a.trajectory_levels or factors):
637
654
  lev = a.trajectory_levels or factors[0]
638
- tr = trajectory(Y, data[group_col], [m.get(lev, "") for m in meta], a.iterations, rng)
655
+ idx = [i for i in complete([group_col]) if str(meta[i].get(lev, "")).strip()]
656
+ tr = trajectory(Y[idx], [data[group_col][i] for i in idx], [meta[i].get(lev, "") for i in idx], a.iterations, rng)
639
657
  if tr:
640
658
  write_rows(out / "trajectory_pairwise.csv", tr["pairs"])
641
659
  report += ["## Phenotypic trajectories", f"{group_col} across levels of {lev}: {tr['levels']}", _md(tr["pairs"]), ""]
642
660
  else:
643
661
  report += ["## Phenotypic trajectories", f"skipped: needs >= 2 groups each observed at every level of {lev}", ""]
644
662
  if "disparity" in a.analyses and group_col:
645
- pv, prs = disparity(Y, data[group_col], a.iterations, rng)
663
+ idx = complete([group_col])
664
+ pv, prs = disparity(Y[idx], [data[group_col][i] for i in idx], a.iterations, rng)
646
665
  write_rows(out / "disparity_by_group.csv", pv); write_rows(out / "disparity_pairwise.csv", prs)
647
- report += ["## Morphological disparity (Procrustes variance)", _md(pv), _md(prs), ""]
666
+ report += ["## Morphological disparity (Procrustes variance)", *left_out(idx), _md(pv), _md(prs), ""]
648
667
  if "pls" in a.analyses and conts:
649
668
  E = np.column_stack([np.array(data[c], float) for c in conts]); E = (E - E.mean(0)) / np.where(E.std(0) > 0, E.std(0), 1)
650
669
  r = two_block_pls(Y, E, a.iterations, rng)
@@ -0,0 +1,71 @@
1
+ #!/usr/bin/env python3
2
+ """Centre-line length on shapes of known length (straight bar, arcs of increasing bend, an S-curve), at several
3
+ widths, against the straight measurement a length-along-the-main-axis would give. Writes a CSV and prints a summary;
4
+ the numbers quoted in the paper and README come from this script.
5
+
6
+ python validate_centerline_lengths.py --out_csv centerline_validation.csv
7
+ """
8
+ import argparse
9
+ import csv
10
+ import sys
11
+ from pathlib import Path
12
+
13
+ import cv2
14
+ import numpy as np
15
+
16
+ sys.path.insert(0, str(Path(__file__).resolve().parents[1]))
17
+ import descriptron_centerline as dc # noqa: E402
18
+
19
+
20
+ def mask(pts, width, size=1000):
21
+ m = np.zeros((size, size), np.uint8)
22
+ cv2.polylines(m, [np.round(pts).astype(np.int32)], False, 1, width)
23
+ return m.astype(bool)
24
+
25
+
26
+ def arclen(p):
27
+ return float(np.hypot(*np.diff(p, axis=0).T).sum())
28
+
29
+
30
+ def axis_extent(m):
31
+ ys, xs = np.nonzero(m)
32
+ P = np.stack([xs, ys], 1).astype(float); P -= P.mean(0)
33
+ pr = P @ np.linalg.svd(P, full_matrices=False)[2][0]
34
+ return float(pr.max() - pr.min())
35
+
36
+
37
+ T = np.linspace(0, 1, 4000)
38
+ SHAPES = {"straight": np.stack([200 + 346.4 * T, 300 + 200 * T], 1)}
39
+ for deg in (60, 90, 120):
40
+ a = np.radians(-90 - deg / 2 + deg * T)
41
+ SHAPES[f"arc_{deg}deg"] = np.stack([500 + 300 * np.cos(a), 700 + 300 * np.sin(a)], 1)
42
+ SHAPES["s_curve"] = np.stack([150 + 700 * T, 500 + 120 * np.sin(2 * np.pi * T)], 1)
43
+
44
+
45
+ def main():
46
+ ap = argparse.ArgumentParser(description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter)
47
+ ap.add_argument("--out_csv", required=True)
48
+ a = ap.parse_args()
49
+ rows = []
50
+ for name, pts in SHAPES.items():
51
+ for w in (6, 10, 20, 30):
52
+ m = mask(pts, w)
53
+ true = arclen(pts) + w # round caps add w/2 at each end
54
+ r = dc.centerline(m)
55
+ rows.append(dict(shape=name, width_px=w, true_length_px=round(true, 1),
56
+ centerline_px=round(r["length_px"], 1),
57
+ centerline_err_pct=round(100 * (r["length_px"] - true) / true, 2),
58
+ straight_axis_px=round(axis_extent(m), 1),
59
+ straight_err_pct=round(100 * (axis_extent(m) - true) / true, 2)))
60
+ with open(a.out_csv, "w", newline="") as fh:
61
+ wr = csv.DictWriter(fh, fieldnames=list(rows[0])); wr.writeheader(); wr.writerows(rows)
62
+ ce = np.abs([r["centerline_err_pct"] for r in rows])
63
+ print(f"centre line: |error| median {np.median(ce):.2f}%, max {ce.max():.2f}% over {len(rows)} shape x width cases")
64
+ for name in SHAPES:
65
+ s = [r["straight_err_pct"] for r in rows if r["shape"] == name]
66
+ c = [r["centerline_err_pct"] for r in rows if r["shape"] == name]
67
+ print(f" {name:12s} straight axis {min(s):+.1f} to {max(s):+.1f}% centre line {min(c):+.2f} to {max(c):+.2f}%")
68
+
69
+
70
+ if __name__ == "__main__":
71
+ main()
@@ -0,0 +1,68 @@
1
+ #!/usr/bin/env python3
2
+ """Checks of Descriptron's texture features (texture_phenomics_homology.py).
3
+
4
+ 1. Masked GLCM against scikit-image: pixels outside the mask get an extra 'outside' grey level, scikit-image builds
5
+ the symmetric GLCM, and that level's row and column are dropped; what remains is exactly the 'both pixels in the
6
+ mask' matrix Descriptron computes. (scikit-image names the two diagonals the other way round; Descriptron averages
7
+ the four directions, so its features are unaffected.)
8
+ 2. Grey-level invariance: the same image quantised to 8 and 32 levels; ratio of the feature values (1 = invariant),
9
+ for Descriptron's invariant Haralick formulas and the standard ones.
10
+
11
+ python validate_texture_glcm.py --image_glob "<dir>/*.png" --texture_script <texture_phenomics_homology.py> --out_csv out.csv
12
+ """
13
+ import argparse
14
+ import csv
15
+ import glob
16
+ import importlib.util
17
+
18
+ import cv2
19
+ import numpy as np
20
+ from skimage.feature import graycomatrix
21
+
22
+ ANGLES = (0, np.pi / 4, np.pi / 2, 3 * np.pi / 4)
23
+
24
+
25
+ def main():
26
+ ap = argparse.ArgumentParser(description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter)
27
+ ap.add_argument("--image_glob", required=True); ap.add_argument("--texture_script", required=True)
28
+ ap.add_argument("--n_images", type=int, default=15); ap.add_argument("--out_csv", required=True)
29
+ a = ap.parse_args()
30
+ spec = importlib.util.spec_from_file_location("tx", a.texture_script)
31
+ tx = importlib.util.module_from_spec(spec); spec.loader.exec_module(tx)
32
+ imgs = sorted(glob.glob(a.image_glob))[: a.n_images]
33
+ rows = []
34
+ worst, n = 0.0, 0
35
+ for f in imgs:
36
+ g = cv2.imread(f, 0); q = (g // 16).astype(np.uint8)
37
+ h, w = q.shape; yy, xx = np.mgrid[:h, :w]
38
+ for s in range(3):
39
+ cy, cx, ry, rx = h * (0.3 + 0.2 * s), w * (0.3 + 0.2 * s), h * 0.25, w * 0.2
40
+ m = ((yy - cy) / ry) ** 2 + ((xx - cx) / rx) ** 2 < 1
41
+ for ang in ANGLES:
42
+ ours = tx._masked_glcm_single(q, m, 3, ang, levels=16)
43
+ sk = {np.pi / 4: 3 * np.pi / 4, 3 * np.pi / 4: np.pi / 4}.get(ang, ang)
44
+ R = graycomatrix(np.where(m, q, 16).astype(np.uint8), [3], [sk], levels=17,
45
+ symmetric=True, normed=False)[:16, :16, 0, 0].astype(float)
46
+ R /= R.sum()
47
+ worst = max(worst, float(np.abs(ours - R).max())); n += 1
48
+ full = np.ones(g.shape, bool); gf = g.astype(float); vals = {}
49
+ for L in (8, 32):
50
+ ql = np.clip((gf - gf.min()) / (gf.max() - gf.min()) * (L - 1), 0, L - 1).astype(np.uint8)
51
+ G = np.mean([tx._masked_glcm_single(ql, full, 3, an, levels=L) for an in ANGLES], axis=0)
52
+ I, J = np.meshgrid(np.arange(L), np.arange(L), indexing="ij")
53
+ vals[L] = (tx._haralick_invariant(G, levels=L),
54
+ [float((G * (I - J) ** 2).sum()), float((G / (1 + (I - J) ** 2)).sum()), float((G ** 2).sum())])
55
+ for k, name in enumerate(("contrast", "homogeneity", "energy")):
56
+ rows.append(dict(image=f, feature=name, invariant_ratio_32_vs_8=vals[32][0][k] / vals[8][0][k],
57
+ standard_ratio_32_vs_8=vals[32][1][k] / vals[8][1][k]))
58
+ with open(a.out_csv, "w", newline="") as fh:
59
+ wr = csv.DictWriter(fh, fieldnames=list(rows[0])); wr.writeheader(); wr.writerows(rows)
60
+ print(f"masked GLCM vs scikit-image: max |difference| {worst:.2e} over {n} matrices")
61
+ for name in ("contrast", "homogeneity", "energy"):
62
+ r = [x for x in rows if x["feature"] == name]
63
+ print(f"{name:12s} ratio 32/8 levels, median: invariant {np.median([x['invariant_ratio_32_vs_8'] for x in r]):.2f}"
64
+ f" standard {np.median([x['standard_ratio_32_vs_8'] for x in r]):.2f}")
65
+
66
+
67
+ if __name__ == "__main__":
68
+ main()