Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
82 changes: 76 additions & 6 deletions src/vstarstack/library/calibration/dark.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
#
# Copyright (c) 2022 Vladislav Tsendrovskii
# Copyright (c) 2022-2024 Vladislav Tsendrovskii
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
Expand All @@ -12,6 +12,8 @@
# along with this program. If not, see <https://www.gnu.org/licenses/>.
#

import math
from typing import Generator
import vstarstack.library.common
import vstarstack.library.data
import vstarstack.library.merge.simple_mean
Expand All @@ -34,8 +36,13 @@ def remove_dark(dataframe : vstarstack.library.data.DataFrame,

for channel in dataframe.get_channels():
image, opts = dataframe.get_channel(channel)
if not opts["brightness"]:
print(f"Skipping {channel}")
if dataframe.get_channel_option(channel, "weight"):
continue
if not dataframe.get_channel_option(channel, "brightness"):
print(f"Skipping {channel}, not brightness")
continue
if dataframe.get_channel_option(channel, "dark-removed"):
print(f"Skipping {channel}, dark already removed")
continue

if channel in dark.get_channels():
Expand All @@ -44,10 +51,73 @@ def remove_dark(dataframe : vstarstack.library.data.DataFrame,
dark_layer, _ = dark.get_channel(dark_channel_name)

image = image - dark_layer
opts["dark-removed"] = True
dataframe.replace_channel(image, channel, **opts)
return dataframe

def prepare_darks(images : vstarstack.library.common.IImageSource
) -> vstarstack.library.data.DataFrame:
class TemperatureIndex:
def __init__(self, delta_temperature : float, basic_temperature : float):
self.dt = delta_temperature
self.bt = basic_temperature

def temperature_to_index(self, temperature : float) -> int | None:
if temperature is None or self.bt is None or self.dt is None:
return None
return math.floor((temperature - self.bt) / self.dt + 0.5)

def index_to_temperature(self, index : int) -> float:
if index is None or self.bt is None or self.dt is None:
return None
return index * self.dt + self.bt

class FilterSource(vstarstack.library.common.IImageSource):
"""Filter sources with exposure/gain/temperature"""
def __init__(self, source : vstarstack.library.common.IImageSource,
exposure : float,
gain : float,
temperature : float | None,
temperature_indexer : TemperatureIndex):
self.temperature_indexer = temperature_indexer
self.source = source
self.exposure = exposure
self.gain = gain
self.temperature = temperature
self.temperature_idx = self.temperature_indexer.temperature_to_index(self.temperature)

def items(self) -> Generator[vstarstack.library.data.DataFrame, None, None]:
for df in self.source.items():
exposure = df.get_parameter("exposure")
gain = df.get_parameter("gain")
temperature = df.get_parameter("temperature")
temperature_idx = self.temperature_indexer.temperature_to_index(temperature)
if exposure == self.exposure and gain == self.gain and temperature_idx == self.temperature_idx:
yield df

def empty(self) -> bool:
# TODO: better detect if there are matched df in source list
return self.source.empty()

def prepare_darks(images : vstarstack.library.common.IImageSource,
basic_temperature : float | None,
delta_temperature : float | None) -> list:
"""Build dark frame"""
return vstarstack.library.merge.simple_mean.mean(images)
parameters = set()
indexer = TemperatureIndex(delta_temperature, basic_temperature)
for df in images.items():
exposure = df.get_parameter("exposure")
gain = df.get_parameter("gain")
temperature = df.get_parameter("temperature")
index = indexer.temperature_to_index(temperature)
parameters.add((exposure, gain, index))

darks = []
for exposure, gain, index in parameters:
image_source = FilterSource(images, exposure, gain, temperature, indexer)
dark = vstarstack.library.merge.simple_mean.mean(image_source)
dark.add_parameter(exposure, "exposure")
dark.add_parameter(gain, "gain")
temperature = indexer.index_to_temperature(index)
if temperature is not None:
dark.add_parameter(temperature, "temperature")
darks.append((exposure, gain, temperature, dark))
return darks
30 changes: 17 additions & 13 deletions src/vstarstack/library/loaders/classic.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
"""Reading common image files: jpg/png/tiff"""
#
# Copyright (c) 2023 Vladislav Tsendrovskii
# Copyright (c) 2023-2024 Vladislav Tsendrovskii
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
Expand All @@ -15,43 +15,47 @@

import numpy as np
from PIL import Image
import exifread

import vstarstack.library.common
import vstarstack.library.data
import vstarstack.library.loaders.tags

def readjpeg(fname: str):
"""Read single image (jpg, png, tiff) file"""
rgb = np.asarray(Image.open(fname)).astype(np.float32)
shape = rgb.shape
shape = (shape[0], shape[1])

tags = vstarstack.library.loaders.tags.read_tags(fname)
with open(fname, 'rb') as file:
tags = exifread.process_file(file)

params = {
"w": shape[1],
"h": shape[0],
}

try:
exposure = tags["shutter"]*tags["iso"]
except KeyError as _:
exposure = 1
if "EXIF ExposureTime" in tags:
tag = tags["EXIF ExposureTime"]
params["exposure"] = float(tag.values[0])
else:
params["exposure"] = 1

if "EXIF ISOSpeedRatings" in tags:
tag = tags["EXIF ISOSpeedRatings"]
params["gain"] = float(tag.values[0])
else:
params["gain"] = 1

weight = np.ones((shape[0], shape[1]))*exposure
params["weight"] = params["exposure"] * params["gain"]

dataframe = vstarstack.library.data.DataFrame(params, tags)
dataframe.add_channel(weight, "weight", weight=True)

if len(rgb.shape) == 3:
dataframe.add_channel(rgb[:, :, 0], "R", brightness=True, signal=True)
dataframe.add_channel(rgb[:, :, 1], "G", brightness=True, signal=True)
dataframe.add_channel(rgb[:, :, 2], "B", brightness=True, signal=True)
dataframe.add_channel_link("R", "weight", "weight")
dataframe.add_channel_link("G", "weight", "weight")
dataframe.add_channel_link("B", "weight", "weight")
elif len(rgb.shape) == 2:
dataframe.add_channel(rgb[:, :], "L", brightness=True, signal=True)
dataframe.add_channel_link("L", "weight", "weight")
else:
# unknown shape!
pass
Expand Down
19 changes: 9 additions & 10 deletions src/vstarstack/library/loaders/fits.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
"""Reading FITS files"""
#
# Copyright (c) 2022 Vladislav Tsendrovskii
# Copyright (c) 2022-2024 Vladislav Tsendrovskii
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
Expand All @@ -13,7 +13,6 @@
# along with this program. If not, see <https://www.gnu.org/licenses/>.
#

import numpy as np
from astropy.io import fits

import vstarstack.library.data
Expand Down Expand Up @@ -44,21 +43,22 @@ def readfits(filename: str):
params["UTC"] = tags["DATE-OBS"]

if "EXPTIME" in plane.header:
exptime = plane.header["EXPTIME"]
params["exposure"] = float(plane.header["EXPTIME"])
else:
exptime = 1
params["exposure"] = 1

if "GAIN" in plane.header:
gain = plane.header["GAIN"]
params["gain"] = float(plane.header["GAIN"])
else:
gain = 1
params["gain"] = 1

if "CCD-TEMP" in plane.header:
params["temperature"] = float(plane.header["CCD-TEMP"])

slice_names = []

params["weight"] = params["exposure"]*params["gain"]
dataframe = vstarstack.library.data.DataFrame(params, tags)
weight_channel_name = "weight"
weight = np.ones((shape[1], shape[2]))*exptime*gain
dataframe.add_channel(weight, weight_channel_name, weight=True)

if shape[0] == 1:
if "FILTER" in plane.header:
Expand All @@ -76,6 +76,5 @@ def readfits(filename: str):

for i, slice_name in enumerate(slice_names):
dataframe.add_channel(original[i, :, :], slice_name, brightness=True, signal=True)
dataframe.add_channel_link(slice_name, weight_channel_name, "weight")

yield dataframe
19 changes: 9 additions & 10 deletions src/vstarstack/library/loaders/nef.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
"""Reading NEF image files"""
#
# Copyright (c) 2023 Vladislav Tsendrovskii
# Copyright (c) 2023-2024 Vladislav Tsendrovskii
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
Expand All @@ -15,7 +15,6 @@

import rawpy
import exifread
from exifread.classes import IfdTag

import vstarstack.library.common
import vstarstack.library.data
Expand All @@ -41,13 +40,17 @@ def readnef(filename: str):

if "EXIF ExposureTime" in tags:
tag = tags["EXIF ExposureTime"]
exposure = float(tag.values[0])
params["exposure"] = float(tag.values[0])
else:
exposure = 1
params["exposure"] = 1

iso = 1
if "EXIF ISOSpeedRatings" in tags:
tag = tags["EXIF ISOSpeedRatings"]
params["gain"] = float(tag.values[0])
else:
params["gain"] = 1

exp = exposure * iso
params["weight"] = params["exposure"] * params["gain"]

printable_tags = {}
for tag_name in tags:
Expand All @@ -56,8 +59,4 @@ def readnef(filename: str):
dataframe = vstarstack.library.data.DataFrame(params, printable_tags)
dataframe.add_channel(image, "raw", encoded=True, brightness=True, signal=True)
dataframe.add_parameter(bayer, "format")
dataframe.add_parameter(exp, "weight")
dataframe.add_parameter(exposure, "exposure")
dataframe.add_parameter(iso, "gain")

yield dataframe
9 changes: 3 additions & 6 deletions src/vstarstack/library/loaders/ser.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
"""Read SER images"""
#
# Copyright (c) 2023 Vladislav Tsendrovskii
# Copyright (c) 2023-2024 Vladislav Tsendrovskii
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
Expand Down Expand Up @@ -192,6 +192,8 @@ def readser(fname: str):
"w": width,
"h": height,
"format" : image_format,
"exposure" : 1,
"gain" : 1,
"weight" : 1,
}

Expand All @@ -206,12 +208,7 @@ def readser(fname: str):
frame = _read_to_npy(file, bpp, le16bit, shape)
params["UTC"] = utc
dataframe = vstarstack.library.data.DataFrame(params, tags)
exptime = 1
weight = np.ones(frame.data.shape[0:2])*exptime
index = 0
for index, channel in enumerate(channels):
dataframe.add_channel(frame[:, :, index], channel, **opts)
if dataframe.get_channel_option(channel, "signal"):
dataframe.add_channel(weight, "weight-"+channel, weight=True)
dataframe.add_channel_link(channel, "weight-"+channel, "weight")
yield dataframe
36 changes: 0 additions & 36 deletions src/vstarstack/library/loaders/tags.py

This file was deleted.

17 changes: 5 additions & 12 deletions src/vstarstack/library/loaders/video.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
"""Read video source file"""
#
# Copyright (c) 2022 Vladislav Tsendrovskii
# Copyright (c) 2022-2024 Vladislav Tsendrovskii
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
Expand All @@ -13,7 +13,6 @@
# along with this program. If not, see <https://www.gnu.org/licenses/>.
#

import numpy as np
import cv2

import vstarstack.library.data
Expand All @@ -29,25 +28,19 @@ def read_video(fname: str):
if not success:
break

tags = {
"depth": 8,
}
tags = {}

params = {
"w": frame.shape[1],
"h": frame.shape[0],
"exposure" : 1,
"gain" : 1,
"weight" : 1,
}

exptime = 1
weight = np.ones((frame.shape[0], frame.shape[1]))*exptime

dataframe = vstarstack.library.data.DataFrame(params, tags)
dataframe.add_channel(frame[:, :, 0], "R", brightness=True, signal=True)
dataframe.add_channel(frame[:, :, 1], "G", brightness=True, signal=True)
dataframe.add_channel(frame[:, :, 2], "B", brightness=True, signal=True)
dataframe.add_channel(weight, "weight", weight=True)
dataframe.add_channel_link("R", "weight", "weight")
dataframe.add_channel_link("G", "weight", "weight")
dataframe.add_channel_link("B", "weight", "weight")
yield dataframe
frame_id += 1
Loading