Răsfoiți Sursa

initial commit

Felix Kleinsteuber 3 ani în urmă
comite
211141371e
10 a modificat fișierele cu 1270 adăugiri și 0 ștergeri
  1. 2 0
      .gitignore
  2. 475 0
      analyze_dataset.ipynb
  3. BIN
      dataset_stats.npy
  4. 505 0
      experiments.ipynb
  5. 158 0
      get_image_exif.ipynb
  6. BIN
      lapseDates_1.npy
  7. BIN
      motionDates_1.npy
  8. 40 0
      py/Dataset.py
  9. 66 0
      py/DatasetStatistics.py
  10. 24 0
      py/FileUtils.py

+ 2 - 0
.gitignore

@@ -0,0 +1,2 @@
+*.jpg
+__pycache__

Fișier diff suprimat deoarece este prea mare
+ 475 - 0
analyze_dataset.ipynb


BIN
dataset_stats.npy


Fișier diff suprimat deoarece este prea mare
+ 505 - 0
experiments.ipynb


+ 158 - 0
get_image_exif.ipynb

@@ -0,0 +1,158 @@
+{
+ "cells": [
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "# Get EXIF data from image"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 1,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "from PIL import Image, ExifTags"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 9,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "def print_exif(img_path):\n",
+    "    img = Image.open(img_path)\n",
+    "    img_exif = img.getexif()\n",
+    "\n",
+    "    for key, val in img_exif.items():\n",
+    "        if key in ExifTags.TAGS:\n",
+    "            print(f\"{ExifTags.TAGS[key]} ({key}): {val}\")\n",
+    "        else:\n",
+    "            print(f\"(unknown) {key}: {val}\")"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 10,
+   "metadata": {},
+   "outputs": [
+    {
+     "name": "stdout",
+     "output_type": "stream",
+     "text": [
+      "PrintImageMatching (50341): b'PrintIM\\x000300\\x00\\x00\\x00\\x03\\x00\\x01\\x00\"\\x00\"\\x00\\n\\x00\\x00\\x00\\x00\\x00\\x0b\\x00\\x00\\x00y\\x00\\x00\\x00\\x00\\x00\\x00'\n",
+      "ResolutionUnit (296): 2\n",
+      "ExifOffset (34665): 158\n",
+      "ImageDescription (270): DCIM\\101RECNX\n",
+      "Make (271): RECONYX\n",
+      "Model (272): UltraFire\n",
+      "Software (305): 1.6.20190312a\u0000\u0000\u0000\u0000\u0000\n",
+      "Orientation (274): 1\n",
+      "DateTime (306): 2021:04:26 05:25:21\n",
+      "YCbCrPositioning (531): 1\n",
+      "XResolution (282): 72.0\n",
+      "YResolution (283): 72.0\n"
+     ]
+    }
+   ],
+   "source": [
+    "print_exif(\"sample.jpg\")"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 11,
+   "metadata": {},
+   "outputs": [
+    {
+     "name": "stdout",
+     "output_type": "stream",
+     "text": [
+      "ResolutionUnit (296): 2\n",
+      "ExifOffset (34665): 240\n",
+      "Make (271): CUDDEBACK\n",
+      "Model (272): G-2+\n",
+      "Software (305): 1200 FW 7.10.0\n",
+      "DateTime (306): 2021:06:27 23:01:30\n",
+      "Orientation (274): 1\n",
+      "YCbCrPositioning (531): 2\n",
+      "Copyright (33432): Copyright Cuddeback, 2021\n",
+      "XResolution (282): 72.0\n",
+      "YResolution (283): 72.0\n"
+     ]
+    }
+   ],
+   "source": [
+    "print_exif(\"sample2.jpg\")"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 14,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "from datetime import datetime\n",
+    "\n",
+    "def get_image_date(img_path):\n",
+    "    img = Image.open(img_path)\n",
+    "    date_raw = img.getexif()[306]\n",
+    "    return datetime.strptime(date_raw, \"%Y:%m:%d %H:%M:%S\")"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 15,
+   "metadata": {},
+   "outputs": [
+    {
+     "data": {
+      "text/plain": [
+       "datetime.datetime(2021, 4, 26, 5, 25, 21)"
+      ]
+     },
+     "execution_count": 15,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    "get_image_date(\"sample.jpg\")"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": []
+  }
+ ],
+ "metadata": {
+  "interpreter": {
+   "hash": "31f2aee4e71d21fbe5cf8b01ff0e069b9275f58929596ceb00d14d90e3e16cd6"
+  },
+  "kernelspec": {
+   "display_name": "Python 3.6.9 64-bit",
+   "language": "python",
+   "name": "python3"
+  },
+  "language_info": {
+   "codemirror_mode": {
+    "name": "ipython",
+    "version": 3
+   },
+   "file_extension": ".py",
+   "mimetype": "text/x-python",
+   "name": "python",
+   "nbconvert_exporter": "python",
+   "pygments_lexer": "ipython3",
+   "version": "3.6.9"
+  },
+  "orig_nbformat": 4
+ },
+ "nbformat": 4,
+ "nbformat_minor": 2
+}

BIN
lapseDates_1.npy


BIN
motionDates_1.npy


+ 40 - 0
py/Dataset.py

@@ -0,0 +1,40 @@
+import os
+from tqdm import tqdm
+from py.DatasetStatistics import DatasetStatistics
+from py.FileUtils import list_folders, list_jpegs_recursive
+
+expected_subfolders = sorted(["Motion", "Lapse", "Full"])
+
+class Dataset:
+
+    def __init__(self, base_path: str):
+        self.base_path = base_path
+        self.raw_sessions = []
+        self.__parse_subdirectories()
+    
+
+    def __parse_subdirectories(self):
+        self.raw_sessions = sorted(list_folders(self.base_path))
+        # Verify every session contains the subfolders Motion, Lapse, Full
+        for folder in self.raw_sessions:
+            path = os.path.join(self.base_path, folder)
+            subfolders = list_folders(path)
+            assert sorted(subfolders) == expected_subfolders
+        print(f"Found {len(self.raw_sessions)} sessions")
+
+
+    def get_sessions(self) -> list:
+        # cut off the first 33 characters (redundant)
+        return [name[33:] for name in self.raw_sessions]
+    
+    def create_statistics(self) -> DatasetStatistics:
+        counts = {}
+        for folder in tqdm(self.raw_sessions):
+            counts[folder[33:]] = {}
+            counts[folder[33:]]["Total"] = 0
+            for subfolder in expected_subfolders:
+                path = os.path.join(self.base_path, folder, subfolder)
+                numFiles = len(list_jpegs_recursive(path))
+                counts[folder[33:]][subfolder] = numFiles
+                counts[folder[33:]]["Total"] += numFiles
+        return DatasetStatistics(counts)

+ 66 - 0
py/DatasetStatistics.py

@@ -0,0 +1,66 @@
+from turtle import pd
+from warnings import warn
+
+import numpy as np
+import pandas as pd
+
+class DatasetStatistics:
+
+    def __init__(self, stats_dict: dict = None, load_from_file: str = None):
+        """Create a new statistics instance. The statistics can either be defined
+        as a dict (see Dataset#create_statistics()) or loaded from a file.
+
+        Args:
+            stats_dict (dict, optional): Dict with statistics. Defaults to None.
+            load_from_file (str, optional): File saved with DatasetStatistics#save(). Defaults to None.
+
+        Raises:
+            ValueError: If neither stats_dict nor load_from_file is set.
+        """
+        self.stats = {}
+        if stats_dict is not None:
+            self.stats = stats_dict
+        elif load_from_file is not None:
+            self.load(load_from_file)
+        else:
+            raise ValueError("Please provide 'stats_dict' or 'load_from_file'.")
+        self.__update_dataframe()
+
+    def __update_dataframe(self):
+        self.df = pd.DataFrame.from_dict(self.stats).transpose()
+
+    def add_total_row(self, row_name = "Z_Total") -> "DatasetStatistics":
+        if row_name in self.stats:
+            warn(f"{row_name} is already a defined row")
+            return self
+        self.stats[row_name] = {}
+        # iterate over all folders and subfolders
+        for folder in self.stats:
+            if folder != row_name:
+                for subfolder in self.stats[folder]:
+                    # add to total row
+                    if subfolder in self.stats[row_name]:
+                        self.stats[row_name][subfolder] += self.stats[folder][subfolder]
+                    else:
+                        self.stats[row_name][subfolder] = self.stats[folder][subfolder]
+        self.__update_dataframe()
+        return self
+    
+    def save(self, filename = "dataset_stats.npy"):
+        np.save(filename, self.stats)
+        print(f"Saved to {filename}.")
+    
+    def load(self, filename = "dataset_stats.npy"):
+        self.stats = np.load(filename, allow_pickle=True).tolist()
+        self.__update_dataframe()
+        print(f"Loaded from {filename}.")
+
+    def view(self, col_order = ["Lapse", "Motion", "Full", "Total"]) -> pd.DataFrame:
+        return self.df.sort_index()[col_order]
+    
+    def plot_sessions(self, cols = ["Lapse", "Motion", "Full"], figsize = (20, 10), style = {"width": 2}, exclude_last_row = False):
+        df = self.df[cols]
+        # Plot lapse, motion, full columns without the last row (Z_Total)
+        if exclude_last_row:
+            df = df.iloc[:-1]
+        return df.plot.bar(figsize=figsize, style=style)

+ 24 - 0
py/FileUtils.py

@@ -0,0 +1,24 @@
+from glob import glob
+import os
+
+def list_folders(path: str) -> list:
+    """Returns the names of all immediate child folders of path.
+
+    Args:
+        path (str): path to search
+
+    Returns:
+        list: list of all child folder names
+    """
+    return [name for name in os.listdir(path) if os.path.isdir(os.path.join(path, name))]
+
+def list_jpegs_recursive(path: str) -> list:
+    """Recursively lists all jpeg files in path.
+
+    Args:
+        path (str): path to search
+
+    Returns:
+        list: list of all jpeg files
+    """
+    return [name for name in glob(os.path.join(path, "**/*.jpg"), recursive=True) if os.path.isfile(os.path.join(path, name))]

Unele fișiere nu au fost afișate deoarece prea multe fișiere au fost modificate în acest diff