当前位置:   article > 正文

Unity之合并多张图片为一张大图(二)_unity 多张序列帧合并1张

unity 多张序列帧合并1张

 

一、前言

继之前的测试代码写成了项目内工具。工具如下:

针对之前宽高遗留的问题,需要原始序列帧图片的宽高为2的次幂。然后根据宽高比、数量曲计算单测个数,最后得到宽高。

  1. //是否是宽图
  2. bool isHor = impWidth > impHeight;
  3. float ratio = isHor ? (float)impWidth / impHeight : (float)impHeight / impWidth;
  4. //根据数量计算单侧个数
  5. float oriCnt = Mathf.Sqrt(len / ratio);
  6. int littleCnt = Mathf.CeilToInt(oriCnt);
  7. int moreCnt = Mathf.CeilToInt(oriCnt * ratio);
  8. int wCnt = isHor ? littleCnt : moreCnt;
  9. //纵向个数
  10. int hCnt = isHor ? moreCnt : littleCnt;
  11. //单张高度
  12. int singleH = Mathf.FloorToInt(outHeight / hCnt);
  13. int singleW = Mathf.FloorToInt(outWidth / wCnt);

以下贴上具体代码:

  1. using System.Collections;
  2. using System.Collections.Generic;
  3. using UnityEditor;
  4. using UnityEngine;
  5. using System.IO;
  6. using System;
  7. using System.Threading;
  8. using System.Threading.Tasks;
  9. using System.Linq;
  10. public class MergeSequenceFrameImage : EditorWindow
  11. {
  12. enum EmImgFormat
  13. {
  14. PNG = 0,
  15. JPG = 1,
  16. TGA = 2
  17. }
  18. static string filePath = "";
  19. static string outPath = "";
  20. static int impWidth;
  21. static int impHeight;
  22. static int outSizeIndex = 0;
  23. static int outWidth = 4096;
  24. static int outHeight = 4096;
  25. public static readonly string[] enumNames = Enum.GetNames(typeof(EmImgFormat));
  26. public static readonly string[] enumSizes = new string[] {
  27. "32","64","128","256","512","1024","2048","4096","8192"
  28. };
  29. static List<Task> task = new List<Task>();
  30. static TaskFactory taskfactory = new TaskFactory();
  31. public void Init()
  32. {
  33. isMerge = false;
  34. minSize = new Vector2(500, 540);
  35. outSizeIndex = enumSizes.Length - 1;
  36. outWidth = outHeight = int.Parse(enumSizes[outSizeIndex]);
  37. }
  38. static EmImgFormat emImgFormat = EmImgFormat.JPG;
  39. static EmImgFormat emOutFormat = EmImgFormat.PNG;
  40. static string[] strImgFormat = new string[]
  41. {
  42. "*.png",
  43. "*.jpg",
  44. "*.tga"
  45. };
  46. static string importImgFormat = "*.jpg";
  47. GUIStyle filePathStyle;
  48. static int curProgress = 0;
  49. static int maxProgress = 1;
  50. static float progress = 0f;
  51. static string proDesc = "";
  52. static bool isMerge = false;
  53. private void OnGUI()
  54. {
  55. GUILayout.BeginVertical();
  56. GUILayout.Space(10.0f);
  57. GUILayout.Label("序列帧图片设置", new GUILayoutOption[] { GUILayout.Width(100) });
  58. GUILayout.Space(10.0f);
  59. GUILayout.BeginHorizontal();
  60. GUILayout.Label("序列帧图片路径:", new GUILayoutOption[] { GUILayout.Width(100) });
  61. filePathStyle = new GUIStyle(GUI.skin.GetStyle("TextField"));
  62. filePathStyle.alignment = TextAnchor.LowerLeft;
  63. GUILayout.TextField(filePath, filePathStyle, new GUILayoutOption[] { GUILayout.Width(330) });
  64. GUILayout.Space(10.0f);
  65. if (GUILayout.Button("浏览", new GUILayoutOption[] { GUILayout.Width(40.0f) }))
  66. {
  67. filePath = EditorUtility.OpenFolderPanel("选择序列帧图片目录", Application.dataPath, "");
  68. }
  69. GUILayout.EndHorizontal();
  70. //异常显示
  71. if (string.IsNullOrEmpty(filePath))
  72. {
  73. ShowRedTipsLab("请选择序列帧图片目录路径");
  74. }
  75. //图片格式
  76. GUILayout.Space(10.0f);
  77. GUILayout.BeginHorizontal();
  78. GUILayout.Label("序列帧图片格式:", new GUILayoutOption[] { GUILayout.Width(100) });
  79. EditorGUI.BeginChangeCheck();
  80. emImgFormat = (EmImgFormat)EditorGUILayout.Popup("",(int)emImgFormat, enumNames, new GUILayoutOption[] { GUILayout.Width(330) });
  81. if (EditorGUI.EndChangeCheck())
  82. {
  83. importImgFormat = strImgFormat[(int)emImgFormat];
  84. }
  85. GUILayout.EndHorizontal();
  86. //序列帧尺寸设置
  87. GUILayout.Space(10.0f);
  88. GUILayout.BeginHorizontal();
  89. GUILayout.Label("序列帧图片尺寸:", new GUILayoutOption[] { GUILayout.Width(100) });
  90. GUILayout.Label("Width", new GUILayoutOption[] { GUILayout.Width(40) });
  91. impWidth = EditorGUILayout.IntField(impWidth, new GUILayoutOption[] { GUILayout.Width(80) });
  92. GUILayout.Label("Height", new GUILayoutOption[] { GUILayout.Width(40) });
  93. impHeight = EditorGUILayout.IntField(impHeight, new GUILayoutOption[] { GUILayout.Width(80) });
  94. GUILayout.EndHorizontal();
  95. if (impHeight <= 0 || impWidth <= 0)
  96. {
  97. ShowRedTipsLab("请设置序列帧图片尺寸");
  98. }else if ((impHeight & (impHeight - 1)) != 0 || (impWidth & (impWidth - 1)) != 0)
  99. {
  100. ShowRedTipsLab("输入的尺寸不是2的次幂");
  101. }
  102. GUILayout.Space(10.0f);
  103. GUILayout.Label("注意:请确保宽高为2的次幂", new GUILayoutOption[] { GUILayout.Width(500) });
  104. //导出设置
  105. GUILayout.Space(30.0f);
  106. GUILayout.Label("导出图片设置", new GUILayoutOption[] { GUILayout.Width(100) });
  107. GUILayout.Space(10.0f);
  108. GUILayout.BeginHorizontal();
  109. GUILayout.Label("导出图片路径:", new GUILayoutOption[] { GUILayout.Width(100) });
  110. GUIStyle tryStyle = new GUIStyle(GUI.skin.GetStyle("TextField"));
  111. tryStyle.alignment = TextAnchor.LowerLeft;
  112. GUILayout.TextField(outPath, tryStyle, new GUILayoutOption[] { GUILayout.Width(330) });
  113. GUILayout.Space(10.0f);
  114. if (GUILayout.Button("浏览", new GUILayoutOption[] { GUILayout.Width(40.0f) }))
  115. {
  116. outPath = EditorUtility.OpenFolderPanel("选择要导出的目录", Application.dataPath, "");
  117. }
  118. GUILayout.EndHorizontal();
  119. if (string.IsNullOrEmpty(outPath))
  120. {
  121. ShowRedTipsLab("请选择要导出的目录路径");
  122. }
  123. //导出尺寸设置
  124. GUILayout.Space(10.0f);
  125. GUILayout.BeginHorizontal();
  126. GUILayout.Label("导出图片大小:", new GUILayoutOption[] { GUILayout.Width(100) });
  127. EditorGUI.BeginChangeCheck();
  128. outSizeIndex = EditorGUILayout.Popup("", outSizeIndex, enumSizes, new GUILayoutOption[] { GUILayout.Width(330) });
  129. if (EditorGUI.EndChangeCheck())
  130. {
  131. outWidth = int.Parse(enumSizes[outSizeIndex]);
  132. outHeight = outWidth;
  133. }
  134. GUILayout.EndHorizontal();
  135. //导出图片格式
  136. GUILayout.Space(10.0f);
  137. GUILayout.BeginHorizontal();
  138. GUILayout.Label("导出图片格式:", new GUILayoutOption[] { GUILayout.Width(100) });
  139. emOutFormat = (EmImgFormat)EditorGUILayout.Popup("", (int)emOutFormat, enumNames, new GUILayoutOption[] { GUILayout.Width(330) });
  140. GUILayout.EndHorizontal();
  141. GUILayout.Space(10.0f);
  142. //执行
  143. if (GUILayout.Button("执行合并", new GUILayoutOption[] { GUILayout.Width(120.0f) }) && !isMerge)
  144. {
  145. //先做一系列检测
  146. if (!CheckIsValid())
  147. {
  148. Debug.LogError("参数检查未通过!");
  149. return;
  150. }
  151. isMerge = true;
  152. StartMerge();
  153. }
  154. //GUILayout.Space(10.0f);
  155. 执行
  156. //if (GUILayout.Button("测试", new GUILayoutOption[] { GUILayout.Width(120.0f) }) && !isMerge)
  157. //{
  158. // filePath = "C:/Users/Administrator/Desktop/sucai";
  159. // outPath = Application.dataPath;
  160. // impWidth = 1024;impHeight = 512;
  161. // outWidth = outHeight = 512;
  162. //}
  163. //GUILayout.Space(10.0f);
  164. //GUILayout.BeginHorizontal();
  165. //GUILayout.Label("count", new GUILayoutOption[] { GUILayout.Width(40) });
  166. //cnt = EditorGUILayout.IntField(cnt, new GUILayoutOption[] { GUILayout.Width(80) });
  167. //GUILayout.EndHorizontal();
  168. GUILayout.EndVertical();
  169. }
  170. static void ShowRedTipsLab(string tips)
  171. {
  172. GUI.color = Color.red;
  173. GUILayout.Label(tips, new GUILayoutOption[] { GUILayout.Width(500) });
  174. GUI.color = Color.white;
  175. }
  176. static int cnt = 0;
  177. public static void StartMerge()
  178. {
  179. proDesc = "开始读取序列帧图片";
  180. EditorUtility.DisplayProgressBar(proDesc, "", progress);
  181. Debug.Log(filePath);
  182. DirectoryInfo folder = new DirectoryInfo(filePath);
  183. var files = folder.GetFiles(importImgFormat);
  184. maxProgress = files.Length;
  185. //maxProgress = cnt;//test
  186. Texture2D[] texture2Ds = new Texture2D[maxProgress];
  187. for (int i = 0; i < maxProgress; i++)
  188. {
  189. FileInfo file = files[i];
  190. FileStream fs = new FileStream(filePath + "/" + file.Name, FileMode.Open, FileAccess.Read);
  191. int byteLength = (int)fs.Length;
  192. byte[] imgBytes = new byte[byteLength];
  193. fs.Read(imgBytes, 0, byteLength);
  194. fs.Close();
  195. fs.Dispose();
  196. Texture2D t2d = new Texture2D(impWidth,impHeight);
  197. t2d.LoadImage(imgBytes);
  198. t2d.Apply();
  199. texture2Ds[i] = t2d;
  200. progress = (float)(i + 1) / maxProgress;
  201. EditorUtility.DisplayProgressBar(proDesc, file.Name, progress);
  202. }
  203. proDesc = "准备写入贴图";
  204. progress = 0f;
  205. EditorUtility.DisplayProgressBar(proDesc, "", progress);
  206. Texture2D tex = GetOutTex(texture2Ds);
  207. byte[] bytes = new byte[] { };
  208. string suffix = "";
  209. if(emOutFormat == EmImgFormat.PNG)
  210. {
  211. bytes = tex.EncodeToPNG();
  212. suffix = ".png";
  213. }
  214. else if(emOutFormat == EmImgFormat.JPG)
  215. {
  216. bytes = tex.EncodeToJPG();
  217. suffix = ".jpg";
  218. }
  219. else if(emOutFormat == EmImgFormat.TGA)
  220. {
  221. bytes = tex.EncodeToTGA();
  222. suffix = ".tga";
  223. }
  224. File.WriteAllBytes(outPath + "/output" + suffix, bytes);
  225. EditorUtility.ClearProgressBar();
  226. EditorApplication.ExecuteMenuItem("Assets/Refresh");
  227. isMerge = false;
  228. }
  229. public static Texture2D GetOutTex(Texture2D[] texs)
  230. {
  231. int len = texs.Length;
  232. if (len < 1) return null;
  233. Texture2D nTex = new Texture2D(outWidth, outHeight, TextureFormat.ARGB32, true);
  234. Color[] colors = new Color[outWidth * outHeight];
  235. int offsetW, offsetH;
  236. offsetW = 0;//横向写入偏移
  237. offsetH = 0;//纵向写入偏移
  238. //是否是宽图
  239. bool isHor = impWidth > impHeight;
  240. float ratio = isHor ? (float)impWidth / impHeight : (float)impHeight / impWidth;
  241. //根据数量计算单侧个数
  242. float oriCnt = Mathf.Sqrt(len / ratio);
  243. int littleCnt = Mathf.CeilToInt(oriCnt);
  244. int moreCnt = Mathf.CeilToInt(oriCnt * ratio);
  245. int wCnt = isHor ? littleCnt : moreCnt;
  246. //纵向个数
  247. int hCnt = isHor ? moreCnt : littleCnt;
  248. //单张高度
  249. int singleH = Mathf.FloorToInt(outHeight / hCnt);
  250. int singleW = Mathf.FloorToInt(outWidth / wCnt);
  251. //单张宽度
  252. Debug.Log(string.Format("计算得到单张图的width=={0}==height=={1}",singleW,singleH));
  253. Debug.Log(string.Format("计算得到单张图的wCnt=={0}==hCnt=={1}", wCnt, hCnt));
  254. int texIndex = 0;
  255. GetTextureCol(texs, ref colors, texIndex, singleW, singleH, offsetW, offsetH);
  256. proDesc = "图片合并完成,开始写入大图";
  257. EditorUtility.DisplayProgressBar(proDesc, "", progress);
  258. nTex.SetPixels(colors);
  259. nTex.Apply();
  260. return nTex;
  261. }
  262. static void GetTextureCol(Texture2D[] texs, ref Color[] colors, int texIndex,int singleW,int singleH,int offsetW,int offsetH)
  263. {
  264. proDesc = string.Format("写入第{0}张图片", texIndex + 1);
  265. Texture2D tex = texs[texIndex];
  266. EditorUtility.DisplayProgressBar(proDesc, tex.name, progress);
  267. for (int h = 0; h < singleH; h++)
  268. {
  269. for (int w = 0; w < singleW; w++)
  270. {
  271. Color color = tex.GetPixelBilinear((float)w / singleW, (float)h / singleH);
  272. int index = h * outWidth + w + offsetW + (offsetH * outHeight);
  273. try
  274. {
  275. if (colors[index] == null)
  276. {
  277. colors[index] = color;
  278. continue;
  279. }
  280. colors[index] = color;
  281. }catch(Exception e)
  282. {
  283. Debug.LogError(e.ToString());
  284. }
  285. }
  286. }
  287. offsetW += singleW;
  288. if (offsetW + singleW > outWidth)
  289. {
  290. offsetH += singleH;
  291. offsetW = 0;
  292. }
  293. texIndex = texIndex + 1;
  294. progress = (float)texIndex / maxProgress;
  295. if (texIndex < texs.Length)
  296. {
  297. GetTextureCol(texs, ref colors, texIndex , singleW, singleH, offsetW, offsetH);
  298. }
  299. }
  300. static bool CheckIsValid()
  301. {
  302. bool ret = true;
  303. //路径检测
  304. if (string.IsNullOrEmpty(filePath))
  305. {
  306. return false;
  307. }
  308. if(string.IsNullOrEmpty(outPath))
  309. {
  310. return false;
  311. }
  312. //尺寸检测
  313. if(impHeight <= 0 || impWidth <= 0)
  314. {
  315. return false;
  316. }
  317. if(outWidth <= 0 || outHeight <= 0)
  318. {
  319. return false;
  320. }
  321. return ret;
  322. }
  323. }

完~

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/2023面试高手/article/detail/138374
推荐阅读
相关标签
  

闽ICP备14008679号