Working iris
@@ -0,0 +1 @@
|
||||
iris/data/
|
||||
@@ -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
|
||||
```
|
||||
@@ -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=''"]
|
||||
|
After Width: | Height: | Size: 54 KiB |
|
After Width: | Height: | Size: 106 KiB |
|
After Width: | Height: | Size: 155 KiB |
|
After Width: | Height: | Size: 181 KiB |
|
After Width: | Height: | Size: 42 KiB |
|
After Width: | Height: | Size: 213 KiB |
|
After Width: | Height: | Size: 855 KiB |
|
After Width: | Height: | Size: 134 KiB |
@@ -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",
|
||||
"\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",
|
||||
"\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",
|
||||
"\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",
|
||||
"\n",
|
||||
"\n",
|
||||
"Przykładowe zdjęcie tak rozwiniętej tęczówki przedstawiono poniżej:\n",
|
||||
"\n",
|
||||
"\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",
|
||||
"\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",
|
||||
""
|
||||
]
|
||||
},
|
||||
{
|
||||
"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",
|
||||
""
|
||||
]
|
||||
},
|
||||
{
|
||||
"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
|
||||
}
|
||||
@@ -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))
|
||||