Working iris

This commit is contained in:
2026-02-13 13:12:33 +01:00
parent 108004e959
commit 0ee74cbcb8
16 changed files with 429 additions and 1 deletions
+1
View File
@@ -0,0 +1 @@
iris/data/
+11 -1
View File
@@ -1,2 +1,12 @@
# Laboratorium Biometrii
To repozytorium pozwala zbudowac obraz Docker uzywany podczas cwiczenia z biometerii poczas laboratium Optoelektronika w diagnostyce i zabezpieczeniach obiektow.
To repozytorium pozwala zbudowac obrazy Docker używane podczas ćwiczenia z biometerii podczas laboratium Optoelektronika w diagnostyce i zabezpieczeniach obiektów.
## Rozpoznawanie tęczówki
Budowanie obrazu:
```
docker build -t iris:latest ./iris
```
Uruchomienie obrazu:
```
docker run --rm -p 8888:8888 --mount type=bind,src=./iris/data,dst=/src/data iris:latest
```
+27
View File
@@ -0,0 +1,27 @@
FROM debian:13.3
# Install system dependencies
RUN apt-get update && apt-get install -y wget libgl1 libglib2.0-0 patchelf
# Copy project files
COPY ./src /src
# Create a non-root user
RUN useradd -m student
RUN chown -R student:student /src
USER student
# Install uv
RUN wget -qO- https://astral.sh/uv/install.sh | sh
ENV PATH="/home/student/.local/bin:$PATH"
# Create main env for Jupyter Notebook
WORKDIR /src
RUN uv venv --python 3.11
ENV PATH="/src/.venv/bin:$PATH"
RUN uv pip install notebook open-iris
RUN patchelf --clear-execstack /src/.venv/lib/python3.11/site-packages/onnxruntime/capi/*.so
# Launch Jupyter Notebook
EXPOSE 8888
CMD ["python", "-m", "jupyter", "notebook", "--ip=0.0.0.0", "--port=8888", "--no-browser", "--IdentityProvider.token=''"]
Binary file not shown.
View File
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 54 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 106 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 155 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 181 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 42 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 213 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 855 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 134 KiB

+330
View File
@@ -0,0 +1,330 @@
{
"cells": [
{
"cell_type": "markdown",
"id": "70737cde",
"metadata": {
"vscode": {
"languageId": "plaintext"
}
},
"source": [
"# Skrypt do laboratorium z biometrii tęczówki"
]
},
{
"cell_type": "markdown",
"id": "87df3026",
"metadata": {},
"source": [
"## Zadanie 1:\n",
"\n",
"W celach przyszłej analizy należy wykonać zdjęcia tęczówki w różnych warunkach.\n",
"\n",
"Domyślnie zdjęcia powinny być robione dla **prawego** oka, w podczerwieni, patrząc prosto w kamerę, bez włączonego oświetlacza i wykadrowane w następujący sposób:\n",
"\n",
"![](./img/sample.png)\n",
"\n",
"Należy wykonać następujące zdjęcia oraz odpowiednio je nazwać. Zdjęcia będą usunięte na koniec laboratorium.\n",
"W każdym podpunkcie można wykonać dowolną ilość zestawów zdjęć (w nazwie x zastąpić numerem zestawu), ale w jednym zestawie powinny być zdjęcia tego samego oka.\n",
"\n",
"* Co najmniej dwa zdjęcia oka każdego z członków grupy (w tym przypadku x oznacza numer zdjęcia tego samego oka):\n",
" nazwa pliku: `<imie>_normal_x.png`\n",
"* Oko w świetle widzialnym i podczerwonym\n",
" nazwa pliku: `visible_x.png`, `infrared_x.png`\n",
"* Oko w ciemości, przy normalnym oświetleniu i przy włączonym oświetlaczu\n",
" nazwa pliku: `dark_x.png`, `dim_x.png`, `bright_x.png`\n",
"* Oko przymróżone, standardowo otwarte i maksymalnie otwarte (powieki przytrzymane palcami)\n",
" nazwa pliku: `squint_x.png`, `open_x.png`, `fully_open_x.png`\n",
"* Oko patrzące na 5 różnych celów za kamerą.\n",
" nazwa pliku: `-45_x.png`, `-20_x.png`, `0_x.png`, `20_x.png`, `45_x.png`\n",
"* Oko w różnych odległościach od kamery: standardowa, pół-metra, metr\n",
" nazwa pliku: `20cm_x.png`, `50cm_x.png`, `1m_x.png`\n",
"* Zdjęcie lewego i prawego oka tej samej osoby:\n",
" nazwa pliku: `left_x.png`, `right_x.png`\n",
"* Dowolne inne zdjęcia oka:\n",
" nazwa pliku: `other_x.png`\n",
"\n",
"Wszystkie zdjęcia należy umieścić w folderze `./data/input/`. "
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "dc47a2e9",
"metadata": {},
"outputs": [],
"source": [
"# Uruchom aby przeprocesować wszystkie zdjęcia\n",
"# Po dodaniu nowych zdjęć, uruchom ponownie ten skrypt, aby zaktualizować dane. Jedynie nowe i zmienione zdjęcia zostaną przetworzone, więc proces będzie szybki.\n",
"\n",
"from .process import process\n",
"output = process(\"data\")"
]
},
{
"cell_type": "markdown",
"id": "1459dd9e",
"metadata": {},
"source": [
"## Rozpoznawanie geometrii oka\n",
"Program używa sieci neuronowej aby przydzielić każdy z pikseli obrazu do następujących kategorii: `źrenica`, `tęczówka`, `oko`, `zakłócenia` (włosy, rzęsy itd.).\n",
"Przykładowy przydział został przedstawiony na zdjęciu poniżej. (Przydziały wszystkich zdjęć znajdują się w folderze `./data/segmentation`)\n",
"\n",
"![](./img/segmentation.png)\n",
"\n",
"Dane te użyte są następnie do rozpoznania geometrii oka na zdjęciu. Program znajduje najlepiej dopasowany okrąg bądź elipsę do kształtu pikseli z kategorii `źrenica` i `tęczówka`.\n",
"Za pomocą pikseli z kategorii `oko` program rozpoznaje orientację oka (poziomo/pionowo i wszystko pomiędzy) poprzez znalezienie w którym kierunku \"wydłużony\" jest kształt oka.\n",
"Przykładowa znaleziona geometria przedstawiona jest poniżej (a wszyskie pozostałe zdjęcia znajdują się w folderze `./data/geometry`):\n",
"\n",
"![](./img/geometry.png)\n",
"\n",
"Program potrafii również ocenić czy oko patrzy w kierunku kamery poprzez ocenę eliptyczności tęczówki.\n",
"Im tęczówka jest bliższa okręgu tym bardziej osoba bezpośrednio patrzy w kamerę, a im bardziej eliptyczna, tym bardziej na bok."
]
},
{
"cell_type": "markdown",
"id": "386ad177",
"metadata": {},
"source": [
"## Zadanie 2:\n",
"Na podstawie wykonanych zdjęć i wyników z `./data/geometry` oceńcie przy jakich warunkach program nie radzi sobie z rozpoznawaniem geometrii oka."
]
},
{
"cell_type": "markdown",
"id": "68d1f14d",
"metadata": {},
"source": [
"## Zadanie 3:\n",
"Zbadajcie jak kierunek patrzenia wpływa na wynik zwracany przez program.\n",
"Aby otrzymać wynik należy użyć poniższego skryptu uzupełniając uprzednio nazwę zdjęcia.\n",
"Zaproponujcie wartość progową dla której program powinien przyjąć że oko patrzy bezpośrednio w kierunku kamery.\n",
"Spróbujcie wykonać takie zdjęcie aby \"oszukać\" program."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "2456c1f9",
"metadata": {},
"outputs": [],
"source": [
"image_names = [\"-45_1.png\", \"-20_1.png\", \"0_1.png\", \"20_1.png\", \"45_1.png\"] # Tutaj dodaj nazwy zdjęć, które chcesz przetestować, np. \"other_1.png\"\n",
"# TODO: Return offgaze score from image"
]
},
{
"cell_type": "markdown",
"id": "505d2288",
"metadata": {},
"source": [
"## Normalizacja tęczówki\n",
"Na każdym zdjęciu tęczówka znajduje się w trochę innym miejscu, wzrok skierowany jest w innym kierunku, oraz oko jest przystosowane do innej ilości światła.\n",
"W celu porównania różnych zdjęć geometria tęczówki musi zostać znormalizowana.\n",
"Jest to wykonywane na podstawie geometrii znalezionej w poprzednim kroku. Tęczówka jest \"rozwijana\" z okręgu na linię prostą oraz skalowana do standardowych wymiarów.\n",
"Schemat działania tej operacji przedstawiono na rysunku:\n",
"\n",
"![](./img/norm_explanation.png)\n",
"\n",
"Przykładowe zdjęcie tak rozwiniętej tęczówki przedstawiono poniżej:\n",
"\n",
"![](./img/norm.png)\n",
"\n",
"W tym momencie zdjęcie ma już standardowe wymiary w pikselach, w naszym przypadku: 512x128 px."
]
},
{
"cell_type": "markdown",
"id": "25451080",
"metadata": {},
"source": [
"## Zadanie 4:\n",
"Następny skrypt losuje dwa zdjęcia oczu i rysuje oryginalne zdjęcie oraz znormalizowaną tęczówkę.\n",
"Zróbcie zrzut ekranu a następnie zaznaczcie w programie graficznym (np. gimp) jak tęczówka została rozwinięta.\n",
"Można to zrobić np. zaznaczając kilka punktów szczególnych na obu reprezentacjach."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "a08aabfe",
"metadata": {},
"outputs": [],
"source": [
"# TODO: Return two random images and their normalized irises"
]
},
{
"cell_type": "markdown",
"id": "90d1e6b5",
"metadata": {},
"source": [
"## Zadanie 5:\n",
"Tęczówka reaguje na poziom oświetlenia w otoczeniu tak aby do oka wpadała zbliżona ilość światła.\n",
"W tym celu tęczówka kurczy się albo rozluźnia - zmienia się jej szerokość.\n",
"Jest to jeden z powodów dla których przeprowadzana jest normalizacja.\n",
"Poniższy skrypt wyświetli znormalizowane zdjęcia tęczówki tego samego oka.\n",
"Spróbujcie ocenić które zdjęcie było wykonane przy najmniejszej ilości światła a które przy największej.\n",
"Po decyzji uruchomcie kolejny skrypt aby zweryfikować waszą decyzję.\n",
"W jaki sposób poziom oświetlenia może wpływać na jakość rozpoznawania tęczówki?"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "a71342a0",
"metadata": {},
"outputs": [],
"source": [
"# TODO: Return normalized images with different lighting conditions"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "c6fefb47",
"metadata": {},
"outputs": [],
"source": [
"dark = 0 # Wpisz numer zdjęcia odpowiadającemu jasności\n",
"normal = 0\n",
"bright = 0\n",
"# TODO: Verify"
]
},
{
"cell_type": "markdown",
"id": "81ecf2fe",
"metadata": {},
"source": [
"## Zadanie 6:\n",
"Poniższy skrypt wylosuje jedno znormalizowane zdjęcie tęczówki, oraz pokaże 5 innych zdjęć.\n",
"Spróbujcie dopasować tęczówkę do opisanych zdjęć. W programie graficznym zaznaczcie dlaczego wybraliście dane zdjęcie.\n",
"Po wybraniu zdjęcia możecie uruchomić następny skrypt aby zweryfikować wasz wybór."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "783c6763",
"metadata": {},
"outputs": [],
"source": [
"# TODO: Return one random normalized iris and 5 other images with names"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "ebeffd14",
"metadata": {},
"outputs": [],
"source": [
"selected = 0 # Wpisz numer wybranego zdjęcia (1-5) \n",
"# TODO: Verify"
]
},
{
"cell_type": "markdown",
"id": "a69fd482",
"metadata": {},
"source": [
"## Filtr Gabora\n",
"Ostatnim krokiem w przetwarzaniu obrazu tęczówki jest użycie tzw filtru Gabora.\n",
"Jest to specjalistyczny filtr to przetwarzania obrazów 2D, wykorzystywany do analizy tekstur.\n",
"W dużym uproszczeniu znajduje on wybraną częstotliwość na obrazie w postaci zmieniających się pasów oraz znajduje kierunek tych zmian.\n",
"\n",
"![](./img/gabor.png)\n",
"\n",
"Filtr ten jest użyty aby znaleźć punkty szczególne na obrazie a następnie jego wynik jest progowany i zamieniany na wartość 0 albo 1.\n",
"W ten sposób dla każdego filtru i oka uzyskuje się 512X128 bitów (x2 ze względu na część rzeczywistą i urojonę) które służą jako skompresowany opis danej tęczówki.\n",
"W tym przypadku używane są dwie różne częstotliwości. Cały obraz został w ten sposób skompresowany do 32KB danych które są łatwo porównywalne z innymi zdjęciami.\n",
"\n",
"Przykładowy wynik filtru i progowania został przedstawiony poniżej. Czerwone fragmenty to takie które zostały odrzucone ze względu na zakłócenia takie jak np. powieka czy rzęsy:\n",
"\n",
"![](./img/filtered.png)"
]
},
{
"cell_type": "markdown",
"id": "54fa38ce",
"metadata": {
"vscode": {
"languageId": "plaintext"
}
},
"source": [
"## Zadanie 7:\n",
"Poniższy skrypt wylosuje jeden wynik filtrowania, oraz pokaże 5 innych podpisanych wyników.\n",
"Spróbujcie dopasować tęczówkę do opisanych zdjęć. Do tego celu możecie np użyć warstw w programie graficznym.\n",
"Po wybraniu zdjęcia możecie uruchomić następny skrypt aby zweryfikować wasz wybór."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "5afd9590",
"metadata": {},
"outputs": [],
"source": [
"# TODO: Return one random iris template and 5 other templates with names"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "5063ad6b",
"metadata": {},
"outputs": [],
"source": [
"selected = 0 # Wpisz numer wybranego zdjęcia (1-5) \n",
"# TODO: Verify"
]
},
{
"cell_type": "markdown",
"id": "08d5d94d",
"metadata": {},
"source": [
"## Dopasowanie tęczówki\n",
"Program w celu dopasowania tęczówki porównuje wynik przetwarzania zdjęcia nowej tęczówki z uprzednio przetworzonymi zdjęciami zapisanymi w bazie.\n",
"Do tego celu wykorzystuje tzw. odległość Hamminga czyli proporcję ilości bitów nie zgodnych do ilości wszystkich bitów.\n",
"Ze względu na wybrany sposób przetwarzania obrazu, średnia proporcja dla dwóch różnych tęczówek będzie równa 0.5 ponieważ szansa na wartość piksela równą 0 albo 1 jest taka sama.\n",
"Natomiast dla zdjęć tej samej tęczówki odległość Hamminga będzie znacząco mniejsza.\n",
"Na poniższym rysunku przedstawiono rozkłady dla pasujących oraz niepasujących zdjęć dla 2.5 miliona par zdjęć.\n",
"\n",
"![](./img/hamming.png)"
]
},
{
"cell_type": "markdown",
"id": "049caf5e",
"metadata": {},
"source": [
"## Zadanie 8:\n",
"Używając poniższego skryptu porównaj różne pary zdjęć a następnie oszacuj jaka wartość odległości Hamminga mogła by służyć jako próg odróżniający pasujące i nie pasujące zdjęcia"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "5bbae8b2",
"metadata": {},
"outputs": [],
"source": [
"photo_a = \"other1.png\"\n",
"photo_b = \"other1.png\"\n",
"# TODO: Compare and return hamming distance"
]
}
],
"metadata": {
"language_info": {
"name": "python"
}
},
"nbformat": 4,
"nbformat_minor": 5
}
+60
View File
@@ -0,0 +1,60 @@
import pathlib
import cv2
import matplotlib.pyplot as plt
import iris
def process(path: str) -> None:
path = pathlib.Path(path)
iris_pipeline = iris.IRISPipeline(env=iris.IRISPipeline.DEBUGGING_ENVIRONMENT)
iris_visualizer = iris.visualisation.IRISVisualizer()
outputs = []
for image_path in (path / "input").iterdir():
if image_path.suffix != ".png":
continue
print(f"Processing {image_path}...")
eye_side = "right" if "left" not in image_path.stem else "left"
image_pixels = cv2.imread(str(image_path), cv2.IMREAD_GRAYSCALE)
img = iris.IRImage(
img_data=image_pixels,
eye_side=eye_side,
)
output = iris_pipeline(image_pixels, eye_side=eye_side)
iris_visualizer.plot_segmentation_map(
ir_image=img,
segmap=iris_pipeline.call_trace["segmentation"],
)
(path / "segmentation").mkdir(parents=True, exist_ok=True)
plt.savefig(path / "segmentation" / f"{image_path.stem}.png")
iris_visualizer.plot_all_geometry(
ir_image=img,
geometry_polygons=iris_pipeline.call_trace["geometry_estimation"],
eye_orientation=iris_pipeline.call_trace["eye_orientation"],
eye_center=iris_pipeline.call_trace["eye_center_estimation"],
)
(path / "geometry").mkdir(parents=True, exist_ok=True)
plt.savefig(path / "geometry" / f"{image_path.stem}.png")
iris_visualizer.plot_normalized_iris(
normalized_iris=iris_pipeline.call_trace["normalization"],
)
(path / "normalized").mkdir(parents=True, exist_ok=True)
plt.savefig(path / "normalized" / f"{image_path.stem}.png")
iris_visualizer.plot_iris_template(
iris_template=iris_pipeline.call_trace["encoder"],
)
(path / "template").mkdir(parents=True, exist_ok=True)
plt.savefig(path / "template" / f"{image_path.stem}.png")
outputs.append((image_path, img, output))