diff --git a/ColorRegionMaskCreator.sln b/ColorRegionMaskCreator.sln
new file mode 100644
index 0000000..6a7e747
--- /dev/null
+++ b/ColorRegionMaskCreator.sln
@@ -0,0 +1,25 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio Version 16
+VisualStudioVersion = 16.0.32510.428
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ColorRegionMaskCreator", "ColorRegionMaskCreator\ColorRegionMaskCreator.csproj", "{49C17CC8-3602-45EE-B8B6-C328A58528D2}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Any CPU = Debug|Any CPU
+ Release|Any CPU = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {49C17CC8-3602-45EE-B8B6-C328A58528D2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {49C17CC8-3602-45EE-B8B6-C328A58528D2}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {49C17CC8-3602-45EE-B8B6-C328A58528D2}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {49C17CC8-3602-45EE-B8B6-C328A58528D2}.Release|Any CPU.Build.0 = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+ GlobalSection(ExtensibilityGlobals) = postSolution
+ SolutionGuid = {EC640D85-3A2D-4AE2-8219-A8F3688DDF5C}
+ EndGlobalSection
+EndGlobal
diff --git a/ColorRegionMaskCreator/App.config b/ColorRegionMaskCreator/App.config
new file mode 100644
index 0000000..193aecc
--- /dev/null
+++ b/ColorRegionMaskCreator/App.config
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/ColorRegionMaskCreator/ColorRegionMaskCreator.csproj b/ColorRegionMaskCreator/ColorRegionMaskCreator.csproj
new file mode 100644
index 0000000..97147eb
--- /dev/null
+++ b/ColorRegionMaskCreator/ColorRegionMaskCreator.csproj
@@ -0,0 +1,61 @@
+
+
+
+
+ Debug
+ AnyCPU
+ {49C17CC8-3602-45EE-B8B6-C328A58528D2}
+ Exe
+ ColorRegionMaskCreator
+ ColorRegionMaskCreator
+ v4.8
+ 512
+ true
+ true
+
+
+ AnyCPU
+ true
+ full
+ false
+ bin\Debug\
+ DEBUG;TRACE
+ prompt
+ 4
+ true
+
+
+ AnyCPU
+ pdbonly
+ true
+ bin\Release\
+ TRACE
+ prompt
+ 4
+ true
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ PreserveNewest
+
+
+
+
\ No newline at end of file
diff --git a/ColorRegionMaskCreator/Config.cs b/ColorRegionMaskCreator/Config.cs
new file mode 100644
index 0000000..ee01fdb
--- /dev/null
+++ b/ColorRegionMaskCreator/Config.cs
@@ -0,0 +1,79 @@
+using System.IO;
+using System.Text.RegularExpressions;
+
+namespace ColorRegionMaskCreator
+{
+ public class Config
+ {
+ public bool Load()
+ {
+ const string filePath = "config.ini";
+ if (string.IsNullOrEmpty(filePath) || !File.Exists(filePath))
+ return false;
+
+ var lines = File.ReadAllLines(filePath);
+ foreach (var l in lines)
+ {
+ var t = l.TrimStart();
+ if (string.IsNullOrEmpty(t) || t.StartsWith(";")) continue;
+
+ ReadSetting(t);
+ }
+
+ return true;
+ }
+
+ private void ReadSetting(string line)
+ {
+ var m = RegexSettingLine.Match(line);
+ if (!m.Success) return;
+
+ switch (m.Groups[1].Value.ToLowerInvariant())
+ {
+ case "maxwidth":
+ if (int.TryParse(m.Groups[2].Value, out var i))
+ MaxWidth = i;
+ break;
+ case "maxheight":
+ if (int.TryParse(m.Groups[2].Value, out i))
+ MaxHeight = i;
+ break;
+ case "colorr":
+ if (byte.TryParse(m.Groups[2].Value, out var b))
+ ColorR = b;
+ break;
+ case "colorg":
+ if (byte.TryParse(m.Groups[2].Value, out b))
+ ColorG = b;
+ break;
+ case "colorb":
+ if (byte.TryParse(m.Groups[2].Value, out b))
+ ColorB = b;
+ break;
+ case "greenscreenmingreen":
+ if (byte.TryParse(m.Groups[2].Value, out b))
+ GreenScreenMinGreen = b;
+ break;
+ case "greenscreenfactorglargerthanrb":
+ if (double.TryParse(m.Groups[2].Value, out var d))
+ GreenScreenFactorGLargerThanRB = d;
+ break;
+ case "greenscreengreenlargerthanrb":
+ if (byte.TryParse(m.Groups[2].Value, out b))
+ GreenScreenGreenLargerThanRB = b;
+ break;
+ }
+ }
+
+ public int MaxWidth = 256;
+ public int MaxHeight = 256;
+ public byte ColorR = 255;
+ public byte ColorG = 0;
+ public byte ColorB = 0;
+ public byte GreenScreenMinGreen = 50;
+ public byte GreenScreenGreenLargerThanRB = 50;
+ public double GreenScreenFactorGLargerThanRB = 2;
+
+ private static Regex RegexSettingLine = new Regex(@"(\w+) *= *(.*)");
+ }
+}
diff --git a/ColorRegionMaskCreator/ImageMasks.cs b/ColorRegionMaskCreator/ImageMasks.cs
new file mode 100644
index 0000000..70016ab
--- /dev/null
+++ b/ColorRegionMaskCreator/ImageMasks.cs
@@ -0,0 +1,448 @@
+using System;
+using System.Collections.Generic;
+using System.Drawing;
+using System.Drawing.Drawing2D;
+using System.Drawing.Imaging;
+using System.IO;
+using System.Linq;
+
+namespace ColorRegionMaskCreator
+{
+ internal static class ImageMasks
+ {
+ private const string InputFolder = "in";
+ private const string OutputFolder = "outMasks";
+ private const string OutputRegionsFolder = "outRegions";
+ private static string _baseDirectoryPath;
+ private static string _inputFolderPath;
+ public static string OutputFolderPath;
+ public static string OutputRegionsFolderPath;
+ private static Config _config;
+
+ internal static void CreateImageMasks(bool createRegionImages)
+ {
+ _baseDirectoryPath = Environment.CurrentDirectory;
+ _inputFolderPath = Path.Combine(_baseDirectoryPath, InputFolder);
+ OutputFolderPath = Path.Combine(_baseDirectoryPath, OutputFolder);
+ OutputRegionsFolderPath = Path.Combine(_baseDirectoryPath, OutputRegionsFolder);
+
+ _config = new Config();
+ _config.Load();
+
+ // get source files
+ var files = Directory.GetFiles(_inputFolderPath);
+
+ var imageFiles = new Dictionary();
+
+ // set defaultImages
+ foreach (var f in files)
+ {
+ if (f.EndsWith(".jpg") && !f.EndsWith("_m.jpg"))
+ imageFiles.Add(Path.Combine(_inputFolderPath, f), null);
+ }
+
+ // set maskFiles
+ foreach (var f in files)
+ {
+ if (f.EndsWith("_m.jpg"))
+ {
+ var maskFilePath = Path.Combine(_inputFolderPath, f);
+ var baseImageFilePath = maskFilePath.Replace("_m.jpg", ".jpg");
+ if (imageFiles.ContainsKey(baseImageFilePath))
+ imageFiles[baseImageFilePath] = maskFilePath;
+ }
+ }
+
+ InitializeLogisticFunction();
+
+ Directory.CreateDirectory(OutputFolderPath);
+ if (createRegionImages)
+ Directory.CreateDirectory(OutputRegionsFolderPath);
+
+ int count = imageFiles.Count;
+ int i = 0;
+
+ foreach (var fp in imageFiles)
+ {
+ CreateFiles(fp.Key, fp.Value, createRegionImages);
+ int progress = ++i * 100 / count;
+ Console.WriteLine($"{progress} % ({i}/{count}): {Path.GetFileNameWithoutExtension(fp.Key)}");
+ }
+ }
+
+ ///
+ /// Loads file and creates mask assuming there is a greenScreen.
+ ///
+ private static void CreateFiles(string baseImageFilePath, string colorMaskFile, bool createRegionImages)
+ {
+ using (Bitmap bmpBackgroundJpg = new Bitmap(baseImageFilePath))
+ using (Bitmap bmpBackground = new Bitmap(bmpBackgroundJpg.Width, bmpBackgroundJpg.Height, PixelFormat.Format32bppArgb))
+ using (Bitmap bmpColorMask = File.Exists(colorMaskFile) ? new Bitmap(colorMaskFile) : null)
+ {
+ BitmapData bmpDataJpg = bmpBackgroundJpg.LockBits(
+ new Rectangle(0, 0, bmpBackground.Width, bmpBackground.Height),
+ ImageLockMode.ReadWrite, bmpBackgroundJpg.PixelFormat);
+ BitmapData bmpDataBackground = bmpBackground.LockBits(new Rectangle(0, 0, bmpBackground.Width, bmpBackground.Height),
+ ImageLockMode.ReadWrite, bmpBackground.PixelFormat);
+ BitmapData bmpDataColorMask = bmpColorMask?.LockBits(new Rectangle(0, 0, bmpColorMask.Width, bmpColorMask.Height),
+ ImageLockMode.ReadWrite, bmpColorMask.PixelFormat);
+
+ // count of pixels by lightness. used for contrast improvement
+ var bgHistogram = new int[256];
+
+ unsafe
+ {
+ byte* scan0Jpg = (byte*)bmpDataJpg.Scan0.ToPointer();
+ byte* scan0Background = (byte*)bmpDataBackground.Scan0.ToPointer();
+ byte* scan0ColorMask = bmpDataColorMask != null ? (byte*)bmpDataColorMask.Scan0.ToPointer() : null;
+
+ var width = bmpDataJpg.Width;
+ var height = bmpDataJpg.Height;
+ for (int i = 0; i < width; i++)
+ {
+ for (int j = 0; j < height; j++)
+ {
+ byte* dJpg = scan0Jpg + j * bmpDataJpg.Stride + i * 3;
+ byte* dBg = scan0Background + j * bmpDataBackground.Stride + i * 4;
+ byte* dCm = scan0ColorMask != null ? (scan0ColorMask + j * bmpDataColorMask.Stride + i * 3) : null;
+
+ if (dJpg[1] >= _config.GreenScreenMinGreen && dJpg[2] * _config.GreenScreenFactorGLargerThanRB <= dJpg[1] && dJpg[0] * _config.GreenScreenFactorGLargerThanRB <= dJpg[1])
+ {
+ // is greenScreen, set color mask to black
+ if (dCm != null)
+ {
+ dCm[0] = 0;
+ dCm[1] = 0;
+ dCm[2] = 0;
+ }
+ }
+ else
+ {
+ dBg[0] = dJpg[0];
+ byte alpha = 255;
+ // remove green glow of green screen and set transparency
+ if (dJpg[1] > dJpg[0] + 20 && dJpg[1] > dJpg[0] * 1.2 && dJpg[1] > dJpg[2] * 1.2)
+ {
+ dBg[1] = dJpg[0];
+ var notGreenMean = (dJpg[0] + dJpg[2]) / 2;
+ alpha = (byte)(255 - dJpg[1] + notGreenMean);
+ }
+ else
+ {
+ dBg[1] = dJpg[1];
+ }
+ dBg[2] = dJpg[2];
+ dBg[3] = alpha;
+
+ bgHistogram[(dBg[0] + dBg[1] + dBg[2]) / 3]++; // assume lightness for the histogram adjustment is just the average of rgb
+
+ if (dCm == null) continue;
+
+ const int thresholdDifferentColor = 20;
+ // color mask should only be set if there is no base color
+ bool colorDifference = Math.Abs(dJpg[0] - dCm[0]) > thresholdDifferentColor
+ || Math.Abs(dJpg[1] - dCm[1]) > thresholdDifferentColor
+ || Math.Abs(dJpg[2] - dCm[2]) > thresholdDifferentColor;
+
+ //if (colorDifferenceB == 0 && colorDifferenceG == 0 && colorDifferenceR == 0)
+ if (!colorDifference)
+ {
+ dCm[0] = 0;
+ dCm[1] = 0;
+ dCm[2] = 0;
+ }
+ else
+ {
+ // get the dominant colors
+ byte max = Math.Max(Math.Max(dCm[0], dCm[1]), dCm[2]);
+
+ if (max < 20)
+ {
+ // set to black
+ dCm[0] = 0;
+ dCm[1] = 0;
+ dCm[2] = 0;
+ }
+ else
+ {
+ // the dominant color should have a value of 255
+ double factor = 255d / max;
+
+ // there are at most 2 channels active simultaneously
+ byte min = Math.Min(Math.Min(dCm[0], dCm[1]), dCm[2]);
+ byte threshold = (byte)(min * 1.2);
+
+ dCm[0] = (byte)(dCm[0] > threshold ? factor * dCm[0] : 0);
+ dCm[1] = (byte)(dCm[1] > threshold ? factor * dCm[1] : 0);
+ dCm[2] = (byte)(dCm[2] > threshold ? factor * dCm[2] : 0);
+ }
+ }
+ }
+ }
+ }
+
+ // adjust contrast of the background image to improve the displayed colors
+ var transformFunction = CreateTransformFunction(bgHistogram);
+
+ width = bmpDataBackground.Width;
+ height = bmpDataBackground.Height;
+ for (int i = 0; i < width; i++)
+ {
+ for (int j = 0; j < height; j++)
+ {
+ byte* dBg = scan0Background + j * bmpDataBackground.Stride + i * 4;
+ if (dBg[3] == 0) continue; // transparent
+
+ // adjust lightness of pixel
+ var lightness = (dBg[0] + dBg[1] + dBg[2]) / 3;
+ if (lightness == 0 || lightness == 255) continue;
+
+ var lightnessFactor = (double)transformFunction[lightness] / lightness;
+ dBg[0] = ApplyLightnessCorrection(dBg[0], lightnessFactor);
+ dBg[1] = ApplyLightnessCorrection(dBg[1], lightnessFactor);
+ dBg[2] = ApplyLightnessCorrection(dBg[2], lightnessFactor);
+
+ byte ApplyLightnessCorrection(byte val, double factor)
+ {
+ var result = val * factor;
+ if (result > 255) return 255;
+ if (result < 0) return 0;
+ return (byte)result;
+ }
+ }
+ }
+ }
+
+ bmpBackgroundJpg.UnlockBits(bmpDataJpg);
+ bmpBackground.UnlockBits(bmpDataBackground);
+ bmpColorMask?.UnlockBits(bmpDataColorMask);
+
+ var filePathBaseImage = Path.Combine(OutputFolderPath, Path.GetFileNameWithoutExtension(baseImageFilePath) + ".png");
+ var filePathMaskImage = Path.Combine(OutputFolderPath, Path.GetFileNameWithoutExtension(colorMaskFile) + ".png");
+
+ var resizeFactor = 1d;
+ if (_config.MaxWidth > 0 && _config.MaxWidth < bmpBackground.Width)
+ {
+ resizeFactor = (double)_config.MaxWidth / bmpBackground.Width;
+ }
+
+ if (_config.MaxHeight > 0 && _config.MaxHeight < bmpBackground.Height)
+ {
+ var resizeFactorH = (double)_config.MaxHeight / bmpBackground.Height;
+ if (resizeFactorH < resizeFactor)
+ resizeFactor = resizeFactorH;
+ }
+
+ var imageWidth = (int)(bmpBackground.Width * resizeFactor);
+ var imageHeight = (int)(bmpBackground.Height * resizeFactor);
+
+ SaveResizedBitmap(bmpBackground, filePathBaseImage);
+ SaveResizedBitmap(bmpColorMask, filePathMaskImage);
+
+ void SaveResizedBitmap(Bitmap bmp, string filePath)
+ {
+ if (bmp == null) return;
+
+ using (var bmpResized = new Bitmap(imageWidth, imageHeight, bmp.PixelFormat))
+ using (var g = Graphics.FromImage(bmpResized))
+ {
+ g.CompositingMode = CompositingMode.SourceCopy;
+ g.CompositingQuality = CompositingQuality.HighQuality;
+ g.InterpolationMode = InterpolationMode.HighQualityBicubic;
+ g.SmoothingMode = SmoothingMode.HighQuality;
+ g.PixelOffsetMode = PixelOffsetMode.HighQuality;
+ g.DrawImage(bmp, 0, 0, imageWidth, imageWidth);
+ bmpResized.Save(filePath, ImageFormat.Png);
+ }
+ }
+
+ if (createRegionImages && bmpColorMask != null)
+ {
+ const int colorRegionCount = 6;
+
+ var colors = new byte[colorRegionCount][];
+ var highlightedColor = new byte[] { _config.ColorR, _config.ColorG, _config.ColorB };
+ for (int i = 0; i < colorRegionCount; i++)
+ {
+ colors[i] = highlightedColor;
+ }
+ var enabledColorRegions = new bool[colorRegionCount];
+
+ for (int i = 0; i < colorRegionCount; i++)
+ {
+ for (int j = 0; j < colorRegionCount; j++)
+ {
+ enabledColorRegions[j] = i == j;
+ }
+
+ using (var regionImage = new Bitmap(filePathBaseImage))
+ {
+ if (CreateColorRegionImages(colors, enabledColorRegions, filePathMaskImage, regionImage))
+ SaveResizedBitmap(regionImage,
+ Path.Combine(OutputRegionsFolderPath, Path.GetFileNameWithoutExtension(baseImageFilePath) + "_PaintRegion_" + i + ".png"));
+ }
+ }
+ }
+ }
+ }
+
+ ///
+ /// Creates a transformation for lightness values to create a gaussian histogram.
+ ///
+ ///
+ ///
+ private static int[] CreateTransformFunction(int[] bgHistogram)
+ {
+ var transformation = new int[256];
+ int count = bgHistogram.Sum();
+ int integral = 0;
+ for (int i = 0; i < 256; i++)
+ {
+ // equalize histogram, then make histogram gaussian to increase amount of middle values (these give better colors with the mask)
+ transformation[i] = _invLogisticFunction[255 * integral / count];
+ //transformation[i] = 255 * integral / count;
+ integral += bgHistogram[i];
+ }
+
+ return transformation;
+ }
+
+ private static int[] _invLogisticFunction;
+ ///
+ /// Used to create a good lightness distribution of the final image, so the applied color masks look good.
+ ///
+ private static void InitializeLogisticFunction()
+ {
+ _invLogisticFunction = new int[256];
+ const int x0 = 127;
+ const int k = 50;
+ const int stretch = 22;
+ const double l = 255 + stretch * 2;
+
+ for (int i = 1; i < 255; i++)
+ {
+ // logistic function: (l / (1 + Math.Exp(-k * (i - x0))))
+ var r = (int)(-k * Math.Log(l / (i + stretch) - 1) + x0);
+ if (r < 0) r = 0;
+ else if (r > 255) r = 255;
+ _invLogisticFunction[i] = r;
+ }
+
+ _invLogisticFunction[255] = 255;
+ }
+
+ ///
+ /// Applies the colors to the base image.
+ ///
+ private static bool CreateColorRegionImages(byte[][] rgb, bool[] enabledColorRegions, string speciesColorMaskFilePath, Bitmap bmpBaseImage)
+ {
+ var imageFine = false;
+ using (Bitmap bmpMask = new Bitmap(speciesColorMaskFilePath))
+ {
+ BitmapData bmpDataBaseImage = bmpBaseImage.LockBits(
+ new Rectangle(0, 0, bmpBaseImage.Width, bmpBaseImage.Height), ImageLockMode.ReadOnly,
+ bmpBaseImage.PixelFormat);
+ BitmapData bmpDataMask = bmpMask.LockBits(
+ new Rectangle(0, 0, bmpMask.Width, bmpMask.Height), ImageLockMode.ReadOnly,
+ bmpMask.PixelFormat);
+
+ int bgBytes = bmpBaseImage.PixelFormat == PixelFormat.Format32bppArgb ? 4 : 3;
+ int msBytes = bmpDataMask.PixelFormat == PixelFormat.Format32bppArgb ? 4 : 3;
+
+ float o = 0;
+ try
+ {
+ unsafe
+ {
+ byte* scan0Bg = (byte*)bmpDataBaseImage.Scan0.ToPointer();
+ byte* scan0Ms = (byte*)bmpDataMask.Scan0.ToPointer();
+
+ var width = bmpDataBaseImage.Width;
+ var height = bmpDataBaseImage.Height;
+ var strideBaseImage = bmpDataBaseImage.Stride;
+ var strideMask = bmpDataMask.Stride;
+
+ for (int i = 0; i < width; i++)
+ {
+ for (int j = 0; j < height; j++)
+ {
+ byte* dBg = scan0Bg + j * strideBaseImage + i * bgBytes;
+ // continue if the pixel is transparent
+ if (dBg[3] == 0)
+ continue;
+
+ byte* dMs = scan0Ms + j * strideMask + i * msBytes;
+
+ int r = dMs[2];
+ int g = dMs[1];
+ int b = dMs[0];
+ byte finalR = dBg[2];
+ byte finalG = dBg[1];
+ byte finalB = dBg[0];
+
+ for (int m = 0; m < 6; m++)
+ {
+ if (!enabledColorRegions[m])
+ continue;
+ switch (m)
+ {
+ case 0:
+ o = Math.Max(0, r - g - b) / 255f;
+ break;
+ case 1:
+ o = Math.Max(0, g - r - b) / 255f;
+ break;
+ case 2:
+ o = Math.Max(0, b - r - g) / 255f;
+ break;
+ case 3:
+ o = Math.Min(g, b) / 255f;
+ break;
+ case 4:
+ o = Math.Min(r, g) / 255f;
+ break;
+ case 5:
+ o = Math.Min(r, b) / 255f;
+ break;
+ }
+
+ if (o == 0)
+ continue;
+ // using "grain merge", e.g. see https://docs.gimp.org/en/gimp-concepts-layer-modes.html
+ int rMix = finalR + rgb[m][0] - 128;
+ if (rMix < 0) rMix = 0;
+ else if (rMix > 255) rMix = 255;
+ int gMix = finalG + rgb[m][1] - 128;
+ if (gMix < 0) gMix = 0;
+ else if (gMix > 255) gMix = 255;
+ int bMix = finalB + rgb[m][2] - 128;
+ if (bMix < 0) bMix = 0;
+ else if (bMix > 255) bMix = 255;
+
+ finalR = (byte)(o * rMix + (1 - o) * finalR);
+ finalG = (byte)(o * gMix + (1 - o) * finalG);
+ finalB = (byte)(o * bMix + (1 - o) * finalB);
+ }
+
+ // set final color
+ dBg[0] = finalB;
+ dBg[1] = finalG;
+ dBg[2] = finalR;
+ }
+ }
+ imageFine = true;
+ }
+ }
+ catch
+ {
+ // error during drawing, maybe mask is smaller than image
+ }
+
+ bmpBaseImage.UnlockBits(bmpDataBaseImage);
+ bmpMask.UnlockBits(bmpDataMask);
+ }
+
+ return imageFine;
+ }
+ }
+}
diff --git a/ColorRegionMaskCreator/Program.cs b/ColorRegionMaskCreator/Program.cs
new file mode 100644
index 0000000..0127fe2
--- /dev/null
+++ b/ColorRegionMaskCreator/Program.cs
@@ -0,0 +1,33 @@
+using System;
+using System.Diagnostics;
+using System.IO;
+
+namespace ColorRegionMaskCreator
+{
+ class Program
+ {
+ static void Main(string[] args)
+ {
+ Console.WriteLine("Creates base images and according mask images from jpg files in the in folder.");
+ Console.WriteLine("The base image can have a greenscreen which will be made transparent in the output.");
+ Console.WriteLine("The mask file is expected to have regions that are r, g, b, gb, rg, rb; the contrast will be maximized.");
+ Console.WriteLine("The according mask file needs to have the same filename with _m appended to the name before the extension, e.g. in/myImage.jpg and in/myImage_m.jpg");
+ Console.WriteLine();
+ Console.WriteLine("Also create color region images? (y/n)");
+ var answer = Console.ReadLine();
+ var createColorRegionImages = answer?.Trim().ToLowerInvariant() == "y";
+
+ ImageMasks.CreateImageMasks(createColorRegionImages);
+
+ Console.WriteLine("Open output folder? (y/n)");
+ if (Console.ReadLine()?.Trim().ToLowerInvariant() == "y")
+ {
+ if (Directory.Exists(ImageMasks.OutputFolderPath))
+ Process.Start("explorer.exe", $"\"{ImageMasks.OutputFolderPath}\"");
+
+ if (createColorRegionImages && Directory.Exists(ImageMasks.OutputFolderPath))
+ Process.Start("explorer.exe", $"\"{ImageMasks.OutputRegionsFolderPath}\"");
+ }
+ }
+ }
+}
diff --git a/ColorRegionMaskCreator/Properties/AssemblyInfo.cs b/ColorRegionMaskCreator/Properties/AssemblyInfo.cs
new file mode 100644
index 0000000..dbbd559
--- /dev/null
+++ b/ColorRegionMaskCreator/Properties/AssemblyInfo.cs
@@ -0,0 +1,36 @@
+using System.Reflection;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+// General Information about an assembly is controlled through the following
+// set of attributes. Change these attribute values to modify the information
+// associated with an assembly.
+[assembly: AssemblyTitle("ColorRegionMaskCreator")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("")]
+[assembly: AssemblyProduct("ColorRegionMaskCreator")]
+[assembly: AssemblyCopyright("Copyright © 2022 by cadon")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+
+// Setting ComVisible to false makes the types in this assembly not visible
+// to COM components. If you need to access a type in this assembly from
+// COM, set the ComVisible attribute to true on that type.
+[assembly: ComVisible(false)]
+
+// The following GUID is for the ID of the typelib if this project is exposed to COM
+[assembly: Guid("49c17cc8-3602-45ee-b8b6-c328a58528d2")]
+
+// Version information for an assembly consists of the following four values:
+//
+// Major Version
+// Minor Version
+// Build Number
+// Revision
+//
+// You can specify all the values or you can default the Build and Revision Numbers
+// by using the '*' as shown below:
+// [assembly: AssemblyVersion("1.0.*")]
+[assembly: AssemblyVersion("1.0.0.0")]
+[assembly: AssemblyFileVersion("1.0.0.0")]
diff --git a/ColorRegionMaskCreator/config.ini b/ColorRegionMaskCreator/config.ini
new file mode 100644
index 0000000..5d7901b
--- /dev/null
+++ b/ColorRegionMaskCreator/config.ini
@@ -0,0 +1,16 @@
+; max size of the output images. Smaller images are not enlarged
+maxWidth = 600
+maxHeight = 800
+
+; Color of the region highlighting
+colorR = 240
+colorG = 50
+colorB = 10
+
+;;; GreenScreen detection
+; if there are green edges on the object from the greenscreen (spilling), decreasing the following values may help to reduce that. Too low values will remove too much from the object.
+; GreenScreenMinGreen is the min value of the green channel that a pixel can be set as greenscreen (if also the GreenScreenFactorGLargerThanRB condition is fulfilled). Default 50.
+GreenScreenMinGreen = 50
+; GreenScreenFactorGLargerThanRB is the factor the green channel needs to be larger than the red and the blue chennel that a pixel can be set as greenscreen (if also the GreenScreenMinGreen condition is fulfilled)
+; E.g. with a a value of 2 a pixel with the rgb color (50,100,50) is considered a greenscreen, but the color (51,100,0) is not. Default 2.0.
+GreenScreenFactorGLargerThanRB = 2.0
diff --git a/README.md b/README.md
index 634f595..8b88d0f 100644
--- a/README.md
+++ b/README.md
@@ -1,2 +1,7 @@
# ColorRegionMaskCreator
Creates image masks based on a base image and a mask file where up to 6 masks are defined by r, g, b, gb, rg, rb.
+
+The source files needs to be in the subfolder `in` and in the jpg format.
+The base image can have a greenscreen which will be made transparent in the output.
+The mask file is expected to have regions that are r, g, b, gb, rg, rb; the contrast will be maximized.
+The according mask file needs to have the same filename with _m appended to the name before the extension, e.g. in/myImage.jpg and in/myImage_m.jpg.
\ No newline at end of file