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