private void worker_DoWork(object sender, DoWorkEventArgs e) { //_totalFiles = filesInSourceFolder.Length; List <string[]> completeGroups = new List <string[]>(); int frameDelay = 0; //byte[] RGBPositions = new byte[1]; ShotSetting[] shotSettings = new ShotSetting[3]; bool IsTIFF = false; bool IsEXR = false; int maxThreads = Environment.ProcessorCount; string customOutputName = ""; int leadingZeros = 0; bool overwriteExisting = false; float HDRClippingPoint = 0.99f; float HDRFeatherMultiplier = 1; bool EXRIntegrityVerification = true; this.Dispatcher.Invoke(() => { frameDelay = 0; int.TryParse(delay.Text, out frameDelay); int.TryParse(outputNameLeadingZeros_txt.Text, out leadingZeros); int.TryParse(maxThreads_txt.Text, out maxThreads); //RGBPositions = getRGBPositions(); shotSettings = getShots(); IsTIFF = (bool)formatTif.IsChecked; IsEXR = (bool)formatExr.IsChecked; customOutputName = outputNameBase_txt.Text; overwriteExisting = !(bool)overwrite_no.IsChecked && (bool)overwrite_yes.IsChecked; HDRClippingPoint = getHDRClippingpoint(); HDRFeatherMultiplier = getFeatherMultiplier(); EXRIntegrityVerification = (bool)exrIntegrityVerification_check.IsChecked; }); int groupLength = shotSettings.Length; _totalFiles = (int)Math.Floor(filesInSourceFolder.Length / (decimal)groupLength); if (maxThreads == 0) { maxThreads = Environment.ProcessorCount; } FORMAT inputFormat = getInputFormat(); TARGETFORMAT targetFormat = TARGETFORMAT.EXR; if (IsEXR) { targetFormat = TARGETFORMAT.EXR; } else { targetFormat = TARGETFORMAT.TIF; } for (int baseIndex = frameDelay; baseIndex < filesInSourceFolder.Length; baseIndex += groupLength) { if ((baseIndex + (groupLength - 1)) > (filesInSourceFolder.Length - 1)) { MessageBox.Show("Group incomplete. Skipping."); continue; } int[] fileIndizi = new int[groupLength]; string[] files = new string[groupLength]; for (var i = 0; i < groupLength; i++) { fileIndizi[i] = baseIndex + i; files[i] = filesInSourceFolder[baseIndex + i]; } //int[] RGBIndizi = new int[groupLength] { baseIndex + RGBPositions[0], baseIndex + RGBPositions[1], baseIndex + RGBPositions[2] }; //string[] RGBFiles = new string[groupLength] { filesInSourceFolder[RGBIndizi[0]], filesInSourceFolder[RGBIndizi[1]], filesInSourceFolder[RGBIndizi[2]] }; completeGroups.Add(files); } int processFrom = 1; int processTo = completeGroups.Count(); this.Dispatcher.Invoke(() => { int.TryParse(processFrom_txt.Text, out processFrom); int.TryParse(processTo_txt.Text, out processTo); }); // Index starting at 0, but GUI starts at 1 processFrom--; processTo--; var countLock = new object(); CurrentProgress = 0; _counterTotal = 0; _counterSkippedRange = 0; _counterDone = 0; _counterSkippedExisting = 0; if (EXRIntegrityVerification) { IntegrityChecker.BuildIntegrityVerificationAcceptableLossCache(); } integrityCheckFailCount = 0; Parallel.ForEach(completeGroups, new ParallelOptions { MaxDegreeOfParallelism = maxThreads }, (currentGroup, loopState, index) => // foreach (string srcFileName in filesInSourceFolder) { _counterTotal++; var percentage = (double)_counterTotal / _totalFiles * 100.0; if (worker.CancellationPending == true) { e.Cancel = true; return; } if (index < processFrom || index > processTo) { // Skip this one. _counterSkippedRange++; lock (countLock) { worker?.ReportProgress((int)percentage); } return; } string fileNameWithoutFolder = customOutputName == "" ? Path.GetFileNameWithoutExtension(currentGroup[0]) : customOutputName + (leadingZeros == 0 ? index.ToString() : index.ToString("D" + leadingZeros.ToString())); string fileNameWithoutExtension = targetFolder + "\\" + fileNameWithoutFolder; string fileName = fileNameWithoutExtension + (targetFormat == TARGETFORMAT.EXR ? ".exr" : "") + (targetFormat == TARGETFORMAT.TIF ? ".tif" : ""); if (File.Exists(fileName) && !overwriteExisting) { // Error: File already exists. No overwriting. Move on. //continue; _counterSkippedExisting++; lock (countLock) { worker?.ReportProgress((int)percentage); } return; } ProcessRAW(currentGroup, shotSettings, fileName, targetFormat, inputFormat, maxThreads, HDRClippingPoint, HDRFeatherMultiplier, EXRIntegrityVerification); _counterDone++; lock (countLock) { worker?.ReportProgress((int)percentage); } }); this.Dispatcher.Invoke(() => { txtStatus.Text = "Finished. " + integrityCheckFailCount + " integrity check fails"; }); }
private void ProcessRAW(string[] srcRGBTriplet, ShotSetting[] shotSettings, string targetFilename, TARGETFORMAT targetFormat, FORMAT inputFormat, int maxThreads, float HDRClippingPoint, float HDRFeatherMultiplier, bool EXRIntegrityVerification) { int groupLength = shotSettings.Length; byte[][] buffers = new byte[groupLength][]; for (int i = 0; i < groupLength; i++) { buffers[i] = File.ReadAllBytes(srcRGBTriplet[i]); if (inputFormat == FORMAT.MONO12p) { buffers[i] = convert12pto16bit(buffers[i]); } } int width = 4096; int height = 3000; this.Dispatcher.Invoke(() => { width = 1; int.TryParse(rawWidth.Text, out width); height = 1; int.TryParse(rawHeight.Text, out height); }); byte[][] RGBBuffers = HDRMerge(buffers, shotSettings, HDRClippingPoint, HDRFeatherMultiplier); byte[] buffR = RGBBuffers[0]; byte[] buffG = RGBBuffers[1]; byte[] buffB = RGBBuffers[2]; // Interleave int pixelCount = width * height; int totalLength = width * height * 3; byte[] buff = new byte[totalLength * 2]; if (buffR.Count() < pixelCount * 2) { this.Dispatcher.Invoke(() => { MessageBox.Show("Red file too short: " + srcRGBTriplet[0]); }); return; } if (buffG.Count() < pixelCount * 2) { this.Dispatcher.Invoke(() => { MessageBox.Show("Green file too short: " + srcRGBTriplet[1]); }); return; } if (buffB.Count() < pixelCount * 2) { this.Dispatcher.Invoke(() => { MessageBox.Show("Blue file too short: " + srcRGBTriplet[2]); }); return; } for (int pixelIndex = 0; pixelIndex < pixelCount; pixelIndex++) { /* * // BGR * buff[pixelIndex * 3 * 2] = buffB[pixelIndex * 2]; * buff[pixelIndex * 3 * 2 + 1] = buffB[pixelIndex * 2 + 1]; * buff[pixelIndex * 3 * 2 +4] = buffR[pixelIndex*2]; * buff[pixelIndex * 3 * 2 +5] = buffR[pixelIndex * 2 + 1]; * buff[pixelIndex * 3 * 2 +2] = buffG[pixelIndex * 2]; * buff[pixelIndex * 3 * 2 +3] = buffG[pixelIndex * 2 + 1]; */ // RGB buff[pixelIndex * 3 * 2] = buffR[pixelIndex * 2]; buff[pixelIndex * 3 * 2 + 1] = buffR[pixelIndex * 2 + 1]; buff[pixelIndex * 3 * 2 + 2] = buffG[pixelIndex * 2]; buff[pixelIndex * 3 * 2 + 3] = buffG[pixelIndex * 2 + 1]; buff[pixelIndex * 3 * 2 + 4] = buffB[pixelIndex * 2]; buff[pixelIndex * 3 * 2 + 5] = buffB[pixelIndex * 2 + 1]; } string fileName = targetFilename; if (targetFormat == TARGETFORMAT.EXR) { ResourceLimits.Thread = (ulong)maxThreads; ResourceLimits.LimitMemory(new Percentage(90)); MagickReadSettings settings = new MagickReadSettings(); settings.Width = width; settings.Height = height; settings.Format = MagickFormat.Rgb; // Correction, this is actually right, I had flipped RGB to BGR elsewhere in the code before. Fixed now. /*ColorManager.ICC.ICCProfileWriter iccWriter = new ColorManager.ICC.ICCProfileWriter(); * iccWriter.WriteProfile(new ColorManager.ICC.ICCProfile()); */ if (EXRIntegrityVerification) { /* * Info on half float format: https://www.openexr.com/about.html */ // What does this mean for precision of converting 16 bit integers to 16 bit floating point? // We need to know the maximum precision achievable to be able to tell rounding errors from actual integrity fails. // More info here: https://en.wikipedia.org/wiki/Half-precision_floating-point_format // Basically, precision at any given value is 11 bits or 2048 values. int integrityCheckFailCountLocal = 0; bool integrityCheckPassed = false; bool retriesExhausted = false; while (!integrityCheckPassed && !retriesExhausted) { using (var image = new MagickImage(buff, settings)) { //ExifProfile profile = new ExifProfile(); //profile.SetValue(ExifTag.UserComment, Encoding.ASCII.GetBytes(srcRGBTriplet[0] + "," + srcRGBTriplet[1] + "," + srcRGBTriplet[2])); //image.SetProfile(profile); image.Format = MagickFormat.Exr; image.Settings.Compression = CompressionMethod.Piz; //image.Write(fileName); byte[] exrFile = image.ToByteArray(); bool integrityCheckFailed = false; using (var reloadedImage = new MagickImage(exrFile)) { reloadedImage.Depth = 16; reloadedImage.ColorSpace = ColorSpace.Undefined; byte[] reloadedImageBytes = reloadedImage.ToByteArray(MagickFormat.Rgb); integrityCheckFailed = integrityCheckFailed | !IntegrityChecker.VerifyIntegrityUInt16InHalfPrecisionFloat(buff, reloadedImageBytes); } if (integrityCheckFailed) { integrityCheckFailCount++; integrityCheckFailCountLocal++; continue; } else { integrityCheckPassed = true; File.WriteAllBytes(fileName, exrFile); } if (integrityCheckFailCountLocal > integrityCheckRetries) { retriesExhausted = true; // At this point just write it into a subfolder and be done with it. string failedFolder = Path.GetDirectoryName(fileName) + Path.DirectorySeparatorChar + "FAILED" + Path.DirectorySeparatorChar; Directory.CreateDirectory(failedFolder); string failedFile = failedFolder + Path.GetFileName(fileName); File.WriteAllBytes(failedFile, exrFile); } } } } else { using (var image = new MagickImage(buff, settings)) { //ExifProfile profile = new ExifProfile(); //profile.SetValue(ExifTag.UserComment, Encoding.ASCII.GetBytes(srcRGBTriplet[0] + "," + srcRGBTriplet[1] + "," + srcRGBTriplet[2])); //image.SetProfile(profile); image.Format = MagickFormat.Exr; image.Settings.Compression = CompressionMethod.Piz; //image.Write(fileName); byte[] exrFile = image.ToByteArray(); File.WriteAllBytes(fileName, exrFile); } } } else if (targetFormat == TARGETFORMAT.TIF) { using (Tiff output = Tiff.Open(fileName, "w")) { output.SetField(TiffTag.SUBFILETYPE, 0); //output.SetField(TiffTag.ORIGINALRAWFILENAME, srcRGBTriplet[0]+","+srcRGBTriplet[1]+","+srcRGBTriplet[2]); output.SetField(TiffTag.IMAGEWIDTH, width); output.SetField(TiffTag.IMAGELENGTH, height); output.SetField(TiffTag.SAMPLESPERPIXEL, 3); output.SetField(TiffTag.BITSPERSAMPLE, 16); output.SetField(TiffTag.ORIENTATION, Orientation.TOPLEFT); output.SetField(TiffTag.PHOTOMETRIC, Photometric.RGB); output.SetField(TiffTag.FILLORDER, FillOrder.MSB2LSB); output.SetField(TiffTag.COMPRESSION, Compression.DEFLATE); output.SetField(TiffTag.PLANARCONFIG, PlanarConfig.CONTIG); output.WriteEncodedStrip(0, buff, width * height * 2 * 3); } } }