这就是我一直试图解决的问题。鉴于:
- a) 产品图片。预计全彩
- b) 该图像的掩码:RGBA(0,0,0,0) 表示忽略,RGBA(255,255,255,255) 表示替换
- c) 合成图像:这是与蒙版合成的
想法是将蒙版与合成图像合成,这将导致所有白色像素成为合成像素,但透明像素保持透明。这最终覆盖在产品图像的顶部,仅有效地转换了遮罩区域中的像素。
我有这个完美的工作,除了一个小问题。每次通过我的 Composite 函数似乎都会将输出缩小 0.5 倍。
我的解决方案中有一些代码,所以我会发布我认为必要的内容,但请随时提出更多要求。
这是我的复合方法:
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using OpenTK;
using OpenTK.Graphics;
using OpenTK.Graphics.OpenGL;
using OpenTK.Platform;
using EnableCap = OpenTK.Graphics.OpenGL.EnableCap;
using GL = OpenTK.Graphics.OpenGL.GL;
using HintMode = OpenTK.Graphics.OpenGL.HintMode;
using HintTarget = OpenTK.Graphics.OpenGL.HintTarget;
using MatrixMode = OpenTK.Graphics.OpenGL.MatrixMode;
using PixelFormat = System.Drawing.Imaging.PixelFormat;
using PixelType = OpenTK.Graphics.OpenGL.PixelType;
using TextureUnit = OpenTK.Graphics.OpenGL.TextureUnit;
using Utilities = OpenTK.Platform.Utilities;
namespace SpriteSheetMaker
{
public static class ImageBlender
{
static DebugLogger logger = DebugLogger.GetInstance(@"debug.txt");
//Mask should be white where replacing, and transparent elsewhere
public static TexturedPolygon Composite(TexturedPolygon __baseImage, TextureBlendItem item, int level = 1)
{
var oldScale = OpenGLHelpers.Scale.Clone();
var oldSize = OpenGLHelpers.Canvas.Size;
var newSize = __baseImage.Texture2D.Texture2D.Size;
logger.WriteLine("Composite Requested. Mask Details: " + __baseImage.Rotation + " --- " + __baseImage.Scale + " --- " + __baseImage.Translation);
OpenGLHelpers.ClearScreen();
var _baseImage = __baseImage.Clone();
_baseImage.Texture2D.Texture2D.Save("composite_test_base" + level + ".png");
var _mask = item.Mask.Clone();
var _composite = item.Composite.Clone();
logger.WriteLine("Composite Requested. Mask Details: " + __baseImage.Rotation + " --- " + __baseImage.Scale + " --- " + __baseImage.Translation);
_mask.ResetTransform();
_composite.ResetTransform();
_baseImage.ResetTransform();
GL.Enable(EnableCap.Blend);
// render the mask
_mask.Draw();
//Blend the composite
GL.Disable(EnableCap.Blend);
GL.Enable(EnableCap.Blend);
GL.BlendEquation(BlendEquationMode.Min);
_composite.Draw();
GL.Disable(EnableCap.Blend); ;
//Not sure exactly what this does
GL.Enable(EnableCap.Blend);
GL.BlendEquation(BlendEquationMode.FuncAdd);
//Grab the composite and save it into a variable, because we are clearing the screen now
var bmp = OpenGLHelpers.GrabScreenshot(oldSize);
var _composite_2 = new BaseTextureImage(bmp).GetDrawable();
_composite_2.ResetTransform();
//Now we have the composited mask, we can simply draw it over the original image
OpenGLHelpers.ClearScreen();
_baseImage.Draw();
_composite_2.Draw();
bmp = OpenGLHelpers.GrabScreenshot(oldSize);
// OpenGLHelpers.Canvas.Resize(oldSize);
return new BaseTextureImage(bmp).GetDrawable();
}
public static TexturedPolygon Composite(TexturedPolygon _baseImage, List<TextureBlendItem> blends)
{
TexturedPolygon rtn = _baseImage.Clone();
bool doScaleFix = true;
int idx = 0;
foreach(var blend in blends)
{
var c = Composite(rtn, blend, (idx+1));
c.Texture2D.Texture2D.Save("composite_test_" + idx + ".png");
c.Scale = _baseImage.Scale;
c.Rotation = _baseImage.Rotation;
c.Translation = _baseImage.Translation;
rtn = c;
doScaleFix = false;
idx++;
}
return rtn;
}
}
}
这是 TexturedPolygon 类
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using OpenTK;
using OpenTK.Graphics;
using OpenTK.Graphics.OpenGL;
using OpenTK.Platform;
using EnableCap = OpenTK.Graphics.OpenGL.EnableCap;
using GL = OpenTK.Graphics.OpenGL.GL;
using HintMode = OpenTK.Graphics.OpenGL.HintMode;
using HintTarget = OpenTK.Graphics.OpenGL.HintTarget;
using MatrixMode = OpenTK.Graphics.OpenGL.MatrixMode;
using PixelFormat = System.Drawing.Imaging.PixelFormat;
using PixelType = OpenTK.Graphics.OpenGL.PixelType;
using TextureUnit = OpenTK.Graphics.OpenGL.TextureUnit;
using Utilities = OpenTK.Platform.Utilities;
using System.Drawing.Imaging;
using System.Reflection;
namespace SpriteSheetMaker
{
public class TexturedPolygon : BasePolygon
{
public BaseTextureImage Texture2D { get; set; }
public TexturedPolygon(BaseTextureImage texture, List<Vector3> pts) : base(pts)
{
Texture2D = texture;
}
public new TexturedPolygon Clone()
{
TexturedPolygon polygon = new TexturedPolygon(Texture2D, Vertices);
polygon.FillColor = this.FillColor.Clone();
polygon.EdgeColor = this.EdgeColor.Clone();
polygon.EdgeWidth = this.EdgeWidth;
polygon.Translation = this.Translation.Clone();
polygon.Rotation = this.Rotation.Clone();
polygon.Scale = this.Scale.Clone();
return polygon;
}
public TexturedPolygon(List<Vector3> pts) : base(pts)
{
}
public override void _draw()
{
if (Texture2D == null)
{
GL.ClearColor(Color.Transparent);
GL.Enable(EnableCap.Blend);
GL.BlendFunc(BlendingFactorSrc.SrcAlpha, BlendingFactorDest.OneMinusSrcAlpha);
this._basePolygonDraw();
return;
}
var bb = NoTransformBoundingBox();
GL.ClearColor(Color.Transparent);
GL.BindTexture(TextureTarget.Texture2D, Texture2D.TextureID);
GL.Enable(EnableCap.Texture2D);
GL.Enable(EnableCap.Blend);
GL.BlendFunc(BlendingFactorSrc.SrcAlpha, BlendingFactorDest.OneMinusSrcAlpha);
GL.Begin(PrimitiveType.Polygon);
for(int i = 0; i < Vertices.Count; i++)
{
var pt = Vertices[i];
var fillColor = GetVertexFillColor(i);
var alpha = fillColor.Alpha;
GL.Color4(1.0, 1.0, 1.0, alpha);
var texX = (pt.X - (float)bb.Left) / (bb.Right - (float)bb.Left);
var texY = (pt.Y - (float)bb.Top) / (bb.Bottom - (float)bb.Top);
GL.TexCoord2(texX, texY);
GL.Vertex2(pt.X, pt.Y);
}
GL.End();
// GL.Disable(EnableCap.Texture2D);
}
}
}
这是 BasePolygon 类:
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using OpenTK;
using OpenTK.Graphics;
using OpenTK.Graphics.OpenGL;
using OpenTK.Platform;
using EnableCap = OpenTK.Graphics.OpenGL.EnableCap;
using GL = OpenTK.Graphics.OpenGL.GL;
using HintMode = OpenTK.Graphics.OpenGL.HintMode;
using HintTarget = OpenTK.Graphics.OpenGL.HintTarget;
using MatrixMode = OpenTK.Graphics.OpenGL.MatrixMode;
using PixelFormat = System.Drawing.Imaging.PixelFormat;
using PixelType = OpenTK.Graphics.OpenGL.PixelType;
using TextureUnit = OpenTK.Graphics.OpenGL.TextureUnit;
using Utilities = OpenTK.Platform.Utilities;
namespace SpriteSheetMaker
{
public class BasePolygon : BaseTexture
{
protected List<Vector3> Vertices = new List<Vector3>();
public GeometryColor FillColor = new GeometryColor();
public GeometryColor EdgeColor = new GeometryColor();
public float EdgeWidth = 1.0f;
public List<Vector3> CurrentVertices
{
get
{
return Vertices.ToList();
}
}
Point[] _points
{
get
{
return Vertices.Select(x => x.ToPoint()).ToArray();
}
}
protected override List<Vector3> GetPolygonPoints()
{
List<Vector3> rtn = new List<Vector3>();
foreach(var pt in Vertices)
{
rtn.Add(pt.Clone());
}
return rtn;
}
protected void AlignToOrigin()
{
var ctr = NoTransformCenter();
for(int i = 0; i < Vertices.Count; i++)
{
var pt = Vertices[i];
pt = pt.Subtract(ctr);
Vertices[i] = pt;
}
Translation = ctr.Clone();
}
protected void AlignToCenter()
{
var ctr = NoTransformCenter();
for (int i = 0; i < Vertices.Count; i++)
{
var pt = Vertices[i];
pt = pt.Add(Translation);
Vertices[i] = pt;
}
Translation = Vector3.ZERO;
}
public static BasePolygon ClonePolygon(BasePolygon poly)
{
return poly.Clone();
}
public BasePolygon Clone()
{
BasePolygon polygon = new BasePolygon();
polygon.Vertices = this.CurrentVertices;
polygon.FillColor = this.FillColor.Clone();
polygon.EdgeColor = this.EdgeColor.Clone();
polygon.EdgeWidth = this.EdgeWidth;
polygon.Translation = this.Translation.Clone();
polygon.Rotation = this.Rotation.Clone();
polygon.Scale = this.Scale.Clone();
return polygon;
}
public BasePolygon(List<Vector3> pts)
{
AddVertexes(pts);
AlignToOrigin();
}
public BasePolygon(params Vector3[] pts)
{
AddVertexes(pts.ToList());
AlignToOrigin();
}
protected void AddVertex(Vector3 pt)
{
Vertices.Add(pt.Clone());
}
protected void AddVertexes(IEnumerable<Vector3> pts)
{
pts.ToList().ForEach(x =>
{
AddVertex(x);
});
}
protected void RemoveVertex(Vector3 pt)
{
Vertices.Remove(pt);
}
protected void RemoveVertexes(IEnumerable<Vector3> pts)
{
pts.ToList().ForEach(x =>
{
RemoveVertex(x);
});
}
public override void _draw()
{
this._basePolygonDraw();
}
public ColorLibrary.sRGB GetVertexFillColor(int index)
{
var pct = ((float)index + 1.0f) / (float)Vertices.Count;
var colorIdx = (int)((FillColor.Colors.Count - 1.0f) * pct);
return FillColor.Colors[colorIdx];
}
public ColorLibrary.sRGB GetVertexEdgeColor(int index)
{
var pct = ((float)index + 1.0f) / (float)Vertices.Count;
var colorIdx = (int)Math.Round((float)(EdgeColor.Colors.Count - 1.0f) * pct);
return EdgeColor.Colors[colorIdx];
}
protected void _basePolygonDraw()
{
this.wireframe();
this.fill();
}
private void wireframe()
{
GL.BindTexture(TextureTarget.Texture2D, 0);
GL.Hint(HintTarget.LineSmoothHint, HintMode.Nicest);
GL.Hint(HintTarget.PolygonSmoothHint, HintMode.Nicest);
var pts = Vertices;
ColorLibrary.sRGB color1;
ColorLibrary.sRGB color2;
GL.Begin(PrimitiveType.Lines);
for (var i = 0; i < pts.Count; i++)
{
var idx2 = (i + 1) % pts.Count;
color1 = GetVertexEdgeColor(i);
color2 = GetVertexEdgeColor(idx2);
var a = pts[i];
var b = pts[idx2];
GL.Color4(color1.R, color1.G, color1.B, color1.Alpha);
GL.Vertex3(a.OpenTKVector);
GL.Color4(color2.R, color2.G, color2.B, color2.Alpha);
GL.Vertex3(b.OpenTKVector);
}
GL.End();
}
private void fill()
{
GL.Hint(HintTarget.LineSmoothHint, HintMode.Nicest);
GL.Hint(HintTarget.PolygonSmoothHint, HintMode.Nicest);
GL.BindTexture(TextureTarget.Texture2D, 0);
var pts = Vertices;
ColorLibrary.sRGB color;
GL.Begin(PrimitiveType.Polygon);
for(int i = 0; i < pts.Count; i++)
{
color = GetVertexFillColor(i);
var pt = pts[i];
GL.Color4(color.R, color.G, color.B, color.Alpha);
GL.Vertex3(pt.OpenTKVector);
}
GL.End();
}
}
}
这是 BaseTexture 类
using ....
namespace SpriteSheetMaker
{
public abstract class BaseTexture
{
protected static Random rnd = new Random();
public Vector3 Translation = new Vector3(0, 0, 0);
public Vector3 Scale = new Vector3(1, 1, 1);
public Vector3 Rotation = new Vector3(0, 0, 0);
public Vector3 Velocity = new Vector3(0, 0, 0);
public void ResetTransform()
{
Translation = Vector3.ZERO;
Scale = Vector3.ONE;
Rotation = Vector3.ZERO;
}
private Guid _guid = Guid.NewGuid();
public Guid Guid
{
get
{
return _guid;
}
private set
{
_guid = value;
}
}
public bool DrawBounds = false;
public enum CollisionDirection
{
None = 0,
Up = 1,
Right = 2,
Down = 3,
Left = 4
}
public RectangleF NoTransformBoundingBox()
{
var poly = GetPolygonPoints();
var rect = Utility.PointsToRectangle(poly);
return rect;
}
protected abstract List<Vector3> GetPolygonPoints();
public Vector3 NoTransformCenter()
{
var poly = GetPolygonPoints();
var ctr = Vector3.Average(poly);
return ctr;
}
protected virtual RectangleF NonRotatedBoundingBox()
{
var polyPts = GetPolygonPoints();
foreach (var p in polyPts)
{
p.X = (Translation.X + Scale.X * p.X);
p.Y = (Translation.Y + Scale.Y * p.Y);
}
var rect = Utility.PointsToRectangle(polyPts);
return rect;
}
public RectangleF BoundingBox()
{
if(this.Rotation.Z == 0)
{
return NonRotatedBoundingBox();
}
return RotatedBoundingBox();
}
private RectangleF RotatedBoundingBox()
{
var polyPts = GetPolygonPoints();
foreach(var p in polyPts)
{
p.X = Translation.X + Scale.X*p.X;
p.Y = Translation.Y + Scale.Y*p.Y;
}
var ctr = this.Center();
var theta = this.Rotation.Z;
var ptsRotated = Utility.RotatePoints(polyPts, ctr, theta);
var bb = Utility.GetBoundsFromPoints(ptsRotated);
var rotated = new RectangleF(bb.Left, bb.Top, bb.Width, bb.Height);
return rotated;
}
public bool IsColliding(BaseTexture tex)
{
var bb = this.BoundingBox();
var bb2 = tex.BoundingBox();
return RectangleF.Intersect(bb, bb2) != RectangleF.Empty;
}
public CollisionDirection CollisionSide(BaseTexture tex)
{
var isColliding = IsColliding(tex);
if (!isColliding)
{
return CollisionDirection.None;
}
var ctrThis = this.Center();
var ctrThat = tex.Center();
var angleTo = ctrThis.AngleTo(ctrThat);
var pi = Math.PI;
var two_pi = 2*pi;
var pi_over_2 = pi/2.0;
var pi_over_4 = pi/4.0;
//0 to 45
if (angleTo >= 0 && angleTo <= pi_over_4)
{
return CollisionDirection.Right;
}
//45 to 135
else if (angleTo >= pi_over_4 && angleTo <= (pi_over_4) + (pi_over_2))
{
return CollisionDirection.Down;
}
//135 to 225
else if (angleTo >= (pi_over_4) + (pi_over_2) && angleTo <= (pi) + (pi_over_4))
{
return CollisionDirection.Left;
}
//225 to 315
else if (angleTo >= (pi) + (pi_over_4) && angleTo <= two_pi - pi_over_4)
{
return CollisionDirection.Up;
}
//315 to 360
else if (angleTo >= two_pi - pi_over_4 && angleTo <= two_pi)
{
return CollisionDirection.Right;
}
else
{
return CollisionDirection.None;
}
}
public Vector3 Center()
{
return NoTransformCenter().Add(this.Translation);
}
public float NoTransformRadius
{
get
{
var bb = NoTransformBoundingBox();
return (float)(Math.Max(bb.Width, bb.Height));
}
}
public float Radius
{
get
{
var bb = BoundingBox();
return (float)(Math.Max(bb.Width, bb.Height));
}
}
public abstract void _draw();
protected void _draw_bounds()
{
var bb = BoundingBox();
var ctr = Utility.RectangleCenter(bb);
var ctr2 = this.Center();
RectanglePolygon rec = RectanglePolygon.Create(ctr, bb.Width, bb.Height);
var fillColor = new ColorLibrary.sRGB(1,1,1);
fillColor.Alpha = 0.0;
rec.FillColor.SetSolid(fillColor);
rec.EdgeColor.SetSolid(new ColorLibrary.sRGB(1,0,0));
rec.Draw();
}
public virtual void Draw()
{
GL.ClearColor(Color.Transparent);
if (this.DrawBounds)
{
this._draw_bounds();
}
var ctr = NoTransformCenter();
OpenGLHelpers.RotationPivot = ctr;
OpenGLHelpers.AddTransform(Rotation,Scale,Translation);
OpenGLHelpers.ApplyTransforms();
_draw();
OpenGLHelpers.SubtractTransform(Rotation,Scale,Translation);
GL.PopMatrix();
// GL.Flush();
}
public override bool Equals(object obj)
{
var o = obj as BaseTexture;
return o.Guid == Guid;
}
}
}
如果您需要更多代码,例如 OpenGLHelpers 内部的方法,我可以提供。请记住,通常我的绘图代码有效。我没有在这里设置任何投影,所以这就是我重置变换的原因。无论如何,平移和旋转现在都为零,因此实际上只是重置比例。我的基本绘图方法从 [0,1] 标准化,并且比例是将图像拉伸到所需的任何尺寸。
此外,这就是我所说的规模变得混乱的意思。那个红色的烧瓶形状应该是完全覆盖了烧瓶的内部,而且,烧瓶本身也被缩小了!所以它缩水了一倍...
这显示了通过 4 次通过的迭代。笑脸是添加的另一个面具/复合组合
感谢任何帮助的人