Files
better-genshin-impact/Test/BetterGenshinImpact.Test/Dataset/AvatarClassifyTransparentGen.cs
2025-02-23 22:03:52 +08:00

202 lines
9.9 KiB
C#

using System.Diagnostics;
using System.IO;
using OpenCvSharp;
namespace BetterGenshinImpact.Test.Dataset;
public class AvatarClassifyTransparentGen
{
// 基础图像文件夹
private const string BaseDir = @"E:\HuiTask\更好的原神\自动秘境\自动战斗\队伍识别\分类器\";
// 背景图像文件夹
private static readonly string BackgroundDir = Path.Combine(BaseDir, "background");
private static readonly Random Rd = new Random();
// 在类的开头添加一个新的常量
private const string SideSrcDir = "side_src";
private const string SideSrcTransportDir = "side_src_transport";
public static void GenAll()
{
// 调整透明度(可选,取消注释以启用)
AdjustTransparency(0.5f);
// 读取基础图像
// List<string> sideImageFiles = Directory.GetFiles(Path.Combine(BaseDir, SideSrcTransportDir), "*.png", SearchOption.TopDirectoryOnly).ToList();
// 只用一个图像
List<string> sideImageFiles = Directory.GetFiles(Path.Combine(BaseDir, SideSrcTransportDir), "UI_AvatarIcon_Side_HutaoCostumeWinter.png", SearchOption.TopDirectoryOnly).ToList();
List<string> sideImageFiles2 = Directory.GetFiles(Path.Combine(BaseDir, SideSrcTransportDir), "UI_AvatarIcon_Side_Iansan.png", SearchOption.TopDirectoryOnly).ToList();
sideImageFiles.AddRange(sideImageFiles2);
List<string> sideImageFiles3 = Directory.GetFiles(Path.Combine(BaseDir, SideSrcTransportDir), "UI_AvatarIcon_Side_Citlali.png", SearchOption.TopDirectoryOnly).ToList();
sideImageFiles.AddRange(sideImageFiles3);
List<string> sideImageFiles4 = Directory.GetFiles(Path.Combine(BaseDir, SideSrcTransportDir), "UI_AvatarIcon_Side_Lanyan.png", SearchOption.TopDirectoryOnly).ToList();
sideImageFiles.AddRange(sideImageFiles4);
List<string> sideImageFiles5 = Directory.GetFiles(Path.Combine(BaseDir, SideSrcTransportDir), "UI_AvatarIcon_Side_XianglingCostumeWinter.png", SearchOption.TopDirectoryOnly).ToList();
sideImageFiles.AddRange(sideImageFiles5);
List<string> sideImageFiles6 = Directory.GetFiles(Path.Combine(BaseDir, SideSrcTransportDir), "UI_AvatarIcon_Side_Mavuika.png", SearchOption.TopDirectoryOnly).ToList();
sideImageFiles.AddRange(sideImageFiles6);
// 生成训练集
GenTo(sideImageFiles, Path.Combine(BaseDir, @"dateset\train"), 200);
// 生成测试集
GenTo(sideImageFiles, Path.Combine(BaseDir, @"dateset\test"), 40);
// GenTo(new List<string> { sideImageFiles[1] }, Path.Combine(BaseDir, @"dateset\test"), 1);
}
static void GenTo(List<string> sideImageFiles, string dataFolder, int count)
{
// 保留区域 从底边中心为固定点
var reservedSize = new Size(60, 80);
Directory.CreateDirectory(dataFolder);
// 循环生成每个基础图像对应的数据集
foreach (string sideImageFile in sideImageFiles)
{
// 获取基础图像文件名
string sideImageFileName = Path.GetFileNameWithoutExtension(sideImageFile);
sideImageFileName = sideImageFileName.Replace("UI_AvatarIcon_Side_", "");
// 创建基础图像对应的数据集文件夹
string sideDataFolder = Path.Combine(dataFolder, sideImageFileName);
Directory.CreateDirectory(sideDataFolder);
// 读取基础图像
Mat sideImageSrc = Cv2.ImRead(sideImageFile, ImreadModes.Unchanged);
var channels = sideImageSrc.Split();
var alphaChannel = channels[3]; // 透明通道
for (int i = 0; i < 3; i++)
{
Cv2.Multiply(channels[i], alphaChannel, channels[i], 1 / 255.0);
}
var sideImage = new Mat();
Cv2.Merge(channels[..3], sideImage);
// Cv2.ImShow("avatar", sideImage);
// 循环生成图像
for (int i = 0; i < count; i++)
{
// 随机挑选一张背景图像
string backgroundImageFile = Path.Combine(BackgroundDir, Directory.GetFiles(BackgroundDir, "*.png")[Rd.Next(Directory.GetFiles(BackgroundDir, "*.png").Length)]);
// 从背景图像中随机取一块 128x128 的区域
Mat backgroundImage = Cv2.ImRead(backgroundImageFile, ImreadModes.Color);
Rect backgroundRect = new Rect(Rd.Next(backgroundImage.Width - 128), new Random().Next(backgroundImage.Height - 128), 128, 128);
Mat backgroundImageRegion = backgroundImage[backgroundRect];
// 随机平移、缩放保留区域
float scale = (float)(Rd.NextDouble() * (1.6 - 0.7) + 0.7);
int w = (int)(sideImage.Width * scale);
int h = (int)(sideImage.Height * scale);
Debug.WriteLine($"{sideImageFileName} 生成随机缩放{scale}");
// 把保留区域合成到背景图像上
Mat backgroundImageRegionClone = backgroundImageRegion.Clone();
var resizedSideImage = new Mat();
Cv2.Resize(sideImage, resizedSideImage, new Size(128 * scale, 128 * scale));
// Cv2.ImShow("resizedSideImage", resizedSideImage);
var resizedMaskImage = new Mat();
// Cv2.Threshold(alphaChannel, alphaChannel, 200, 255, ThresholdTypes.Otsu);
Cv2.Resize(255 - alphaChannel, resizedMaskImage, new Size(128 * scale, 128 * scale), 0, 0, InterpolationFlags.Cubic);
var resizedAlphaChannel = new Mat();
Cv2.Resize(alphaChannel, resizedAlphaChannel, new Size(128 * scale, 128 * scale), 0, 0, InterpolationFlags.Cubic);
// Cv2.ImShow("resizedMaskImage", resizedMaskImage);
// generatedImage[transformedRect] = resizedSideImage;
Mat result;
if (scale > 1)
{
int xSpace1 = (int)((128 - reservedSize.Width * scale) / 2.0);
int ySpace1 = (int)(128 - reservedSize.Height * scale);
int xSpace2 = (int)((resizedSideImage.Width - 128) / 2.0);
int ySpace2 = resizedSideImage.Height - 128;
int xSpace = Math.Min(xSpace1, xSpace2);
int ySpace = Math.Min(ySpace1, ySpace2);
int offsetX = Rd.Next(-xSpace, xSpace);
int offsetY = Rd.Next(-ySpace, 0);
Debug.WriteLine($"{sideImageFileName} 缩放{scale}大于1 偏移 ({offsetX},{offsetY})");
var roi = new Rect((resizedSideImage.Width - 128) / 2 + offsetX, (resizedSideImage.Height - 128) + offsetY, 128, 128);
// result = new Mat();
// Cv2.BitwiseAnd(backgroundImageRegionClone, backgroundImageRegionClone, result, resizedMaskImage[roi]);
result = Mul(backgroundImageRegionClone, resizedAlphaChannel[roi]);
Cv2.Add(result, resizedSideImage[roi], result);
}
else
{
int xSpace = (128 - w) / 2;
int ySpace = 128 - h;
int offsetX = Rd.Next(-xSpace, xSpace);
int offsetY = Rd.Next(-ySpace, 0);
Debug.WriteLine($"{sideImageFileName} 缩放{scale}小于等于1 偏移 ({offsetX},{offsetY})");
var roi = new Rect((128 - resizedSideImage.Width) / 2 + offsetX, (128 - resizedSideImage.Height) + offsetY, resizedSideImage.Width, resizedSideImage.Height);
var res = new Mat();
// Cv2.BitwiseAnd(backgroundImageRegionClone[roi], backgroundImageRegionClone[roi], res, resizedMaskImage);
res = Mul(backgroundImageRegionClone[roi], resizedAlphaChannel);
Cv2.Add(res, resizedSideImage, res);
backgroundImageRegionClone[roi] = res;
result = backgroundImageRegionClone.Clone();
}
// Cv2.ImShow("avatarR", result);
// 保存生成的图像
Cv2.ImWrite(Path.Combine(sideDataFolder, $"{sideImageFileName}_t_{i}.png"), result);
}
}
static Mat Mul(Mat background, Mat alphaChannel)
{
var channels = background.Split();
for (int i = 0; i < 3; i++)
{
Cv2.Multiply(channels[i], 255 - alphaChannel, channels[i], 1 / 255.0);
}
Mat result = new Mat();
Cv2.Merge(channels[..3], result);
return result;
}
}
// 在类中添加以下新方法
public static void AdjustTransparency(float targetAlpha = 0.5f)
{
string sourcePath = Path.Combine(BaseDir, SideSrcDir);
string destinationPath = Path.Combine(BaseDir, SideSrcTransportDir);
Directory.CreateDirectory(destinationPath);
List<string> sideImageFiles = Directory.GetFiles(sourcePath, "*.png", SearchOption.TopDirectoryOnly).ToList();
foreach (string sideImageFile in sideImageFiles)
{
string fileName = Path.GetFileName(sideImageFile);
string destFile = Path.Combine(destinationPath, fileName);
Mat image = Cv2.ImRead(sideImageFile, ImreadModes.Unchanged);
Mat[] channels = Cv2.Split(image);
if (channels.Length == 4) // 确保图像有透明通道
{
Mat alphaChannel = channels[3];
Cv2.Multiply(alphaChannel, targetAlpha, alphaChannel);
Mat result = new Mat();
Cv2.Merge(channels, result);
Cv2.ImWrite(destFile, result);
}
else
{
Console.WriteLine($"警告:{fileName} 没有透明通道,将直接复制原文件。");
File.Copy(sideImageFile, destFile, true);
}
}
Console.WriteLine($"已完成透明度调整,结果保存在 {destinationPath}");
}
}