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
36 changes: 36 additions & 0 deletions src/vstarstack/library/data.py
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,8 @@ def add_channel(self, data : np.ndarray, name : str, **options):
options["encoded"] = False
if "brightness" not in options:
options["brightness"] = False
if "signal" not in options:
options["signal"] = False

self.channels[name] = {
"data": data,
Expand Down Expand Up @@ -126,6 +128,21 @@ def add_parameter(self, value, name : str):
"""Add parameter"""
self.params[name] = value

def get_parameter(self, name : str):
"""
Get parameter

Parameters:
name (str) - parameter name

Returns:
None - if parameter doesn't exists
parameter value - if parameter exists
"""
if name not in self.params:
return None
return self.params[name]

def get_channel(self, channel : str) -> Tuple[np.ndarray, dict]:
"""
Get channel image.
Expand All @@ -142,6 +159,25 @@ def get_channels(self) -> List[str]:
"""Get list of channels"""
return list(self.channels.keys())

def get_channel_option(self, channel : str, option : str) -> bool | None:
"""
Get option of channel.

Parameters:
channel (str) - channel name
option (str) - option name

Returns:
None if channel doesn't exist
False if option is not exist
option value if option is exist
"""
if channel not in self.channels:
return None
if option not in self.channels[channel]["options"]:
return False
return self.channels[channel]["options"][option]

@staticmethod
def _store_json(value, file):
file.write(bytes(json.dumps(value, indent=4, ensure_ascii=False), 'utf8'))
Expand Down
23 changes: 11 additions & 12 deletions src/vstarstack/library/debayer/bayer.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ def generate_mask(name):
return result_mask

def debayer_image(image : np.ndarray,
weight : np.ndarray,
weight : float,
mask : dict):
"""Process debayer on image"""
h = image.shape[0]
Expand All @@ -51,11 +51,9 @@ def debayer_image(image : np.ndarray,

if h % 2 == 1:
image = image[0:h-1,:]
weight = weight[0:h-1,:]
h -= 1
if w % 2 == 1:
image = image[:,0:w-1]
weight = weight[:,0:w-1]
w -= 1

for color in mask:
Expand All @@ -67,10 +65,10 @@ def debayer_image(image : np.ndarray,
image01 = image[0::2, 1::2]
image10 = image[1::2, 0::2]
image11 = image[1::2, 1::2]
weight00 = weight[0::2, 0::2]
weight01 = weight[0::2, 1::2]
weight10 = weight[1::2, 0::2]
weight11 = weight[1::2, 1::2]
weight00 = np.ones(image00.shape)*weight
weight01 = np.ones(image01.shape)*weight
weight10 = np.ones(image10.shape)*weight
weight11 = np.ones(image11.shape)*weight

layers[color] = k00 * image00 + k01 * image01 + k10 * image10 + k11 * image11
weights[color] = k00 * weight00 + k01 * weight01 + k10 * weight10 + k11 * weight11
Expand All @@ -82,7 +80,8 @@ def debayer_dataframe(dataframe : vstarstack.library.data.DataFrame,
raw_channel_name : str):
"""Debayer dataframe"""
raw, _ = dataframe.get_channel(raw_channel_name)
weight, _ = dataframe.get_channel(dataframe.links["weight"][raw_channel_name])
if (weight := dataframe.get_parameter("weight")) is None:
weight = 1

layers, weights = debayer_image(raw, weight, mask)
for color in layers:
Expand All @@ -93,10 +92,10 @@ def debayer_dataframe(dataframe : vstarstack.library.data.DataFrame,
dataframe.remove_channel(dataframe.links["weight"][raw_channel_name])
dataframe.remove_channel(raw_channel_name)

dataframe.params["w"] = int(dataframe.params["w"]/2)
dataframe.params["h"] = int(dataframe.params["h"]/2)
dataframe.params["format"] = "flat"
if "projection" in dataframe.params and dataframe.params["projection"] == "perspective":
dataframe.add_parameter(int(dataframe.get_parameter("w")/2), "w")
dataframe.add_parameter(int(dataframe.get_parameter("h")/2), "h")
dataframe.add_parameter("flat", "format")
if dataframe.get_parameter("projection") == "perspective":
dataframe.params["perspective_kw"] *= 2
dataframe.params["perspective_kh"] *= 2
return dataframe
9 changes: 4 additions & 5 deletions src/vstarstack/library/loaders/classic.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,15 +43,14 @@ def readjpeg(fname: str):
dataframe.add_channel(weight, "weight", weight=True)

if len(rgb.shape) == 3:
dataframe.add_channel(rgb[:, :, 0], "R", brightness=True)
dataframe.add_channel(rgb[:, :, 1], "G", brightness=True)
dataframe.add_channel(rgb[:, :, 2], "B", brightness=True)
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", weight_name="weight", brightness=True)
dataframe.add_channel(rgb[:, :], "L", brightness=True, signal=True)
dataframe.add_channel_link("L", "weight", "weight")
else:
# unknown shape!
Expand Down
6 changes: 2 additions & 4 deletions src/vstarstack/library/loaders/fits.py
Original file line number Diff line number Diff line change
Expand Up @@ -68,9 +68,7 @@ def readfits(filename: str):
yield None

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

yield dataframe
10 changes: 3 additions & 7 deletions src/vstarstack/library/loaders/nef.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,15 +34,11 @@ def readnef(filename: str):
"h": image.data.shape[0],
}

exptime = tags["shutter"]*tags["iso"]
exp = tags["shutter"]*tags["iso"]

dataframe = vstarstack.library.data.DataFrame(params, tags)

weight = np.ones(image.data.shape)*exptime

dataframe.add_channel(image, "raw", encoded=True)
dataframe.add_channel(weight, "weight", weight=True)
dataframe.add_channel_link("raw", "weight", "weight")

dataframe.add_parameter("bayerGRBG", "format")
dataframe.add_parameter(exp, "weight")

yield dataframe
8 changes: 5 additions & 3 deletions src/vstarstack/library/loaders/ser.py
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,7 @@ def readser(fname: str):
channels = ["L"] # luminocity
image_format = "flat"
opts["brightness"] = True
opts["signal"] = True
vpp = 1
elif colorid == 8:
shape = (height, width, 1)
Expand Down Expand Up @@ -173,6 +174,7 @@ def readser(fname: str):
"w": width,
"h": height,
"format" : image_format,
"weight" : 1,
}

with open(fname, "rb") as trailer_f:
Expand All @@ -191,7 +193,7 @@ def readser(fname: str):
index = 0
for index, channel in enumerate(channels):
dataframe.add_channel(frame[:, :, index], channel, **opts)
dataframe.add_channel(weight, "weight-"+channel, weight=True)
dataframe.add_channel_link(channel, "weight-"+channel, "weight")
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

6 changes: 3 additions & 3 deletions src/vstarstack/library/loaders/video.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,9 +42,9 @@ def read_video(fname: str):
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)
dataframe.add_channel(frame[:, :, 1], "G", brightness=True)
dataframe.add_channel(frame[:, :, 2], "B", brightness=True)
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")
Expand Down
4 changes: 1 addition & 3 deletions src/vstarstack/library/loaders/yuv.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,11 +45,9 @@ def readyuv(fname: str, width: int, height: int):

dataframe = vstarstack.library.data.DataFrame(params, tags)
exptime = 1
weight = np.ones(frame.data.shape)*exptime

dataframe.add_channel(yuv, "raw", encoded=True)
dataframe.add_channel(weight, "weight")
dataframe.add_channel_link("raw", "weight", "weight")
dataframe.add_parameter("yuv422", "format")
dataframe.add_parameter(exptime, "weight")
yield dataframe
frame_id += 1
97 changes: 71 additions & 26 deletions src/vstarstack/library/movement/move_image.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,19 +37,37 @@ def _generate_points(height, width):

def move_image(image: np.ndarray,
transformation: basic_movement.Movement,
input_proj, output_proj,
image_weight: float,
image_weight_layer=None):
"""Apply movement to image"""
shape = image.shape
input_proj, output_proj,*,
image_weight: float = 1,
image_weight_layer: np.ndarray | None = None,
output_shape: tuple | None = None):
"""
Apply movement to image

Parameters:
image (np.ndarray) - input image
transformation (Movement) - movement which should be applied
input_proj (Projection) - input image projection
output_proj (Projection) - output image projection
image_weight (float) - weight of input image, if weight layer is not provided
image_weight_layer (np.ndarray) - weight layer of input image
output_shape (tuple(h,w)) - dimensions of output image
Returns:
shifted image, shifted layer
"""
if output_shape is None:
shape = image.shape
else:
shape = output_shape

h = shape[0]
w = shape[1]

shifted = np.zeros(shape)
shifted_weight_layer = np.zeros(shape)

if image_weight_layer is None:
image_weight_layer = np.ones(shape)*image_weight
image_weight_layer = np.ones(image.shape)*image_weight

positions = _generate_points(h, w)
original_positions = transformation.reverse(positions.astype('double'),
Expand All @@ -66,36 +84,63 @@ def move_image(image: np.ndarray,
transform_array[y, x, 1] = orig_x

crdtf = lambda pos : tuple(transform_array[pos[0], pos[1], :])
shifted = scipy.ndimage.geometric_transform(image, crdtf, order=3)
shifted_weight_layer = scipy.ndimage.geometric_transform(image_weight_layer, crdtf, order=3)
shifted = scipy.ndimage.geometric_transform(image, crdtf, output_shape=shape, order=3)
shifted_weight_layer = scipy.ndimage.geometric_transform(image_weight_layer, crdtf, output_shape=shape, order=3)
return shifted, shifted_weight_layer

def move_dataframe(dataframe: DataFrame,
transformation: basic_movement.Movement,
proj=None):
"""Apply movement to dataframe"""
if proj is None:
proj = vstarstack.library.projection.tools.get_projection(dataframe)
transformation: basic_movement.Movement,*,
input_proj = None,
output_proj = None,
output_shape : tuple | None = None):
"""Apply movement to dataframe
Parameters:
dataframe (DataFrame) - input dataframe
transformation (Movement) - movement which should be applied
input_proj (Projection) - input image projection
output_proj (Projection) - output image projection
output_shape (tuple(h,w)) - dimensions of output image
Returns:
shifted image, shifted layer"""

if input_proj is None:
input_proj = vstarstack.library.projection.tools.get_projection(dataframe)
if output_proj is None:
output_proj = input_proj

if output_shape is None:
w = dataframe.get_parameter("w")
h = dataframe.get_parameter("h")
output_shape = (h, w)

output_dataframe = DataFrame()
output_dataframe.add_parameter(output_shape[0], "h")
output_dataframe.add_parameter(output_shape[1], "w")

for channel in dataframe.get_channels():
image, opts = dataframe.get_channel(channel)
if opts["weight"]:
continue
if opts["encoded"]:
if not dataframe.get_channel_option(channel, "signal"):
continue

weight_channel = None
if channel in dataframe.links["weight"]:
weight_channel = dataframe.links["weight"][channel]

if weight_channel:
weight, _ = dataframe.get_channel(weight_channel)
else:
weight = np.ones(image.shape)*1

shifted, shifted_weight = move_image(image, transformation, proj, proj, weight)
dataframe.add_channel(shifted, channel, **opts)
dataframe.add_channel(shifted_weight, weight_channel, weight=True)
dataframe.add_channel_link(channel, weight_channel, "weight")

return dataframe
if (w := dataframe.get_parameter("weight")) is not None:
weight = np.ones(image.shape)*w
else:
weight = np.ones(image.shape)

shifted, shifted_weight = move_image(image,
transformation,
input_proj,
output_proj,
image_weight_layer=weight,
output_shape=output_shape)

output_dataframe.add_channel(shifted, channel, **opts)
output_dataframe.add_channel(shifted_weight, weight_channel, weight=True)
output_dataframe.add_channel_link(channel, weight_channel, "weight")

return output_dataframe
6 changes: 6 additions & 0 deletions src/vstarstack/library/projection/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
from enum import Enum

class ProjectionType(Enum):
Perspective = 1
Orthographic = 2
Equirectangular = 3
15 changes: 8 additions & 7 deletions src/vstarstack/library/projection/projections/lib/perspective.c
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* 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 @@ -18,24 +18,25 @@
#include "perspective.h"

bool perspective_projection_init(struct PerspectiveProjection *self,
double W, double H, double F,
double kw, double kh, double F,
double w, double h)
{
if (h <= 0 || w <= 0 ||
W <= 0 || H <= 0 ||
kw <= 0 || kh <= 0 ||
F <= 0)
{
return false;
}

self->W = W;
self->H = H;
self->kx = kw;
self->ky = kh;

self->W = w * self->kx;
self->H = h * self->ky;
self->F = F;
self->w = w;
self->h = h;

self->kx = self->W / self->w;
self->ky = self->H / self->h;
return true;
}

Expand Down
Loading