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
67 changes: 67 additions & 0 deletions PhotoLocator/BitmapOperations/AstroStretchOperation.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
using System;

namespace PhotoLocator.BitmapOperations
{
class AstroStretchOperation : OperationBase
{
public double Stretch { get; set; } = 10;

public double BackgroundSmooth { get; set; } = 8;

public double BlackPoint { get; set; }

public override void Apply()
{
if (SrcBitmap is not null && DstBitmap != SrcBitmap)
DstBitmap.Assign(SrcBitmap);
if (Stretch > 0)
{
var s = (float)Math.Exp(-Stretch);
DstBitmap.ProcessElementWise(p => Math.Max(0, (s - 1) * p / ((2 * s - 1) * p - s)));
}
if (BackgroundSmooth > 0)
{
var background = ConvertToGrayscaleOperation.ConvertToGrayscale(DstBitmap);
IIRSmoothOperation.Apply(background, (float)Math.Exp(BackgroundSmooth));
if (BlackPoint > 0)
{
var bp = (float)BlackPoint;
DstBitmap.ProcessElementWise(background, (p, b) => Math.Max(p - b - bp, 0));
}
else
DstBitmap.ProcessElementWise(background, (p, b) => Math.Max(p - b, 0));
}
else if (BlackPoint > 0)
{
var bp = (float)BlackPoint;
DstBitmap.ProcessElementWise(p => Math.Max(p - bp, 0));
}
}

public static double OptimizeStretch(FloatBitmap srcBitmap)
{
const double TargetMean = 0.1;
const int SampleHeight = 100;

var grayImage = ConvertToGrayscaleOperation.ConvertToGrayscale(srcBitmap);
srcBitmap = new FloatBitmap(Math.Max(1, srcBitmap.Width * SampleHeight / srcBitmap.Height), SampleHeight, 1);
BilinearResizeOperation.ApplyToPlaneParallel(grayImage, srcBitmap);
Comment thread
meesoft marked this conversation as resolved.

double bestStretch = 1;
double bestMean = 0;
var op = new AstroStretchOperation { SrcBitmap = srcBitmap, DstBitmap = new(), BackgroundSmooth = 0 };
for (double s = 1; s <= 20; s += 0.2)
{
op.Stretch = s;
op.Apply();
var mean = op.DstBitmap.Mean();
if (Math.Abs(mean - TargetMean) < Math.Abs(bestMean - TargetMean))
{
bestStretch = s;
bestMean = mean;
}
}
return bestStretch;
}
}
}
3 changes: 2 additions & 1 deletion PhotoLocator/BitmapOperations/FloatBitmap.cs
Original file line number Diff line number Diff line change
Expand Up @@ -498,7 +498,8 @@ public void ProcessElementWise(FloatBitmap other, Func<float, float, float> oper
fixed (float* otherElements = &other.Elements[y, 0])
{
int xx = 0;
for (var x = 0; x < Width; x++)
var width = Width;
for (var x = 0; x < width; x++)
{
var otherElement = otherElements[x];
elements[xx] = operation(elements[xx++], otherElement);
Expand Down
4 changes: 2 additions & 2 deletions PhotoLocator/Helpers/CelestialCalculator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -33,10 +33,10 @@ public static (DateTime? Sunrise, double? RiseAzimuth, DateTime? Sunset, double?
return (sunrise, sunriseAzimuth, sunset, sunsetAzimuth);
}

public static double? GetSunPosition(Location location, DateTime time)
public static (double Azimuth, double Altitude) GetSunPosition(Location location, DateTime time)
{
var pos = SunCalc.GetSunPosition(time, location.Latitude, location.Longitude);
return pos.Altitude < 0 ? null : SunCalcNetAzimuthRadiansToDegrees(pos.Azimuth);
return (SunCalcNetAzimuthRadiansToDegrees(pos.Azimuth), pos.Altitude * 180 / Math.PI);
}

/// <summary>
Expand Down
6 changes: 4 additions & 2 deletions PhotoLocator/JpegTransformCommands.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ namespace PhotoLocator
{
public sealed class JpegTransformCommands
{
public const string AstroCommandParameter = "Astro";

private readonly IMainViewModel _mainViewModel;

private bool HasFileSelected(object? o) => _mainViewModel.SelectedItem is not null && _mainViewModel.SelectedItem.IsFile;
Expand Down Expand Up @@ -103,7 +105,7 @@ await Task.Run(() =>
using (var cursor = new MouseCursorOverride())
{
(var image, metadata) = await Task.Run(() => LoadImageWithMetadataAsync(selectedItem));
localContrastViewModel = new LocalContrastViewModel() { SourceBitmap = image };
localContrastViewModel = new LocalContrastViewModel() { IsAstroModeEnabled = o as string == AstroCommandParameter, SourceBitmap = image };
}
var window = new LocalContrastView();
window.Owner = Application.Current.MainWindow;
Expand Down Expand Up @@ -131,7 +133,7 @@ await Task.Run(() =>
private static async Task<(BitmapSource, BitmapMetadata?)> LoadImageWithMetadataAsync(PictureItemViewModel item)
{
BitmapMetadata? metadata = null;
var image = await item.LoadPreviewAsync(default, int.MaxValue, preservePixelFormat: true);
var image = await item.LoadPreviewAsync(default, preservePixelFormat: true);
try
{
using var file = File.OpenRead(item.FullPath);
Expand Down
42 changes: 38 additions & 4 deletions PhotoLocator/LocalContrastView.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,13 @@
d:DataContext="{d:DesignInstance Type=local:LocalContrastViewModel, IsDesignTimeCreatable=True}"
Background="Black"
WindowStartupLocation="CenterOwner" ShowInTaskbar="False"
Title="Local contrast, brightness and colors" Height="960" Width="800" WindowState="Maximized">
Title="Local contrast, brightness and colors" Height="1000" Width="800" WindowState="Maximized">

<Window.Resources>
<Style TargetType="TextBlock">
<Setter Property="Foreground" Value="#FFEEEEEE" />
</Style>
<BooleanToVisibilityConverter x:Key="BooleanToVisibility" />
</Window.Resources>

<Window.InputBindings>
Expand Down Expand Up @@ -44,7 +45,7 @@

<Grid Margin="4">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="200"/>
<ColumnDefinition Width="210"/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>

Expand All @@ -54,8 +55,41 @@
</ScrollViewer.Resources>

<StackPanel Margin="0,0,4,0">
<TextBlock Text="Local adjustments" FontWeight="Bold" Margin="0,0,0,4"/>

<TextBlock Text="Local adjustments" FontWeight="Bold" Margin="0,0,0,4"
Visibility="{Binding IsLocalAdjustmentsLabelVisible, Converter={StaticResource BooleanToVisibility}}"/>

<StackPanel Visibility="{Binding IsAstroModeEnabled, Converter={StaticResource BooleanToVisibility}}">
<DockPanel>
<TextBlock Text="Astro stretch">
<TextBlock.InputBindings>
<MouseBinding Command="{Binding ResetAstroStretchCommand}" MouseAction="LeftClick" />
</TextBlock.InputBindings>
</TextBlock>
<TextBox Text="{Binding AstroStretch,StringFormat=N2,UpdateSourceTrigger=PropertyChanged,Delay=500}" Width="80" HorizontalAlignment="Right" />
</DockPanel>
<Slider Value="{Binding AstroStretch}" Minimum="0" Maximum="20" SmallChange="0.01" LargeChange="0.1" />

<DockPanel>
<TextBlock Text="Background filter">
<TextBlock.InputBindings>
<MouseBinding Command="{Binding ResetBackgroundRemovalSmoothCommand}" MouseAction="LeftClick" />
</TextBlock.InputBindings>
</TextBlock>
<TextBox Text="{Binding BackgroundRemovalSmooth,StringFormat=N2,UpdateSourceTrigger=PropertyChanged,Delay=500}" Width="80" HorizontalAlignment="Right" />
</DockPanel>
<Slider Value="{Binding BackgroundRemovalSmooth}" Minimum="0" Maximum="16" SmallChange="0.05" LargeChange="0.1" />

<DockPanel>
<TextBlock Text="Black point">
<TextBlock.InputBindings>
<MouseBinding Command="{Binding ResetBlackPointCommand}" MouseAction="LeftClick" />
</TextBlock.InputBindings>
</TextBlock>
<TextBox Text="{Binding BlackPoint,StringFormat=N4,UpdateSourceTrigger=PropertyChanged,Delay=500}" Width="80" HorizontalAlignment="Right" />
</DockPanel>
<Slider Value="{Binding BlackPoint}" Minimum="0" Maximum="0.1" SmallChange="0.0001" LargeChange="0.001" />
</StackPanel>

<DockPanel>
<TextBlock Text="Local brightness">
<TextBlock.InputBindings>
Expand Down
Loading
Loading