Search Unity

Generation of multiple 3D textures

Discussion in 'General Discussion' started by Lvs17, May 23, 2023.

  1. Lvs17

    Lvs17

    Joined:
    Feb 27, 2023
    Posts:
    8
    Hello everyone, I've been at an impasse for some time.
    I'm trying to split an unreadable 3D texture into multiple subtextures to improve performance and then apply lazy loading. but I am having trouble.
    indeed the texture is divided but the cubes that I obtain are not coherent with the main texture. I get points instead of images. attached the 2 most necessary scripts of the project.


    Code (CSharp):
    1. using System.Collections.Generic;
    2. using System.Linq;
    3. using UnityEditor;
    4. using UnityEngine;
    5. using UnityEngine.InputSystem;
    6.  
    7. public class Center : MonoBehaviour
    8. {
    9.     private float _divisionResolution;
    10.     public int Channel { get; set; } = 0;
    11.     public int nbChannel;
    12.     public int[] listChannel = { 1 };
    13.     private int _nbChannelGlobal = 1;
    14.     public int sizeChannel;
    15.     public IDictionary<int, byte[]> lut = new Dictionary<int, byte[]>();
    16.     internal Data _data;
    17.     private int _nbOfSliceUsed;
    18.     private bool _textureCreated = false;
    19.     public bool lutChange = true;
    20.     public int[] Size { get; set; }
    21.     public Texture3D Texture { get; set; }
    22.     private const string TextureFolderPath = "Assets/Textures/";
    23.     private const int TextureCount = 16;
    24.  
    25.     public void SetData(Data data, float divisionResolution = 1, int nbChannelsGlobal = 1)
    26.     {
    27.         nbChannel = data._matriceImages[0].channels();
    28.         sizeChannel = data._matriceImages[0].depth();
    29.         // Extraction des données.
    30.         _data = data;
    31.         _nbChannelGlobal = nbChannelsGlobal;
    32.  
    33.         // Initialisation de la taille de la matrice de référence pour choisir les plans de coupe.
    34.         transform.localScale = new Vector3((float)_data.Size["width"] / _data.Size.Values.Max(), (float)_data.Size["height"] / _data.Size.Values.Max(),
    35.             (float)_data.Size["depth"] / _data.Size.Values.Max());
    36.  
    37.     }
    38.  
    39.     private void Update()
    40.     {
    41.         if ((_data != null && !_textureCreated) && Keyboard.current.kKey.isPressed || (lutChange && _data != null))
    42.         {
    43.             CreateTextures();
    44.             _textureCreated = true;
    45.             lutChange = false;
    46.         }
    47.     }
    48.  
    49.     private void CreateTextures()
    50.     {
    51.         const TextureFormat format = TextureFormat.RGB24;
    52.         int cubeSize = 64; // Taille du côté du bloc cubique (à ajuster selon vos besoins)
    53.         int cubesPerDimension = _data.Size["depth"] / cubeSize; // Nombre de blocs cubiques par dimension
    54.  
    55.         for (int x = 0; x < cubesPerDimension; x++)
    56.         {
    57.             for (int y = 0; y < cubesPerDimension; y++)
    58.             {
    59.                 for (int z = 0; z < cubesPerDimension; z++)
    60.                 {
    61.                     int cubeStart = z * cubeSize;
    62.                     int cubeEnd = (z + 1) * cubeSize;
    63.  
    64.                     // Création de la texture.
    65.                     Texture3D texture = new Texture3D(cubeSize, cubeSize, cubeSize, format, false);
    66.  
    67.                     // Calcul des coordonnées de début et de fin pour le bloc cubique actuel
    68.                     int startX = x * cubeSize;
    69.                     int startY = y * cubeSize;
    70.                     int startZ = z * cubeSize;
    71.                     int endX = (x + 1) * cubeSize;
    72.                     int endY = (y + 1) * cubeSize;
    73.                     int endZ = (z + 1) * cubeSize;
    74.  
    75.                     // Récupération des couleurs de l'image pour le bloc cubique actuel
    76.                     _data.SetPixelTexture(texture, nbChannel, _nbChannelGlobal, listChannel, lut, startX, endX, startY, endY, startZ, endZ);
    77.                     texture.Apply(false, true);
    78.  
    79.                     // Sauvegarde de la texture dans un fichier
    80.                     string fileName = "Texture_" + x.ToString() + "_" + y.ToString() + "_" + z.ToString() + ".asset";
    81.                     string filePath = TextureFolderPath + fileName;
    82.                     AssetDatabase.CreateAsset(texture, filePath);
    83.                     AssetDatabase.SaveAssets();
    84.                     Debug.Log("Texture 3D créée et sauvegardée : " + filePath);
    85.                 }
    86.             }
    87.         }
    88.  
    89.     }
    90. }
    Code (CSharp):
    1. using System;
    2. using System.Collections.Generic;
    3. using System.IO;
    4. using System.Threading.Tasks;
    5. using System.Linq;
    6. using UnityEngine;
    7. using Mat = OpenCVForUnity.CoreModule.Mat;
    8. using Size = OpenCVForUnity.CoreModule.Size;
    9.  
    10. public class Data
    11. {
    12.     public readonly bool IsEmpty = true;
    13.     internal List<Mat> _matriceImages;
    14.  
    15.     // Initialisation des images à partir d'un dossier.
    16.     public Data(string path, int nbOfSlice, float divResolution, int nbChannelsGlobal)
    17.     {
    18.         FillImages(LoadImages(path, nbOfSlice, nbChannelsGlobal), nbOfSlice, divResolution, nbChannelsGlobal);
    19.         IsEmpty = false;
    20.     }
    21.  
    22.     // Initialisation des images à partir d'une matrice 3D.
    23.     public Data(List<Mat> matrix3d, int nbChannelsGlobal)
    24.     {
    25.         FillImages(matrix3d, matrix3d.Count, 1, nbChannelsGlobal);
    26.         IsEmpty = false;
    27.     }
    28.  
    29.     public Data(List<Mat> matrix3d, int nbOfSlice, float divResolution, int nbChannelsGlobal)
    30.     {
    31.         FillImages(matrix3d, nbOfSlice, divResolution, nbChannelsGlobal);
    32.         IsEmpty = false;
    33.     }
    34.  
    35.     // Taille de la matrice.
    36.     public Dictionary<string, int> Size { get; } = new Dictionary<string, int>();
    37.  
    38.     private List<Mat> LoadImages(string path, int nbOfSlice, int nbChannelsGlobal)
    39.     {
    40.         // Récupération de tous les fichiers ".tif" dans le dossier.
    41.         var filePaths = Directory.GetFiles(path, "*.tif");
    42.  
    43.         var mat3d = new List<Mat>(nbOfSlice);
    44.  
    45.         // Chargement de chaque image et ajout dans une liste de matrice.
    46.         if (nbChannelsGlobal == 1)
    47.         {
    48.             for (var i = 0; i < nbOfSlice; i++)
    49.             {
    50.                 mat3d.Add(OpenCVForUnity.ImgcodecsModule.Imgcodecs.imread(filePaths[i],
    51.                     OpenCVForUnity.ImgcodecsModule.Imgcodecs.IMREAD_ANYCOLOR |
    52.                     OpenCVForUnity.ImgcodecsModule.Imgcodecs.IMREAD_ANYDEPTH));
    53.                 if (mat3d[i].empty()) Console.WriteLine("Impossible de charger l'image " + filePaths[i]);
    54.             }
    55.         }
    56.         else
    57.         {
    58.             for (var j = 0; j < nbChannelsGlobal; j++)
    59.             {
    60.                 var mat3dChannel = new List<Mat>(nbOfSlice);
    61.                 OpenCVForUnity.ImgcodecsModule.Imgcodecs.imreadmulti(filePaths[j], mat3dChannel, OpenCVForUnity.ImgcodecsModule.Imgcodecs.IMREAD_ANYCOLOR | OpenCVForUnity.ImgcodecsModule.Imgcodecs.IMREAD_ANYDEPTH);
    62.                 for (var i = 0; i < nbOfSlice; i++)
    63.                 {
    64.                     mat3d.Add(mat3dChannel[i]);
    65.                 }
    66.             }
    67.  
    68.         }
    69.  
    70.         return mat3d;
    71.     }
    72.  
    73.     // Redimensionner les images et les ajouter dans une liste.
    74.     private void FillImages(List<Mat> matrix3d, int nbOfSlice, float divResolution, int nbChannelsGlobal)
    75.     {
    76.         _matriceImages = new List<Mat>(nbOfSlice * nbChannelsGlobal);
    77.         for (var i = 0; i < nbOfSlice * nbChannelsGlobal; i += (int)divResolution)
    78.         {
    79.             Mat mat = new Mat();
    80.             OpenCVForUnity.ImgprocModule.Imgproc.resize(matrix3d[i], mat, new Size((matrix3d[i].size(1) / divResolution), (matrix3d[i].size(0) / divResolution)));
    81.             _matriceImages.Add(mat);
    82.         }
    83.  
    84.         Size["width"] = _matriceImages[0].size(1);
    85.         Size["height"] = _matriceImages[0].size(0);
    86.         Size["depth"] = (int)(nbOfSlice / divResolution);
    87.     }
    88.  
    89.     private byte[] GetChannelLut(int k, IDictionary<int, byte[]> lut, float pixel)
    90.     {
    91.         byte[] dataRgb = new byte[3];
    92.         dataRgb[0] = (byte)(pixel * lut[k][0]);
    93.         dataRgb[1] = (byte)(pixel * lut[k][1]);
    94.         dataRgb[2] = (byte)(pixel * lut[k][2]);
    95.  
    96.         return dataRgb;
    97.     }
    98.  
    99.     private void SetLut(int nbChannel, int nbChannelsGlobal, int[] listChannel, byte[] colors, IDictionary<int, byte[]> lut)
    100.     {
    101.         var encodageCouleur = Mathf.Pow(2, 8);
    102.         var plane = Size["width"] * Size["height"];
    103.         // Gerer le garbage collector
    104.         Parallel.ForEach(Enumerable.Range(0, _matriceImages.Count - 1).Where(j => j % nbChannelsGlobal == 0), j =>
    105.         {
    106.             Parallel.ForEach(listChannel, k =>
    107.             {
    108.                 Mat mat = _matriceImages[k + j];
    109.  
    110.                 int z = j / nbChannelsGlobal;
    111.                 int zOffset = z * plane * 3;
    112.                 unsafe
    113.                 {
    114.                     long p = mat.dataAddr();
    115.                     for (var x = 0; x < Size["width"]; x++)
    116.                     {
    117.                         int xOffset = x * Size["height"] * 3;
    118.                         for (var y = 0; y < Size["height"]; y++)
    119.                         {
    120.                             int yOffset = y * Size["width"] * 3;
    121.                             int targetIndex = yOffset + xOffset + zOffset;
    122.                             if (CheckIndex(targetIndex, colors.Length))
    123.                             {
    124.                                 colors[targetIndex] += ConvertPixelToByte(p, mat.type(), lut, k, 0);
    125.                                 colors[targetIndex + 1] += ConvertPixelToByte(p, mat.type(), lut, k, 1);
    126.                                 colors[targetIndex + 2] += ConvertPixelToByte(p, mat.type(), lut, k, 2);
    127.                             }
    128.                            
    129.                             p += mat.elemSize();
    130.                             if (p % 2 == 1)
    131.                             {
    132.                                 p--;
    133.                             }
    134.                         }
    135.                     }
    136.                 }
    137.             });
    138.         });
    139.     }
    140.  
    141.     // Définir la texture 3D avec les valeurs de pixel correspondantes.
    142.     public void SetPixelTexture(Texture3D texture, int channel, int nbChannelGlobal, int[] listChannel, IDictionary<int, byte[]> lut, int startX, int endX, int startY, int endY, int startZ, int endZ)
    143.     {
    144.         Debug.Log("Création de la texture 3D en cours");
    145.         var chrono = new System.Diagnostics.Stopwatch();
    146.         var chrono2 = new System.Diagnostics.Stopwatch();
    147.         var chrono3 = new System.Diagnostics.Stopwatch();
    148.         chrono3.Start();
    149.  
    150.         int width = endX - startX;
    151.         int height = endY - startY;
    152.         int depth = endZ - startZ;
    153.  
    154.         byte[] colors = new byte[width * height * depth * 3];
    155.         chrono2.Start();
    156.         SetLut(channel, nbChannelGlobal, listChannel, colors, lut);
    157.         chrono2.Stop();
    158.         chrono.Start();
    159.  
    160.         for (int z = startZ; z < endZ; z++)
    161.         {
    162.             for (int y = startY; y < endY; y++)
    163.             {
    164.                 for (int x = startX; x < endX; x++)
    165.                 {
    166.                     int textureX = x - startX;
    167.                     int textureY = y - startY;
    168.                     int textureZ = z - startZ;
    169.  
    170.                     int colorsIndex = (textureZ * width * height + textureY * width + textureX) * 3;
    171.                     Color color = new Color32(colors[colorsIndex], colors[colorsIndex + 1], colors[colorsIndex + 2], 255);
    172.  
    173.                     texture.SetPixel(x, y, z, color);
    174.                 }
    175.             }
    176.         }
    177.  
    178.         chrono.Stop();
    179.         chrono3.Stop();
    180.  
    181.         Debug.Log("Temps de génération de SetPixelData en ms : " + chrono.ElapsedMilliseconds);
    182.         Debug.Log("Temps de génération des boucles for en ms : " + chrono2.ElapsedMilliseconds);
    183.         Debug.Log("Total : " + chrono3.ElapsedMilliseconds);
    184.     }
    185.  
    186.  
    187.     private byte ConvertPixelToByte(long address, int matType, IDictionary<int, byte[]> lut, int k, int c)
    188.     {
    189.         unsafe
    190.         {
    191.             byte value = 0;
    192.             switch (matType % 8)
    193.             {
    194.                 case 0:
    195.                     value = *((byte*)address);
    196.                     break;
    197.                 case 1:
    198.                     value = (byte)((*(sbyte*)address) * lut[k][c]);
    199.                     break;
    200.                 case 2:
    201.                     value = (byte)(((ushort*)address)[0] / (Mathf.Pow(2, 16)) * lut[k][c]);
    202.                     break;
    203.                 case 3:
    204.                     value = (byte)(((short*)address)[0] / (Mathf.Pow(2, 16)) * lut[k][c]);
    205.                     break;
    206.                 case 4:
    207.                     value = (byte)(((int*)address)[0] / (Mathf.Pow(2, 32)) * lut[k][c]);
    208.                     break;
    209.                 case 5:
    210.                     value = (byte)((*(float*)address) * lut[k][c]);
    211.                     break;
    212.                 case 6:
    213.                     value = (byte)((*(double*)address) * lut[k][c]);
    214.                     break;
    215.             }
    216.  
    217.             return value;
    218.         }
    219.     }
    220.  
    221.     public int GetPixelColor(int i, int i1, int i2, int channel)
    222.     {
    223.         throw new NotImplementedException();
    224.     }
    225.     private bool CheckIndex(int index, int length)
    226.     {
    227.         return index >= 0 && index < length;
    228.     }
    229.  
    230. }
    231.