Reputation: 856
As I said, I have a mesh that I decided to redo the UVs on, and in order to save time, I've stored the original UVs by face, as well as the new UVs to separate files. The code I have successfully transfers and places the detail from the old texture to the new one, but due to the fact that pixels don't perfectly fit on the edge of a triangle, the result has a lot of seams.
I'd be grateful if someone could help by increasing the size of the captured/placed triangle by a couple of pixels to hopefully eliminate these seams:
program.cs:
using System.Drawing;
using Emgu.CV;
using Emgu.CV.CvEnum;
using Emgu.CV.Structure;
using Emgu.CV.Util;
namespace texgen
{
internal class Program
{
static void Main()
{
// Paths to the binary files and images
string originalUVFilePath = @"E:\uvbackup.bin";
string newUVFilePath = @"E:\uvnewbackup.bin";
string originalImagePath = @"E:\Ship_Mat_Fed_Galaxy_2019_Type1_D.bmp";
string newImagePath = @"E:\output_image.bmp";
// Read and normalize the UVW coordinates
UVWCoordinates[] originalUVWs = UVWReader.ReadAndNormalizeUVWCoordinates(originalUVFilePath);
UVWCoordinates[] newUVWs = UVWReader.ReadAndNormalizeUVWCoordinates(newUVFilePath);
// Load the original image
Mat originalImage = CvInvoke.Imread(originalImagePath, ImreadModes.Color);
// Create a new blank image
Mat newImage = new Mat(originalImage.Size, DepthType.Cv8U, 3);
newImage.SetTo(new MCvScalar(0));
// Map the texture
UVWMapper.MapTexture(originalImage, originalUVWs, newUVWs, newImage);
// Save the new image
CvInvoke.Imwrite(newImagePath, newImage);
Console.WriteLine("Processing done. Check the new image.");
}
}
}
UVWReader.cs:
using System.IO;
using System.Collections.Generic;
using System.Numerics;
public struct UVWCoordinates
{
public int FaceIndex;
public Vector3 UVW1;
public Vector3 UVW2;
public Vector3 UVW3;
}
public class UVWReader
{
public static UVWCoordinates[] ReadAndNormalizeUVWCoordinates(string filePath)
{
var coordinatesList = new List<UVWCoordinates>();
using (var reader = new BinaryReader(File.Open(filePath, FileMode.Open)))
{
while (reader.BaseStream.Position != reader.BaseStream.Length)
{
var coord = new UVWCoordinates
{
FaceIndex = reader.ReadInt32(),
UVW1 = NormalizeUVW(new Vector3((float)reader.ReadDouble(), (float)reader.ReadDouble(), (float)reader.ReadDouble())),
UVW2 = NormalizeUVW(new Vector3((float)reader.ReadDouble(), (float)reader.ReadDouble(), (float)reader.ReadDouble())),
UVW3 = NormalizeUVW(new Vector3((float)reader.ReadDouble(), (float)reader.ReadDouble(), (float)reader.ReadDouble()))
};
coordinatesList.Add(coord);
}
}
return coordinatesList.ToArray();
}
private static Vector3 NormalizeUVW(Vector3 uvw)
{
return new Vector3(uvw.X - (float)Math.Floor(uvw.X), uvw.Y - (float)Math.Floor(uvw.Y), uvw.Z);
}
}
UVWMapper.cs:
using Emgu.CV.CvEnum;
using Emgu.CV.Structure;
using Emgu.CV.Util;
using System.Numerics;
using System.Drawing;
public class UVWMapper
{
public static void MapTexture(
Mat originalImage,
UVWCoordinates[] originalUVWs,
UVWCoordinates[] newUVWs,
Mat newImage)
{
for (int i = 0; i < originalUVWs.Length; i++)
{
var origUVW = originalUVWs[i];
var newUVW = newUVWs[i];
// Define the source points (original UVs)
PointF[] srcPoints = new PointF[]
{
new PointF(origUVW.UVW1.X * originalImage.Width, (1 - origUVW.UVW1.Y) * originalImage.Height),
new PointF(origUVW.UVW2.X * originalImage.Width, (1 - origUVW.UVW2.Y) * originalImage.Height),
new PointF(origUVW.UVW3.X * originalImage.Width, (1 - origUVW.UVW3.Y) * originalImage.Height)
};
// Define the destination points (new UVs)
PointF[] dstPoints = new PointF[]
{
new PointF(newUVW.UVW1.X * newImage.Width, (1 - newUVW.UVW1.Y) * newImage.Height),
new PointF(newUVW.UVW2.X * newImage.Width, (1 - newUVW.UVW2.Y) * newImage.Height),
new PointF(newUVW.UVW3.X * newImage.Width, (1 - newUVW.UVW3.Y) * newImage.Height)
};
// Get the transformation matrix
Mat transformationMatrix = CvInvoke.GetAffineTransform(srcPoints, dstPoints);
// Extract the triangle from the original image
Mat originalTriangle = ExtractTriangle(originalImage, srcPoints);
// Warp the triangle to the new coordinates
Mat warpedTriangle = new Mat();
CvInvoke.WarpAffine(originalTriangle, warpedTriangle, transformationMatrix, newImage.Size, Inter.Linear, Warp.Default, BorderType.Constant, new MCvScalar(0));
// Add the warped triangle to the new image
CvInvoke.Add(newImage, warpedTriangle, newImage);
}
}
private static Mat ExtractTriangle(Mat image, PointF[] srcPoints)
{
// Create a mask for the triangular area
Mat mask = new Mat(image.Size, DepthType.Cv8U, 1);
mask.SetTo(new MCvScalar(0));
VectorOfPoint triangle = new VectorOfPoint(Array.ConvertAll(srcPoints, p => new Point((int)p.X, (int)p.Y)));
CvInvoke.FillConvexPoly(mask, triangle, new MCvScalar(255));
// Extract the triangular portion from the image
Mat triangularPortion = new Mat();
CvInvoke.BitwiseAnd(image, image, triangularPortion, mask);
return triangularPortion;
}
}
Upvotes: 0
Views: 28