/// <summary> /// Blends images stored in this <see cref="Blending"/> object. /// magicSliderValue: Percentage of range? See whiteboard...[0, 1] /// </summary> public static HDRImage Blend(float magicSliderValue, params LDRImage[] imgs) { // Put that array in a list //_imgs = new List<LDRImage>(imgs); // Init outputs // TODO: Get real values for height and width /* * * So yeah here I guess we do the calculations * I imagine for max multithread perf. we should * split the pixels up by rows? * */ int n = imgs.Length; //Number of Images Being Used byte[][] imageBytes = new byte[n][]; //Row: Image Number Column: byte value for (int i = 0; i < n; i++) //Place pixels into 2D Array { imageBytes[i] = imgs[i].GetBytes(); } float[] raw = new float[imageBytes[0].Length];//Output Array float[] xValues = new float[n];//Average Brightness of Image #j for (int j = 0; j < xValues.Length; j++) { xValues[j] = AverageBrightness(imageBytes[j]); } for (int i = 0; i < imgs[0].GetDecoder().PixelWidth *imgs[0].GetDecoder().PixelHeight; i++) //Loop through each Pixel Position { float[] yValues = new float[n]; //Luminance //Terms used in Logarithmic Regression float sumYLnX = 0; float sumY = 0; float sumLnX = 0; float sum_LnX2 = 0; float sumLnX_2 = 0; for (int j = 0; j < n; j++)//Find Pixel Values at Position i for each image { //Calculate Y Value (Luminance) for each image at Position i //0.299 * R + 0.587 * G + 0.114 * B = Luminance yValues[j] = (float)(0.299 * imageBytes[j][i * 4 + 0] + 0.587 * imageBytes[j][i * 4 + 1] + 0.114 * imageBytes[j][i * 4 + 2]); sumYLnX += (float)yValues[j] * (float)Math.Log(xValues[j]); sumY += yValues[j]; sumLnX += (float)Math.Log(xValues[j]); sum_LnX2 += (float)Math.Log(xValues[j]) * (float)Math.Log(xValues[j]); } sumLnX_2 = sumLnX * sumLnX; float b = (n * sumYLnX - sumY * sumLnX) / (n * sum_LnX2 - sumLnX_2); //Coefficient (Vertical Stretch) of Parent Function ln() float a = (sumY - b * sumLnX) / n; //Constant term (Vertical Shift) //Bounds for Transformation along Curve float alpha = xValues[n / 2]; float beta = xValues[0]; //Choose either left or right bound based on size of range of Y values if (b * Math.Abs(Math.Log(xValues[n / 2]) - Math.Log(xValues[0])) < b * Math.Abs(Math.Log(xValues[xValues.Length - 1]) - Math.Log(xValues[n / 2]))) { beta = xValues[xValues.Length - 1]; } float gamma = Math.Abs(alpha - beta) * magicSliderValue;//X Value of New Luminance Value for output pixel if (beta < alpha) { gamma += beta; } else { gamma += alpha; } float newLuminance = a + b * (float)Math.Log(gamma);//New Luminance for output pixel //Calculate chrominance channels from middle image to recover new RGB values float cb = 128 + (float)(-0.168736 * imageBytes[n / 2][i * 4] - 0.331264 * imageBytes[n / 2][i * 4 + 1] + 0.5 * imageBytes[n / 2][i * 4 + 2]); float cr = 128 + (float)(0.5 * imageBytes[n / 2][i * 4] - 0.418688 * imageBytes[n / 2][i * 4 + 1] - 0.081312 * imageBytes[n / 2][i * 4 + 2]); //Recover RGB values float newRed = newLuminance + 1.042f * (cr - 128); float newGreen = newLuminance - 0.344136f * (cb - 128) - 0.714136f * (cr - 128); float newBlue = newLuminance + 1.772f * (cb - 128); //Assign to output array // Swapped red and blue because it seemed to be flipping those - Callum raw[i * 4] = newBlue; raw[i * 4 + 1] = newGreen; raw[i * 4 + 2] = newRed; raw[i * 4 + 3] = imageBytes[n / 2][i * 4 + 3]; } // Construct HDR image for final output HDRImage output = new HDRImage(imgs[0].GetWidth(), raw); return(output); }