Compare commits
4
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6af462858b | ||
|
|
d9c59b6edc | ||
|
|
3031f8e0ac | ||
|
|
ae5a6f5959 |
@@ -0,0 +1,4 @@
|
|||||||
|
venv/
|
||||||
|
__pycache__/
|
||||||
|
*.pyc
|
||||||
|
.pytest_cache/
|
||||||
+59
-1
@@ -92,7 +92,7 @@ Objectif : déterminer si une image donnée, cadrée sur un objet du ciel profon
|
|||||||
| `ra_deg` | float | Ascension droite, en degrés décimaux (converti depuis le format heures/minutes/secondes natif : 1h = 15°) |
|
| `ra_deg` | float | Ascension droite, en degrés décimaux (converti depuis le format heures/minutes/secondes natif : 1h = 15°) |
|
||||||
| `dec_deg` | float | Déclinaison, en degrés décimaux (-90 à +90) |
|
| `dec_deg` | float | Déclinaison, en degrés décimaux (-90 à +90) |
|
||||||
| `magnitude` | float | Magnitude apparente (échelle inversée : plus la valeur est basse, plus l'objet est brillant) |
|
| `magnitude` | float | Magnitude apparente (échelle inversée : plus la valeur est basse, plus l'objet est brillant) |
|
||||||
| `proper_name` | string ou NULL | Nom propre de l'étoile si disponible (absent pour la grande majorité des entrées du catalogue) |
|
| `proper_name` | string ou NULL | Nom propre de l'étoile si disponible (absent pour la grande majorité des entrées du catalogue) |
|
||||||
|
|
||||||
Exemple de ligne (Sirius / Alpha Canis Majoris) :
|
Exemple de ligne (Sirius / Alpha Canis Majoris) :
|
||||||
|
|
||||||
@@ -103,6 +103,64 @@ hip_id;ra_deg;dec_deg;magnitude;proper_name
|
|||||||
|
|
||||||
> Note d'implémentation : les valeurs décimales ci-dessus utilisent un point comme séparateur (format attendu par `float()` en Python / `pandas.read_csv` par défaut), à ne pas confondre avec la notation à virgule utilisée à l'oral pendant la conception.
|
> Note d'implémentation : les valeurs décimales ci-dessus utilisent un point comme séparateur (format attendu par `float()` en Python / `pandas.read_csv` par défaut), à ne pas confondre avec la notation à virgule utilisée à l'oral pendant la conception.
|
||||||
|
|
||||||
|
## Modèle de données - constellations
|
||||||
|
|
||||||
|
**Fichier** : `dataset_constellations.json`
|
||||||
|
**Format** : JSON, liste d'objets
|
||||||
|
|
||||||
|
Chaque constellation référence ses étoiles via leur `hip_id` (clé commune avec `dataset_hipparcos.csv`), sans duplication des données d'étoiles. La structure ne conserve qu'une liste de `liaisons` (paires d'étoiles reliées) - la liste des étoiles composant la constellation est dérivable de cette liste, ce qui évite toute redondance/désynchronisation entre deux champs qui porteraient la même information.
|
||||||
|
|
||||||
|
L'identifiant `id` suit la nomenclature standardisée de l'Union Astronomique Internationale (abréviation à 3 lettres, ex. `UMa` pour la Grande Ourse) plutôt qu'un identifiant arbitraire propre au projet, pour rester compatible avec toute source de données externe (Stellarium, autres catalogues) qui utiliserait la même convention.
|
||||||
|
|
||||||
|
```json
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"id": "UMa",
|
||||||
|
"nom": "Grande Ourse",
|
||||||
|
"liaisons": [
|
||||||
|
[54061, 53910],
|
||||||
|
[53910, 58001],
|
||||||
|
[58001, 59774],
|
||||||
|
[59774, 54061],
|
||||||
|
[59774, 62956],
|
||||||
|
[62956, 65378],
|
||||||
|
[65378, 67301]
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
```
|
||||||
|
|
||||||
|
## Arborescence du projet
|
||||||
|
|
||||||
|
```
|
||||||
|
data/
|
||||||
|
pictures_test/ # images de test, Pipeline B
|
||||||
|
messier_classifier/ # dataset C0/C1/C2
|
||||||
|
manifest.csv # colonnes : nom;label (label = "messier" ou "ngc")
|
||||||
|
dataset_hipparcos.csv
|
||||||
|
dataset_constellations.json
|
||||||
|
pipeline_a/
|
||||||
|
skymap_generator.py
|
||||||
|
pipeline_b/
|
||||||
|
star_detector.py
|
||||||
|
pipeline_c/
|
||||||
|
c0_dataset_acquisition.py
|
||||||
|
c1_model_training.py
|
||||||
|
c2_operational_model.py
|
||||||
|
tests/
|
||||||
|
tests_pipeline_a.py
|
||||||
|
tests_pipeline_b.py
|
||||||
|
tests_pipeline_c0.py
|
||||||
|
tests_pipeline_c1.py
|
||||||
|
tests_pipeline_c2.py
|
||||||
|
app.py
|
||||||
|
CONTRIBUTING.md
|
||||||
|
ARCHITECTURE.md
|
||||||
|
README.md
|
||||||
|
```
|
||||||
|
|
||||||
|
Chaque pipeline (A/B/C) vit dans son propre dossier, reflétant l'indépendance actée en phase de Découpage. Le pipeline C conserve la séparation C0/C1/C2 jusque dans les fichiers de test, ces trois sous-étapes ayant des natures très différentes (scraping, entraînement, inférence). Pas de fichier `utils.py` créé par anticipation : le code partagé entre pipelines, s'il s'avère nécessaire, sera extrait au moment où un besoin concret de duplication apparaîtra, plutôt que par anticipation.
|
||||||
|
|
||||||
## Décisions techniques actées
|
## Décisions techniques actées
|
||||||
|
|
||||||
- Catalogue source : Hipparcos, pré-traité offline via `astropy` en CSV propre (RA, Dec, magnitude, nom) plutôt que parsé à la volée dans l'application
|
- Catalogue source : Hipparcos, pré-traité offline via `astropy` en CSV propre (RA, Dec, magnitude, nom) plutôt que parsé à la volée dans l'application
|
||||||
|
|||||||
@@ -0,0 +1,29 @@
|
|||||||
|
import numpy as np # Je le laisse pour plus tard
|
||||||
|
import cv2
|
||||||
|
import os
|
||||||
|
|
||||||
|
def reading_picture(path):
|
||||||
|
if not os.path.exists(path):
|
||||||
|
raise FileNotFoundError(f"{path} n'existe pas.")
|
||||||
|
pic = cv2.imread(path, cv2.IMREAD_GRAYSCALE)
|
||||||
|
if pic is None:
|
||||||
|
raise ValueError(f"{path} est corrompu ou n'est pas une image valide.")
|
||||||
|
return pic
|
||||||
|
|
||||||
|
def blob_detector(picture):
|
||||||
|
params = cv2.SimpleBlobDetector_Params()
|
||||||
|
params.minThreshold = 10
|
||||||
|
params.maxThreshold = 200
|
||||||
|
params.filterByArea = True
|
||||||
|
params.minArea = 4
|
||||||
|
params.filterByCircularity = True
|
||||||
|
params.minCircularity = 0.1
|
||||||
|
params.filterByConvexity = True
|
||||||
|
params.minConvexity = 0.87
|
||||||
|
params.filterByInertia = True
|
||||||
|
params.minInertiaRatio = 0.01
|
||||||
|
# TODO: valeurs provisoires, à recalibrer avec de vraies images de test cette semaine.
|
||||||
|
detector = cv2.SimpleBlobDetector.create(params)
|
||||||
|
stars = detector.detect(picture)
|
||||||
|
result = [star.pt for star in stars]
|
||||||
|
return result
|
||||||
Binary file not shown.
@@ -0,0 +1,29 @@
|
|||||||
|
import pytest
|
||||||
|
import cv2
|
||||||
|
import numpy as np
|
||||||
|
from pipeline_b import star_detector
|
||||||
|
|
||||||
|
def test_file_exists_and_is_an_image(tmp_path):
|
||||||
|
file = tmp_path / "image.jpg"
|
||||||
|
tableau_image = np.random.randint(0, 256, (64, 64), dtype=np.uint8)
|
||||||
|
cv2.imwrite(str(file), tableau_image)
|
||||||
|
assert isinstance(star_detector.reading_picture(file), np.ndarray)
|
||||||
|
|
||||||
|
def test_file_exists_not_an_image(tmp_path):
|
||||||
|
file = tmp_path / "file.txt"
|
||||||
|
file.write_text("Not an image.")
|
||||||
|
with pytest.raises(ValueError):
|
||||||
|
star_detector.reading_picture(file)
|
||||||
|
|
||||||
|
def test_file_does_not_exists(tmp_path):
|
||||||
|
with pytest.raises(FileNotFoundError):
|
||||||
|
star_detector.reading_picture(tmp_path/"do_I_exist.txt")
|
||||||
|
|
||||||
|
@pytest.mark.skip(reason="A corriger : égalité stricte sur flottants, canall couleur incohérent, détour disque inutile")
|
||||||
|
def test_blob_detector_actually_detects_a_blob(tmp_path):
|
||||||
|
file = tmp_path / "image.jpg"
|
||||||
|
tableau_image = np.full((64,64), 0, dtype=np.uint8)
|
||||||
|
cv2.imwrite(str(file), tableau_image)
|
||||||
|
src = cv2.imread(file)
|
||||||
|
cv2.circle(src, center=(23, 31), radius = 5, color= (255, 255, 255), thickness=-1)
|
||||||
|
assert star_detector.blob_detector(src) == [(23, 31)]
|
||||||
Reference in New Issue
Block a user