public static DNGVector operator *(DNGVector vec, double scale) { DNGVector ret = new DNGVector(vec); ret.Scale(scale); return(ret); }
/// \brief Compute a 3x3 matrix which maps colors from white point white1 to /// white point white2 /// /// Uses linearized Bradford adaptation matrix to compute a mapping from /// colors measured with one white point (white1) to another (white2). public static DNGMatrix3x3 MapWhiteMatrix(DNGxyCoord white1, DNGxyCoord white2) { // Use the linearized Bradford adaptation matrix. DNGMatrix3x3 Mb = new DNGMatrix3x3(0.8951, 0.2664, -0.1614, -0.7502, 1.7135, 0.0367, 0.0389, -0.0685, 1.0296); DNGVector w1 = Mb * white1.XYtoXYZ(); DNGVector w2 = Mb * white2.XYtoXYZ(); // Negative white coordinates are kind of meaningless. w1[0] = Math.Max(w1[0], 0.0); w1[1] = Math.Max(w1[1], 0.0); w1[2] = Math.Max(w1[2], 0.0); w2[0] = Math.Max(w2[0], 0.0); w2[1] = Math.Max(w2[1], 0.0); w2[2] = Math.Max(w2[2], 0.0); // Limit scaling to something reasonable. DNGMatrix3x3 A = new DNGMatrix3x3(); A[0, 0] = DNGUtils.Pin(0.1, w1[0] > 0.0 ? w2[0] / w1[0] : 10.0, 10.0); A[1, 1] = DNGUtils.Pin(0.1, w1[1] > 0.0 ? w2[1] / w1[1] : 10.0, 10.0); A[2, 2] = DNGUtils.Pin(0.1, w1[2] > 0.0 ? w2[2] / w1[2] : 10.0, 10.0); DNGMatrix3x3 B = new DNGMatrix3x3(Mb.Invert() * A * Mb); return(B); }
public static DNGVector Identity(uint size) { DNGVector mat = new DNGVector(); mat.SetIdentity(size); return(mat); }
public DNGVector4(DNGVector v) : base(v) { if (Count != 4) { throw new ArgumentException("This is not a four element vector."); } }
public DNGVector3(DNGVector v) : base(v) { if (Count != 3) { throw new ArgumentException("This is not a three element vector."); } }
public DNGVector(DNGVector aVector) { _count = aVector.Count; for (uint index = 0; index < _count; index++) { _data[index] = aVector._data[index]; } }
public override bool Equals(object obj) { if (obj == null) { return(false); } if (!(obj is DNGVector)) { return(false); } DNGVector value = (DNGVector)obj; return(Equals(value)); }
public bool Equals(DNGVector value) { if (_count != value._count) { return(false); } for (uint index = 0; index < _count; index++) { if (_data[index] != value._data[index]) { return(false); } } return(true); }
public static double Dot(DNGVector a, DNGVector b) { uint count = a.Count; if (b.Count != count) { throw new ArgumentException("Vectors don't have same size."); } double sum = 0.0; for (uint i = 0; i < count; i++) { sum += a[i] * b[i]; } return(sum); }
public static DNGVector operator -(DNGVector a, DNGVector b) { uint count = a.Count; if (b.Count != count) { throw new ArgumentException("Vectors don't have same size."); } DNGVector ret = new DNGVector(count); for (uint i = 0; i < count; i++) { ret[i] = a[i] - b[i]; } return(ret); }
private DNGMatrix NormalizeForwardMatrix(DNGMatrix m) { if (m == null) { return(new DNGMatrix()); } if (m.NotEmpty()) { DNGVector cameraOne = DNGVector.Identity(m.Cols); DNGVector xyz = m * cameraOne; m = DNGxyCoord.PCStoXYZ.AsDiagonal() * (xyz.AsDiagonal().Invert()) * m; } return(m); }
public DNGColorSpace(DNGMatrix3x3 toPCS) { // The matrix values are often rounded, so adjust to // get them to convert device white exactly to the PCS. DNGVector W1 = toPCS * new DNGVector3(1.0, 1.0, 1.0); DNGVector W2 = DNGxyCoord.PCStoXYZ; double s0 = W2[0] / W1[0]; double s1 = W2[1] / W1[1]; double s2 = W2[2] / W1[2]; DNGMatrix3x3 S = new DNGMatrix3x3(s0, 0, 0, 0, s1, 0, 0, 0, s2); mToPCS = S * toPCS; // Find reverse matrix. mFromPCS = mToPCS.Invert(); }
/// Return the XY value to use for SetWhiteXY for a given camera color /// space coordinate as the white point. /// \param neutral A camera color space value to use for white point. /// Components range from 0.0 to 1.0 and should be normalized such that /// the largest value is 1.0 . /// \retval White point in XY space that makes neutral map to this /// XY value as closely as possible. public DNGxyCoord NeutralToXY(DNGVector neutral) { const uint kMaxPasses = 30; if (fChannels == 1) { return(DNGxyCoord.PCStoXY); } DNGxyCoord last = DNGxyCoord.D50; for (uint pass = 0; pass < kMaxPasses; pass++) { DNGMatrix nullMat = null; DNGMatrix xyzToCamera = FindXYZtoCamera(last, ref nullMat, ref nullMat, ref nullMat); DNGMatrix inv = xyzToCamera.Invert(); DNGVector vec = inv * neutral; DNGVector3 vec3 = new DNGVector3(vec); DNGxyCoord next = DNGxyCoord.XYZtoXY(new DNGVector3(xyzToCamera.Invert() * neutral)); if (Math.Abs(next.X - last.X) + Math.Abs(next.Y - last.Y) < 0.0000001) { return(next); } // If we reach the limit without converging, we are most likely // in a two value oscillation. So take the average of the last // two estimates and give up. if (pass == kMaxPasses - 1) { next.X = (last.X + next.X) * 0.5; next.Y = (last.Y + next.Y) * 0.5; } last = next; } return(last); }
public static DNGVector operator *(DNGMatrix a, DNGVector b) { if (a.Cols != b.Count) { throw new ArgumentException("Matrix/Vector dimensions don't match."); } DNGVector ret = new DNGVector(a.Rows); for (uint j = 0; j < ret.Count; j++) { ret[j] = 0.0; for (uint m = 0; m < a.Cols; m++) { double aa = a[j, m]; double bb = b[m]; ret[j] += aa * bb; } } return(ret); }
private ImageFileDirectory SetMembers() { ImageFileDirectory rawIFD = null; //find the IFD with the RAW Bayer image for (int i = 0; i < mIfds.Count; i++) { //well, it should actually be somewhere in IFD0... if (mIfds[i].GetEntry <IFDPhotometricInterpretation>() != null) { if (mIfds[i].GetEntry <IFDPhotometricInterpretation>().Value == IFDPhotometricInterpretation.PhotometricInterpretation.CFA) { rawIFD = mIfds[i]; break; } } } //no root IFD seems to contain RAW bayer, search for Sub-IFDs if (rawIFD == null) { //find the IFD with the RAW Bayer image for (int i = 0; i < mIfds.Count; i++) { IFDSubIFDs subIFD = mIfds[i].GetEntry <IFDSubIFDs>(); if (subIFD == null) { continue; } for (int j = 0; j < subIFD.Value.Count; j++) { if (subIFD.Value[j].GetEntry <IFDPhotometricInterpretation>().Value == IFDPhotometricInterpretation.PhotometricInterpretation.CFA) { rawIFD = subIFD.Value[j]; break; } } } } if (rawIFD == null) { throw new ArgumentException("Can't find IFD with Bayer RAW image."); } mLinearizationTable = rawIFD.GetEntry <IFDDNGLinearizationTable>(); mWidth = (int)rawIFD.GetEntry <IFDImageWidth>().Value; mHeight = (int)rawIFD.GetEntry <IFDImageLength>().Value; if (mIfds[0].GetEntry <IFDDateTime>() != null) { mRecordingDate = mIfds[0].GetEntry <IFDDateTime>().Value; } //in case of Pentax this will have succes: IFDDNGPrivateData privateData = mIfds[0].GetEntry <IFDDNGPrivateData>(); if (privateData != null) { MNLevelInfo levelInfo = privateData.PentaxMakerNotes.GetEntry <MNLevelInfo>(); if (levelInfo != null) { mRollAngle = levelInfo.Value.RollAngle; mRollAnglePresent = true; } } IFDExif exif = mIfds[0].GetEntry <IFDExif>(); if (exif != null) { mISO = exif.GetEntry <ExifISOSpeedRatings>().Value; mExposureTime = exif.GetEntry <ExifExposureTime>().Value; mRecordingDate = (exif.GetEntry <ExifDateTimeDigitized>()?.Value).GetValueOrDefault(mRecordingDate); } else if (rawIFD.GetEntry <IFDISOSpeedRatings>() != null) { mISO = rawIFD.GetEntry <IFDISOSpeedRatings>().Value; //very likely that exposure time is also present mExposureTime = rawIFD.GetEntry <IFDExposureTime>().Value; } else if (mIfds[0].GetEntry <IFDISOSpeedRatings>() != null) { mISO = mIfds[0].GetEntry <IFDISOSpeedRatings>().Value; //very likely that exposure time is also present mExposureTime = mIfds[0].GetEntry <IFDExposureTime>().Value; } mBitDepth = rawIFD.GetEntry <IFDBitsPerSample>().Value[0]; int bayerWidth = rawIFD.GetEntry <IFDCFARepeatPatternDim>().Value[0]; int bayerHeight = rawIFD.GetEntry <IFDCFARepeatPatternDim>().Value[1]; if (bayerHeight != 2 || bayerWidth != 2) { throw new ArgumentException("This file has a bayer pattern size different than 2x2. Can't decode that."); } ExifCFAPattern.BayerColor[] bayer = rawIFD.GetEntry <IFDCFAPattern>().Value; mBayerPattern = new BayerColor[bayer.Length]; for (int i = 0; i < bayer.Length; i++) { mBayerPattern[i] = (BayerColor)(int)bayer[i]; } IFDDNGCFAPlaneColor planeColor = rawIFD.GetEntry <IFDDNGCFAPlaneColor>(); int[] planeColorHelper = new int[] { 0, 1, 2 }; if (planeColor != null) //did it ever differ from 0,1,2? { //0 = red, 1 = gree, 2 = blue. //The debayer algo creates images with plane order red/green/blue. //If this order differs, we need to re-order the planes in order to //have the color matrices correct. //reset colorTwist matrix: mColorTwist = new float[3, 4]; if (planeColor.Value.Length > 3 || planeColor.Value.Length < 3) { throw new ArgumentException("This image doesn't contain three color planes."); } for (int i = 0; i < planeColor.Value.Length; i++) { int color = planeColor.Value[i]; planeColorHelper[i] = color; if (color > 2) { throw new ArgumentException("This image contains colors different than red/green/blue."); } mColorTwist[color, i] = 1; if (color != i) { mColorTwistIsIdentity = false; } } } mColorSpec = new DNGColorSpec(3, mIfds[0], rawIFD); IFDDNGWhiteLevel whiteLevel = rawIFD.GetEntry <IFDDNGWhiteLevel>(); IFDDNGBlackLevel blackLevel = rawIFD.GetEntry <IFDDNGBlackLevel>(); mBlackLevel = new float[3]; if (blackLevel != null) { //only one value for all colors if (blackLevel.Value.Length == 1) { mBlackLevel[0] = (float)blackLevel.Value[0].Value; mBlackLevel[1] = (float)blackLevel.Value[0].Value; mBlackLevel[2] = (float)blackLevel.Value[0].Value; } //values per color channel if (blackLevel.Value.Length == 3) { mBlackLevel[planeColorHelper[0]] = (float)blackLevel.Value[0].Value; mBlackLevel[planeColorHelper[1]] = (float)blackLevel.Value[1].Value; mBlackLevel[planeColorHelper[2]] = (float)blackLevel.Value[2].Value; } //values per color bayer pattern if (blackLevel.Value.Length == 4) { //red int indexR = -1; for (int i = 0; i < mBayerPattern.Length; i++) { if (mBayerPattern[i] == BayerColor.Red) { indexR = i; break; } } mBlackLevel[0] = (float)blackLevel.Value[indexR].Value; //blue int indexB = -1; for (int i = 0; i < mBayerPattern.Length; i++) { if (mBayerPattern[i] == BayerColor.Blue) { indexB = i; break; } } mBlackLevel[2] = (float)blackLevel.Value[indexB].Value; //green, the two remaining indices int indexG1 = -1, indexG2 = -1; for (int i = 0; i < mBayerPattern.Length; i++) { if (mBayerPattern[i] == BayerColor.Green && indexG1 == -1) { indexG1 = i; } if (mBayerPattern[i] == BayerColor.Green && indexG1 != -1) { indexG2 = i; } } float g1 = (float)blackLevel.Value[indexG1].Value; float g2 = (float)blackLevel.Value[indexG2].Value; mBlackLevel[1] = Math.Max(g1, g2); //well, one could distinguish the two greens, but what for? } } mWhiteLevel = new float[3]; mWhiteLevel[0] = (float)whiteLevel.Value[0]; mWhiteLevel[1] = (float)whiteLevel.Value[0]; mWhiteLevel[2] = (float)whiteLevel.Value[0]; //subtract black level from white level mWhiteLevel[0] -= mBlackLevel[0]; mWhiteLevel[1] -= mBlackLevel[1]; mWhiteLevel[2] -= mBlackLevel[2]; //get white balance from color spec DNGVector wb = mColorSpec.CameraWhite; mWhiteBalance = new float[3]; mWhiteBalance[planeColorHelper[0]] = 1.0f / (float)wb[0]; mWhiteBalance[planeColorHelper[1]] = 1.0f / (float)wb[1]; mWhiteBalance[planeColorHelper[2]] = 1.0f / (float)wb[2]; //look for orientation tag. If RAW ifd has the tag, choose that one if (rawIFD.GetEntry <IFDOrientation>() != null) { mOrientation = new DNGOrientation(rawIFD.GetEntry <IFDOrientation>().Value); } else if (mIfds[0].GetEntry <IFDOrientation>() != null) { mOrientation = new DNGOrientation(mIfds[0].GetEntry <IFDOrientation>().Value); } else { //no tag found, use default mOrientation = new DNGOrientation(DNGOrientation.Orientation.Normal); } //default Values: int cropLeft = 0; int cropTop = 0; int croppedWidth = mWidth; int croppedHeight = mHeight; IFDDNGActiveArea activeArea = rawIFD.GetEntry <IFDDNGActiveArea>(); //if active area is defined: if (activeArea != null) { int top, left, bottom, right; top = (int)activeArea.Value[0]; left = (int)activeArea.Value[1]; bottom = (int)activeArea.Value[2]; right = (int)activeArea.Value[3]; cropLeft += left; cropTop += top; croppedWidth = right - left; croppedHeight = bottom - top; //CFA pattern is defined on active area. If top/left is uneven we need to shift the CFA pattern accordingly if (top % 2 != 0) { BayerColor bayer0 = BayerPattern[0]; BayerColor bayer1 = BayerPattern[1]; BayerPattern[0] = BayerPattern[2]; BayerPattern[1] = BayerPattern[3]; BayerPattern[2] = bayer0; BayerPattern[3] = bayer1; } if (left % 2 != 0) { BayerColor bayer0 = BayerPattern[0]; BayerColor bayer2 = BayerPattern[2]; BayerPattern[0] = BayerPattern[1]; BayerPattern[2] = BayerPattern[3]; BayerPattern[1] = bayer0; BayerPattern[3] = bayer2; } } IFDDNGDefaultCropOrigin cropOrigin = rawIFD.GetEntry <IFDDNGDefaultCropOrigin>(); IFDDNGDefaultCropSize cropSize = rawIFD.GetEntry <IFDDNGDefaultCropSize>(); if (cropOrigin != null && cropSize != null) { int top, left, width, height; left = (int)(cropOrigin.Value[0].Value); top = (int)(cropOrigin.Value[0].Value); width = (int)(cropSize.Value[0].Value); height = (int)(cropSize.Value[0].Value); cropLeft += left; cropTop += top; croppedWidth = width; croppedHeight = height; } //we always crop at least two pixels because of our algos... mCropLeft = Math.Max(2, cropLeft); mCropTop = Math.Max(2, cropTop); mCroppedWidth = croppedWidth - Math.Max(0, (cropLeft + croppedWidth) - (mWidth - 2)); mCroppedHeight = croppedHeight - Math.Max(0, (cropTop + croppedHeight) - (mHeight - 2)); IFDDNGNoiseProfile noise = rawIFD.GetEntry <IFDDNGNoiseProfile>(); if (noise == null) { noise = mIfds[0].GetEntry <IFDDNGNoiseProfile>(); } if (noise != null) { //if noise level is given for all channels, //take the green one as it is usually scalled to one if (noise.Value.Length > 2) { mNoiseModelAlpha = (float)noise.Value[planeColorHelper[1] * 2]; mNoiseModelBeta = (float)noise.Value[planeColorHelper[1] * 2 + 1]; } else { mNoiseModelAlpha = (float)noise.Value[0]; mNoiseModelBeta = (float)noise.Value[1]; } } mMake = mIfds[0].GetEntry <IFDMake>().Value; mUniqueModelName = mIfds[0].GetEntry <IFDDNGUniqueCameraModel>().Value; return(rawIFD); }
public static double Distance(DNGVector a, DNGVector b) { DNGVector c = a - b; return(Math.Sqrt(Dot(c, c))); }
public DNGColorSpec(uint aChannels, ImageFileDirectory ifd0, ImageFileDirectory raw) { fChannels = aChannels; if (GetTag <IFDDNGCalibrationIlluminant1>(ifd0, raw) != null) { fTemperature1 = ConvertIlluminantToTemperature(GetTag <IFDDNGCalibrationIlluminant1>(ifd0, raw).Value); } else { fTemperature1 = 0.0; } if (GetTag <IFDDNGCalibrationIlluminant2>(ifd0, raw) != null) { fTemperature2 = ConvertIlluminantToTemperature(GetTag <IFDDNGCalibrationIlluminant2>(ifd0, raw).Value); } else { fTemperature2 = 0.0; } fColorMatrix1 = GetTag <IFDDNGColorMatrix1>(ifd0, raw)?.Matrix; if (fColorMatrix1 == null) { fColorMatrix1 = DNGMatrix.Identity(fChannels); //best choice if nothing is given... } fColorMatrix2 = GetTag <IFDDNGColorMatrix2>(ifd0, raw)?.Matrix; if (fColorMatrix2 == null) { fColorMatrix2 = new DNGMatrix(); } fForwardMatrix1 = GetTag <IFDDNGForwardMatrix1>(ifd0, raw)?.Matrix; if (fForwardMatrix1 == null) { fForwardMatrix1 = new DNGMatrix(); } fForwardMatrix2 = GetTag <IFDDNGForwardMatrix2>(ifd0, raw)?.Matrix; if (fForwardMatrix2 == null) { fForwardMatrix2 = new DNGMatrix(); } fReductionMatrix1 = GetTag <IFDDNGReductionMatrix1>(ifd0, raw)?.Matrix; if (fReductionMatrix1 == null) { fReductionMatrix1 = new DNGMatrix(); } fReductionMatrix2 = GetTag <IFDDNGReductionMatrix2>(ifd0, raw)?.Matrix; if (fReductionMatrix2 == null) { fReductionMatrix2 = new DNGMatrix(); } fCameraCalibration1 = GetTag <IFDDNGCameraCalibration1>(ifd0, raw)?.Matrix; fCameraCalibration2 = GetTag <IFDDNGCameraCalibration1>(ifd0, raw)?.Matrix; if (fCameraCalibration1 == null) { fCameraCalibration1 = DNGMatrix.Identity(fChannels); } if (fCameraCalibration2 == null) { fCameraCalibration2 = DNGMatrix.Identity(fChannels); } fAnalogBalance = GetTag <IFDDNGAnalogBalance>(ifd0, raw)?.Vector.AsDiagonal(); if (fAnalogBalance == null) { fAnalogBalance = DNGMatrix.Identity(fChannels); } fForwardMatrix1 = NormalizeForwardMatrix(fForwardMatrix1); fColorMatrix1 = fAnalogBalance * fCameraCalibration1 * fColorMatrix1; if (fColorMatrix2.IsEmpty() || fTemperature1 <= 0.0 || fTemperature2 <= 0.0 || fTemperature1 == fTemperature2) { fTemperature1 = 5000.0; fTemperature2 = 5000.0; fColorMatrix2 = fColorMatrix1; fForwardMatrix2 = fForwardMatrix1; fReductionMatrix2 = fReductionMatrix1; fCameraCalibration2 = fCameraCalibration1; } else { fForwardMatrix2 = NormalizeForwardMatrix(fForwardMatrix2); fColorMatrix2 = fAnalogBalance * fCameraCalibration2 * fColorMatrix2; // Swap values if temperatures are out of order. if (fTemperature1 > fTemperature2) { double temp = fTemperature1; fTemperature1 = fTemperature2; fTemperature2 = temp; DNGMatrix T = fColorMatrix1; fColorMatrix1 = fColorMatrix2; fColorMatrix2 = T; T = fForwardMatrix1; fForwardMatrix1 = fForwardMatrix2; fForwardMatrix2 = T; T = fReductionMatrix1; fReductionMatrix1 = fReductionMatrix2; fReductionMatrix2 = T; T = fCameraCalibration1; fCameraCalibration1 = fCameraCalibration2; fCameraCalibration2 = T; } } IFDDNGAsShotNeutral neutral = GetTag <IFDDNGAsShotNeutral>(ifd0, raw); IFDDNGAsShotWhiteXY asShot = GetTag <IFDDNGAsShotWhiteXY>(ifd0, raw); DNGxyCoord white; if (asShot == null) { if (neutral == null) { throw new ArgumentException("The DNG spec says that one of the As Shot White balance tags must be present."); } DNGVector vec = new DNGVector((uint)neutral.Value.Length); for (uint c = 0; c < neutral.Value.Length; c++) { vec[c] = neutral.Value[c].Value; } double unify = 1.0 / vec.MaxEntry(); vec = unify * vec; white = NeutralToXY(vec); } else { double x = asShot.Value[0].Value; double y = asShot.Value[1].Value; white = new DNGxyCoord(x, y); } WhiteXY = white; }
public DNGColorSpec(double[] colorMatrix1, double[] colorMatrix2, IFDDNGCalibrationIlluminant.Illuminant illuminant1, IFDDNGCalibrationIlluminant.Illuminant illuminant2, float[] whiteBalance) { fChannels = 3; fTemperature1 = ConvertIlluminantToTemperature(illuminant1); fTemperature2 = ConvertIlluminantToTemperature(illuminant2); if (colorMatrix1 == null) { fColorMatrix1 = DNGMatrix.Identity(fChannels); //best choice if nothing is given... } else { fColorMatrix1 = new DNGMatrix3x3(colorMatrix1); } if (colorMatrix2 == null) { fColorMatrix2 = new DNGMatrix(); } else { fColorMatrix2 = new DNGMatrix3x3(colorMatrix2); } fForwardMatrix1 = new DNGMatrix(); fForwardMatrix2 = new DNGMatrix(); fReductionMatrix1 = new DNGMatrix(); fReductionMatrix2 = new DNGMatrix(); fCameraCalibration1 = DNGMatrix.Identity(fChannels); fCameraCalibration2 = DNGMatrix.Identity(fChannels); fAnalogBalance = DNGMatrix.Identity(fChannels); fForwardMatrix1 = NormalizeForwardMatrix(fForwardMatrix1); fColorMatrix1 = fAnalogBalance * fCameraCalibration1 * fColorMatrix1; if (fColorMatrix2.IsEmpty() || fTemperature1 <= 0.0 || fTemperature2 <= 0.0 || fTemperature1 == fTemperature2) { fTemperature1 = 5000.0; fTemperature2 = 5000.0; fColorMatrix2 = fColorMatrix1; fForwardMatrix2 = fForwardMatrix1; fReductionMatrix2 = fReductionMatrix1; fCameraCalibration2 = fCameraCalibration1; } else { fForwardMatrix2 = NormalizeForwardMatrix(fForwardMatrix2); fColorMatrix2 = fAnalogBalance * fCameraCalibration2 * fColorMatrix2; // Swap values if temperatures are out of order. if (fTemperature1 > fTemperature2) { double temp = fTemperature1; fTemperature1 = fTemperature2; fTemperature2 = temp; DNGMatrix T = fColorMatrix1; fColorMatrix1 = fColorMatrix2; fColorMatrix2 = T; T = fForwardMatrix1; fForwardMatrix1 = fForwardMatrix2; fForwardMatrix2 = T; T = fReductionMatrix1; fReductionMatrix1 = fReductionMatrix2; fReductionMatrix2 = T; T = fCameraCalibration1; fCameraCalibration1 = fCameraCalibration2; fCameraCalibration2 = T; } } DNGxyCoord white; DNGVector vec = new DNGVector((uint)whiteBalance.Length); for (uint c = 0; c < whiteBalance.Length; c++) { //white point is given as a multiplicatice factor //actual white point is hence 1/value vec[c] = 1.0f / whiteBalance[c]; } double unify = 1.0 / vec.MaxEntry(); vec = unify * vec; white = NeutralToXY(vec); WhiteXY = white; }