/// <summary> /// Dissolves two images using alphablending /// </summary> /// <param name="startImage">Image for percentage=0</param> /// <param name="endImage">Image for percentage=1</param> /// <param name="morphingProgress">Dissolve percentage from 0 to 1</param> /// <param name="outputImage">Target for image output data</param> public unsafe void DissolveImages(Morphing.ImageData startImage, Morphing.ImageData endImage, float percentage, WriteableBitmap outputImage) { System.Diagnostics.Debug.Assert(percentage >= 0.0f && percentage <= 1.0f); System.Diagnostics.Debug.Assert(startImage != null && endImage != null && outputImage != null); outputImage.Lock(); int width = outputImage.PixelWidth; int height = outputImage.PixelHeight; float xStep = 1.0f / width; Color* outputData = (Color*)outputImage.BackBuffer; Parallel.For(0, outputImage.PixelHeight, yi => { Color* outputDataPixel = outputData + yi * width; Color* lastOutputDataPixel = outputDataPixel + width; float y = (float)yi / height; for (float x = 0; outputDataPixel != lastOutputDataPixel; x += xStep, ++outputDataPixel) { *outputDataPixel = Color.Lerp(startImage.Sample(x, y), endImage.Sample(x, y), percentage); } }); outputImage.AddDirtyRect(new System.Windows.Int32Rect(0, 0, outputImage.PixelWidth, outputImage.PixelHeight)); outputImage.Unlock(); }
unsafe void WarpingAlgorithm.WarpImage(MarkerSet markerSet, Morphing.ImageData inputImage, Morphing.ImageData outputImage, bool startImage) { System.Diagnostics.Debug.Assert(markerSet != null && inputImage != null && outputImage != null); PointMarkerSet pointMarkerSet = markerSet as PointMarkerSet; System.Diagnostics.Debug.Assert(pointMarkerSet != null); // minor precomputation - movevector & interppos (for more mem consistency) WarpMarker[] markers; if (startImage) { markers = pointMarkerSet.Points.Select(x => new WarpMarker { CurrentPos = x.InterpolatedMarker, MoveVec = x.StartMarker - x.InterpolatedMarker }).ToArray(); } else { markers = pointMarkerSet.Points.Select(x => new WarpMarker { CurrentPos = x.InterpolatedMarker, MoveVec = x.EndMarker - x.InterpolatedMarker }).ToArray(); } double xStep = 1.0 / outputImage.Width; if (markers.Length == 0) return; Parallel.For(0, outputImage.Height, yi => { Color* outputDataPixel = outputImage.Data + yi * outputImage.Width; Color* lastOutputDataPixel = outputDataPixel + outputImage.Width; double y = (double)yi / outputImage.Height; for (double x = 0; outputDataPixel != lastOutputDataPixel; x += xStep, ++outputDataPixel) { Vector position = new Vector(x, y); Vector displacement = new Vector(0,0); // fixed ptr won't work inside loop! // for(WarpMarker* pMarker = pMarkerFirst; pMarker != pMarkerEnd; ++pMarker) double weightSum = 0.0f; foreach (var marker in markers) { double distSq = (position - marker.CurrentPos).LengthSquared; double weight = Math.Exp(-distSq / POINT_WEIGHT);//1.0f / (1.0f + distSq / POINT_WEIGHT); // inverse quadratic! weightSum += weight; displacement += marker.MoveVec * weight; } displacement /= weightSum; position += displacement; position = position.ClampToImageArea(); *outputDataPixel = inputImage.Sample(position.X, position.Y); } }); }
unsafe void WarpingAlgorithm.WarpImage(MarkerSet markerSet, Morphing.ImageData inputImage, Morphing.ImageData outputImage, bool startImage) { System.Diagnostics.Debug.Assert(markerSet != null && inputImage != null && outputImage != null); LineMarkerSet lineMarkerSet = markerSet as LineMarkerSet; System.Diagnostics.Debug.Assert(lineMarkerSet != null); double xStep = 1.0 / outputImage.Width; WarpMarker[] markers; if (startImage) { markers = lineMarkerSet.Lines.Select(x => new WarpMarker { target_start = x.InterpolatedMarker.Start, target_dirNorm = x.InterpolatedMarker.End - x.InterpolatedMarker.Start, target_perpendicNorm = (x.InterpolatedMarker.End - x.InterpolatedMarker.Start).Perpendicular(), target_lineLength = (x.InterpolatedMarker.End - x.InterpolatedMarker.Start).Length, dest_start = x.StartMarker.Start, dest_dirNorm = x.StartMarker.End - x.StartMarker.Start, dest_perpendicNorm = (x.StartMarker.End - x.StartMarker.Start).Perpendicular(), }).ToArray(); } else { markers = lineMarkerSet.Lines.Select(x => new WarpMarker { target_start = x.InterpolatedMarker.Start, target_dirNorm = x.InterpolatedMarker.End - x.InterpolatedMarker.Start, target_perpendicNorm = (x.InterpolatedMarker.End - x.InterpolatedMarker.Start).Perpendicular(), target_lineLength = (x.InterpolatedMarker.End - x.InterpolatedMarker.Start).Length, dest_start = x.EndMarker.Start, dest_dirNorm = x.EndMarker.End - x.EndMarker.Start, dest_perpendicNorm = (x.EndMarker.End - x.EndMarker.Start).Perpendicular(), }).ToArray(); } for (int markerIdx = 0; markerIdx < markers.Length; ++markerIdx) { // markers[markerIdx].target_dirLength = markers[markerIdx].target_dir.Length; markers[markerIdx].target_perpendicNorm.Normalize(); markers[markerIdx].target_dirNorm.Normalize(); markers[markerIdx].dest_perpendicNorm.Normalize(); markers[markerIdx].dest_dirNorm.Normalize(); } if (markers.Length == 0) return; Parallel.For(0, outputImage.Height, yi => { Color* outputDataPixel = outputImage.Data + yi * outputImage.Width; Color* lastOutputDataPixel = outputDataPixel + outputImage.Width; double y = (double)yi / outputImage.Height; for (double x = 0; outputDataPixel != lastOutputDataPixel; x += xStep, ++outputDataPixel) { Vector position = new Vector(x, y); Vector displacement = new Vector(0, 0); double weightSum = 0.0f; for (int markerIdx = 0; markerIdx < markers.Length; ++markerIdx) { Vector toStart = position - markers[markerIdx].target_start; // calc relative coordinates to line double u = toStart.Dot(markers[markerIdx].target_dirNorm); double v = toStart.Dot(markers[markerIdx].target_perpendicNorm); // calc weight double weight; if (u < 0) // bellow weight = toStart.LengthSquared; else if (u > 1) // above weight = (toStart + markers[markerIdx].target_dirNorm * markers[markerIdx].target_lineLength).LengthSquared; else // beside weight = v * v; weight = Math.Exp(-weight / LINE_WEIGHT); //Math.Pow(markers[markerIdx].target_lineLength / (A + weight), B); weightSum += weight; // translation Vector srcPoint = markers[markerIdx].dest_start + u * markers[markerIdx].dest_dirNorm + v * markers[markerIdx].dest_perpendicNorm; displacement += (srcPoint - position) * weight; } displacement /= weightSum; position += displacement; position = position.ClampToImageArea(); *outputDataPixel = inputImage.Sample(position.X, position.Y); } }); }
unsafe void WarpingAlgorithm.WarpImage(MarkerSet markerSet, Morphing.ImageData inputImage, Morphing.ImageData outputImage, bool startImage) { System.Diagnostics.Debug.Assert(markerSet != null && inputImage != null && outputImage != null); TriangleMarkerSet triangleMarkerSet = markerSet as TriangleMarkerSet; System.Diagnostics.Debug.Assert(triangleMarkerSet != null); if (triangleMarkerSet.Triangles.Count() == 0) return; // prepare data // dest DestTriangle[] destTriangles = triangleMarkerSet.Triangles.Select(x => new DestTriangle() { A = x.Vertices[0].InterpolatedMarker, V0 = x.Vertices[2].InterpolatedMarker - x.Vertices[0].InterpolatedMarker, V1 = x.Vertices[1].InterpolatedMarker - x.Vertices[0].InterpolatedMarker }).ToArray(); for (int i = 0; i < destTriangles.Length; ++i ) { destTriangles[i].V0DotV0_divDenom = destTriangles[i].V0.LengthSquared; destTriangles[i].V1DotV1_divDenom = destTriangles[i].V1.LengthSquared; destTriangles[i].V0DotV1_divDenom = destTriangles[i].V0.Dot(destTriangles[i].V1); double invDenom = 1.0 / (destTriangles[i].V0DotV0_divDenom * destTriangles[i].V1DotV1_divDenom - destTriangles[i].V0DotV1_divDenom * destTriangles[i].V0DotV1_divDenom); destTriangles[i].V0DotV0_divDenom *= invDenom; destTriangles[i].V1DotV1_divDenom *= invDenom; destTriangles[i].V0DotV1_divDenom *= invDenom; } // src SourceTriangle[] sourceTriangles; if (startImage) { sourceTriangles = triangleMarkerSet.Triangles.Select(x => new SourceTriangle() { A = x.Vertices[0].StartMarker, V0 = x.Vertices[2].StartMarker - x.Vertices[0].StartMarker, V1 = x.Vertices[1].StartMarker - x.Vertices[0].StartMarker }).ToArray(); } else { sourceTriangles = triangleMarkerSet.Triangles.Select(x => new SourceTriangle() { A = x.Vertices[0].EndMarker, V0 = x.Vertices[2].EndMarker - x.Vertices[0].EndMarker, V1 = x.Vertices[1].EndMarker - x.Vertices[0].EndMarker }).ToArray(); } // process pixel wise double xStep = 1.0 / outputImage.Width; Parallel.For(0, outputImage.Height, yi => { Color* outputDataPixel = outputImage.Data + yi * outputImage.Width; Color* lastOutputDataPixel = outputDataPixel + outputImage.Width; double y = (double)yi / outputImage.Height; double u,v; double dot02, dot12; Vector v2; for (double x = 0; outputDataPixel != lastOutputDataPixel; x += xStep, ++outputDataPixel) { Vector position = new Vector(x, y); for (int triangleIdx = 0; triangleIdx < destTriangles.Length; ++triangleIdx) { // compute barycentric - http://www.blackpawn.com/texts/pointinpoly/ v2 = position - destTriangles[triangleIdx].A; // Compute dot products dot02 = VectorExtension.Dot(destTriangles[triangleIdx].V0, v2); dot12 = VectorExtension.Dot(destTriangles[triangleIdx].V1, v2); // Compute barycentric coordinates u = (destTriangles[triangleIdx].V1DotV1_divDenom * dot02 - destTriangles[triangleIdx].V0DotV1_divDenom * dot12); v = (destTriangles[triangleIdx].V0DotV0_divDenom * dot12 - destTriangles[triangleIdx].V0DotV1_divDenom * dot02); // Check if point is in triangle if ((u >= 0) && (v >= 0) && (u + v < 1)) { position = sourceTriangles[triangleIdx].A + sourceTriangles[triangleIdx].V0 * u + sourceTriangles[triangleIdx].V1 * v; break; } } position = position.ClampToImageArea(); *outputDataPixel = inputImage.Sample(position.X, position.Y); } }); }