-
-
Notifications
You must be signed in to change notification settings - Fork 1
/
OpcodesImplementation.cs
155 lines (153 loc) · 6.95 KB
/
OpcodesImplementation.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
using System;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using System.Windows.Media;
using System.Windows.Media.Imaging;
namespace DngOpcodesEditor;
public static class OpcodesImplementation
{
// Multiplies a specified area and plane range of an image by a gain map
public static void GainMap(Image img, OpcodeGainMap p)
{
var sw = Stopwatch.StartNew();
// Split p.mapGains by p.planes and transform to a 2D array
// Ex. float[3072] - > 3x float[32,32]
var mapGainsPlanes = new float[p.planes][,];
for (int planeIndex = 0; planeIndex < p.planes; planeIndex++)
{
var planeChannel = p.mapGains.Where((f, i) => i % p.planes == planeIndex).ToArray();
//Image.SaveFloatImage(planeChannel, (int)p.mapPointsH, (int)p.mapPointsV, $"gainMap{planeIndex}.tiff");
mapGainsPlanes[planeIndex] = MathHelper.ArrayToArray2D(planeChannel, (int)p.mapPointsH);
}
Parallel.For(0, img.Height, (y) =>
{
for (int x = 0; x < img.Width; x++)
{
// Inside the gain map values are interpolated using the bilinear interpolation
if ((x >= p.left) && (x <= p.right) && (y >= p.top) || (y <= p.bottom))
{
// Convert x,y into [0,1] image range
double xRel = x / (img.Width - 1.0);
double yRel = y / (img.Height - 1.0);
// convert from image [0,1]x[0,1] to array [0,mapPointsH]x[0,mapPointsV]
double xMap = Math.Min((xRel - p.mapOriginH) / p.mapSpacingH, p.mapPointsH - 1.0);
double yMap = Math.Min((yRel - p.mapOriginV) / p.mapSpacingV, p.mapPointsV - 1.0);
var pixel = img.GetRgb16Pixel(x, y);
// apply the gain from plane p.plane
for (uint planeIndex = p.plane; planeIndex < 3; planeIndex++)
{
// use the last gain map if p.planes > p.mapPlanes
var gain = MathHelper.BilinearInterpolation(mapGainsPlanes[Math.Min(planeIndex, mapGainsPlanes.Length - 1)], xMap, yMap);
pixel[planeIndex] = (UInt16)Math.Clamp(Math.Round(pixel[planeIndex] * gain), 0, 65535);
}
img.SetRgb16Pixel(x, y, pixel[0], pixel[1], pixel[2]);
}
else
{
// Outside the gain map, values are replicated from the edge of the map
// TODO
}
}
});
img.Update();
Debug.WriteLine($"\tGainMap executed in {sw.ElapsedMilliseconds}ms");
}
// Trims the image to the rectangle specified by Top, Left, Bottom, and Right
public static void TrimBounds(Image img, OpcodeTrimBounds p)
{
// In this implementation we keep the original size and we only mask trimmed pixels
Parallel.For(0, img.Height, (y) =>
{
for (int x = 0; x < img.Width; x++)
{
if ((x <= p.left) || (x >= p.right) || (y <= p.top) || (y >= p.bottom))
{
unchecked { img.SetPixel(x, y, 0); }
}
}
});
}
// Applies a gain function to an image and can be used to correct vignetting
public static void FixVignetteRadial(Image img, OpcodeFixVignetteRadial p)
{
var sw = Stopwatch.StartNew();
double k0 = p.k0;
double k1 = p.k1;
double k2 = p.k2;
double k3 = p.k3;
double k4 = p.k4;
double ncx = p.cx;
double ncy = p.cy;
int x0 = 0; int y0 = 0;
int x1 = img.Width - 1; int y1 = img.Height - 1;
double cx = x0 + ncx * (x1 - x0);
double cy = y0 + ncy * (y1 - y0);
double mx = Math.Max(Math.Abs(x0 - cx), Math.Abs(x1 - cx));
double my = Math.Max(Math.Abs(y0 - cy), Math.Abs(y1 - cy));
double m = Math.Sqrt(mx * mx + my * my);
Parallel.For(0, img.Height, (y) =>
{
for (int x = 0; x < img.Width; x++)
{
double r = Math.Sqrt(Math.Pow(x - cx, 2) + Math.Pow(y - cy, 2)) / m;
double g = 1.0 + k0 * Math.Pow(r, 2) + k1 * Math.Pow(r, 4) + k2 * Math.Pow(r, 6) + k3 * Math.Pow(r, 8) + k4 * Math.Pow(r, 10);
img.ChangeRgb16Pixel(x, y, pixel => (float)(pixel * g));
}
});
Debug.WriteLine($"\tFixVignetteRadial executed in {sw.ElapsedMilliseconds}ms");
}
// Applies a warp to an image and can be used to correct geometric distortion and
// lateral (transverse) chromatic aberration for rectilinear lenses.
// The warp function supports both radial and tangential distortion correction.
public static void WarpRectilinear(Image img, OpcodeWarpRectilinear p)
{
// TODO: resampling kernel (ex. cubic spline)
var sw = Stopwatch.StartNew();
if (p.planes != 1)
{
Debug.WriteLine("Multiple planes support not implemented yet");
return;
}
double r0 = p.coefficients[0];
double r1 = p.coefficients[1];
double r2 = p.coefficients[2];
double r3 = p.coefficients[3];
double t0 = p.coefficients[4];
double t1 = p.coefficients[5];
double ncx = p.cx;
double ncy = p.cy;
int x0 = 0; int y0 = 0;
int x1 = img.Width - 1; int y1 = img.Height - 1;
double cx = x0 + ncx * (x1 - x0);
double cy = y0 + ncy * (y1 - y0);
double mx = Math.Max(Math.Abs(x0 - cx), Math.Abs(x1 - cx));
double my = Math.Max(Math.Abs(y0 - cy), Math.Abs(y1 - cy));
double m = Math.Sqrt(mx * mx + my * my);
// Create a new black image to copy pixels in new positions
var newImg = new UInt64[img.Width * img.Height];
Parallel.For(0, img.Height, (y) =>
{
for (int x = 0; x < img.Width; x++)
{
double deltaX = (x - cx) / m;
double deltaY = (y - cy) / m;
double r = Math.Sqrt(deltaX * deltaX + deltaY * deltaY);
double f = r0 + r1 * Math.Pow(r, 2) + r2 * Math.Pow(r, 4) + r3 * Math.Pow(r, 6);
double deltaXr = f * deltaX;
double deltaYr = f * deltaY;
double deltaXt = t0 * (2.0 * deltaX * deltaY) + t1 * (r * r + 2.0 * deltaX * deltaX);
double deltaYt = t1 * (2.0 * deltaX * deltaY) + t0 * (r * r + 2.0 * deltaY * deltaY);
int xSrc = (int)Math.Round(cx + m * (deltaXr + deltaXt));
int ySrc = (int)Math.Round(cy + m * (deltaYr + deltaYt));
if ((xSrc >= 0) && (ySrc >= 0) && (xSrc < img.Width) && (ySrc < img.Height))
{
newImg[x + y * img.Width] = img[xSrc, ySrc];
}
}
});
img.SetPixels(newImg);
Debug.WriteLine($"\tWarpRectilinear executed in {sw.ElapsedMilliseconds}ms");
}
}