0

So here's the problem I've been trying to solve. Given:

  • a) A product image. Expected to be full color
  • b) A mask of that image: RGBA(0,0,0,0) means ignore, RGBA(255,255,255,255) means replace
  • c) A composite image: This is composited with the mask

The idea is to composite the mask with the composite image, which will result in all white pixels becoming the composite pixels, but the transparent pixels remain transparent. This finally gets overlayed on top of the product image, effectively transforming only the pixels in the mask region.

I have this working perfectly, except for one small problem. Every pass through my Composite function seems to shrink the output by a scale of 0.5.

There's a bit of code in my solution, so I'll post what I think is necessary but feel free to ask for more.

Here's my Composite methods:

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;
    }



    }
}

Here is the TexturedPolygon class

            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);
                    }
                }
            }

Here is the BasePolygon class:

            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();


                    }

                }
            }

and here is the BaseTexture class

            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;
                    }

                }
            }

If you need any more code, like the methods inside of OpenGLHelpers, I can provide that. Just keep in mind that generally my drawing code works. I didn't set any projection here, so that's why I reset the transforms. The translation and rotation are zero for now anyway, so it's really just to reset the scale. My base drawing method is normalized from [0,1], and the scale is what stretches the image to whatever dimensions desired.

Also, here is what I mean by the scale gets messed up. That red flask looking shape is supposed to complete cover the inside of the flask, and also, the flask itself got shrunk down! So it double shrunk...

Composite Fail

That shows the iterations through 4 pass throughs. The smiley faces were another mask/composite combo added on

Base Image: Base Image

Mask 1: Mask 1

Composite 1: Composite 1

Thank you to whoever helps

applejacks01
  • 249
  • 1
  • 18
  • Ok I'm on to somethnig. So, all of my polygon classes ae centered around the origin. So I have {-0.5,-0.5}, { 0.5, -0.5}, { 0.5, 0.5 }, {-0.5, 0.5}. When I bound my polygons from 0 to 1, it pretty much works. I really want to keep my polygons in the -0.5 to 0.5 range though......... – applejacks01 Mar 05 '16 at 20:01

1 Answers1

0

I got it working!

So there were 2 problems.

  • I was using -0.5 to 0.5 as my domain range, when -1 to 1 was more appropriate
  • I also had miscellaneous issues, such as needing to make sure all of my textures were actually in the viewport, and to resize the glControl to fit the product texture

    Finally working

applejacks01
  • 249
  • 1
  • 18