Skip to content

Commit

Permalink
version 0.1
Browse files Browse the repository at this point in the history
  • Loading branch information
Vincent Berenz committed Oct 10, 2024
0 parents commit b8077ce
Show file tree
Hide file tree
Showing 23 changed files with 4,258 additions and 0 deletions.
33 changes: 33 additions & 0 deletions .github/workflows/tests.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
name: unit tests

on:
push:
branches:
- master
pull_request:
branches:
- master
workflow_dispatch:

jobs:
test:
runs-on: ubuntu-20.04

strategy:
matrix:
python-version: [3.9.2]

steps:
- uses: actions/checkout@v2
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v2
with:
python-version: ${{ matrix.python-version }}
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install poetry
poetry install --with test
- name: Run tests
run: |
poetry run pytest
16 changes: 16 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# Python bytecode
__pycache__/
.py[cod]

# Environments
venv

# Coverage reports
.coverage
*.xml

# mypy
.mypy_cache/

# Editors.
*~
92 changes: 92 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
[![Python package](https://github.com/MPI-IS/nightskycam-images/actions/workflows/tests.yml/badge.svg)](https://github.com/MPI-IS/nightskycam-images/actions/workflows/tests.yml)
[![PyPI version](https://img.shields.io/pypi/v/nightskycam-images.svg)](https://pypi.org/project/nightskycam-images/)

> 🚧 **Under Construction**
> This project is currently under development. Please check back later for updates.

# Nightskycam Images

This is the repository for `nightskycam_images`,
a python package for managing images captured by the camera-RaspberryPi systems of the nightskycam project.
These images are managed in a filesystem.

Its functions include:
* managing images (and related data) in a filesystem
* generating thumbnail images
* generating summary videos.

## Requirements

* Operating system: Linux or macOS
* Python 3.9+

## Getting Started as a User (using `pip`)

Dependency management with `pip` is easier to set up than with `poetry`, but the optional dependency-groups are not installable with `pip`.

* Create and activate a new Python virtual environment:
```bash
python3 -m venv --copies venv
source venv/bin/activate
```
* Update `pip` and build package:
```bash
pip install -U pip # optional but always advised
pip install . # -e option for editable mode
```

## Getting Started as a Developer (using `poetry`)

Dependency management with `poetry` is required for the installation of the optional dependency-groups.

* Install [poetry](https://python-poetry.org/docs/).
* Install dependencies for package
(also automatically creates project's virtual environment):
```bash
poetry install
```
* Install `dev` dependency group:
```bash
poetry install --with dev
```
* Activate project's virtual environment:
```bash
poetry shell
```
* Optional: Set up pre-commit git hook (automatic `isort` and `black` formatting):
```bash
pre-commit install
```
The hook will now run automatically on `git commit`. It is not recommended, but the hook can be bypassed with the option `--no-verify`.

The hook can also be manually run with:
```bash
# Force checking all files (instead of only changed files).
pre-commit run --all-files
```

## Tests (only possible for setup with `poetry`, not with `pip`)

To install `test` dependency group:
```bash
poetry install --with test
```

To run the tests:
```bash
python -m pytest
```

To extract coverage data:
* Get code coverage by measuring how much of the code is executed when running the tests:
```bash
coverage run -m pytest
```
* View coverage results:
```bash
# Option 1: simple report in terminal.
coverage report
# Option 2: nicer HTML report.
coverage html # Open resulting 'htmlcov/index.html' in browser.
```
Empty file added nightskycam_images/__init__.py
Empty file.
36 changes: 36 additions & 0 deletions nightskycam_images/constants.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
"""
Definition of project-level constants.
"""

from typing import Tuple

# Files in general.
FILE_PERMISSIONS: int = 0o755 # Octal integer.

# HD image.
IMAGE_FILE_FORMATS: Tuple[str, ...] = ("npy", "tiff", "jpg", "jpeg")

# Thumbnail image.
THUMBNAIL_DIR_NAME: str = "thumbnails"
THUMBNAIL_FILE_FORMAT: str = "jpeg"
THUMBNAIL_WIDTH: int = 200 # In pixels.

# Zip.
ZIP_DIR_NAME: str = "zip"

# Video.
VIDEO_FILE_NAME: str = "day_summary.webm"

# Weather summary.
WEATHER_SUMMARY_FILE_NAME: str = "weathers.toml"

# Format patterns.

# formats for filename, e.g. nightskycamX_2024_09_26_13_57_50.jpeg
DATE_FORMAT_FILE: str = "%Y_%m_%d"
TIME_FORMAT_FILE: str = "%H_%M_%S"
DATETIME_FORMATS: Tuple[str, ...] = ("%d_%m_%Y_%H_%M_%S", "%Y_%m_%d_%H_%M_%S")

# formats for displaying on the website
DATE_FORMAT: str = "%Y-%m-%d"
TIME_FORMAT: str = "%H:%M:%S"
44 changes: 44 additions & 0 deletions nightskycam_images/convert_npy.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
from pathlib import Path
import tempfile

import PIL.Image as PILImage
import cv2
import numpy as np
import numpy.typing as npt


def _bits_reduction(data: npt.NDArray, target: type) -> npt.NDArray:
original_max = np.iinfo(data.dtype).max
target_max = np.iinfo(target).max
ratio = target_max / original_max
return (data * ratio).astype(target)


def _to_8bits(image: npt.NDArray) -> npt.NDArray:
return _bits_reduction(image, np.uint8)


def npy_file_to_pil(image_path: Path) -> PILImage.Image:
"""
Read the file (expected to be an .npy file) and
return a corresponding instance of PIL Image.
It first converts the image to 8 bits.
"""
img_array = np.load(image_path)
img_array = _to_8bits(img_array)

# Create a temporary directory for intermediary tiff file.
with tempfile.TemporaryDirectory() as tmp_dir:
tiff_file_path = Path(tmp_dir) / f"{image_path.stem}.tiff"
cv2.imwrite(str(tiff_file_path), img_array, [cv2.IMWRITE_TIFF_COMPRESSION, 1])
return PILImage.open(str(tiff_file_path))


def npy_file_to_numpy(image_path: Path) -> np.ndarray:
"""
Read the file (expected to be an .npy file) and
return a corresponding numpy array, converted to 8 bits.
"""
img_array = np.load(image_path)
img_array = _to_8bits(img_array)
return img_array
42 changes: 42 additions & 0 deletions nightskycam_images/folder_change.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import os
from pathlib import Path
from typing import Optional


def folder_has_changed(
folder: Path, history: Optional[dict[Path, Optional[float]]]
) -> bool:
"""
Check whether the last-modified-time of the directory changed
since the previous check.
If there is no history of a previous check:
return True and initialise history.
Parameters
----------
folder
Path to directory.
history
Last modification times found in previous check:
- key:
Path to directory.
- value:
Time of last modification of directory.
"""
history = history if history else {}

# Float: number of seconds since epoch.
last_modified_time = os.path.getmtime(folder)

# Set to dummy value when key does NOT exist.
previous_modified_time = history.get(folder, None)

# If the folder has NO previous history
# OR was modified since previous history,
# update the history.
if not previous_modified_time or previous_modified_time < last_modified_time:
history[folder] = last_modified_time
return True

return False
Loading

0 comments on commit b8077ce

Please sign in to comment.