Коммит
88dd9c4180
|
@ -0,0 +1,21 @@
|
|||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
|
||||
namespace UnityVolumeRendering
|
||||
{
|
||||
public class ImportSettingsEditorWindow : EditorWindow
|
||||
{
|
||||
public static void ShowWindow()
|
||||
{
|
||||
ImportSettingsEditorWindow wnd = new ImportSettingsEditorWindow();
|
||||
wnd.Show();
|
||||
}
|
||||
|
||||
private void OnGUI()
|
||||
{
|
||||
EditorGUILayout.LabelField("Show promt asking if you want to downscale the dataset on import?");
|
||||
bool showDownscalePrompt = EditorGUILayout.Toggle("Show downscale prompt", EditorPrefs.GetBool("DownscaleDatasetPrompt"));
|
||||
EditorPrefs.SetBool("DownscaleDatasetPrompt", showDownscalePrompt);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 8967c87c2996b4a4988102a8f6eaa239
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -43,12 +43,19 @@ namespace UnityVolumeRendering
|
|||
|
||||
private void ImportDataset()
|
||||
{
|
||||
RawDatasetImporter importer = new RawDatasetImporter(fileToImport, dimX, dimY, dimZ, dataFormat, endianness, bytesToSkip);
|
||||
|
||||
RawDatasetImporter importer = new RawDatasetImporter(fileToImport, dimX, dimY, dimZ, dataFormat, endianness, bytesToSkip);
|
||||
VolumeDataset dataset = importer.Import();
|
||||
|
||||
if (dataset != null)
|
||||
{
|
||||
if (EditorPrefs.GetBool("DownscaleDatasetPrompt"))
|
||||
{
|
||||
if (EditorUtility.DisplayDialog("Optional DownScaling",
|
||||
$"Do you want to downscale the dataset? The dataset's dimension is: {dataset.dimX} x {dataset.dimY} x {dataset.dimZ}", "Yes", "No"))
|
||||
{
|
||||
dataset.DownScaleData();
|
||||
}
|
||||
}
|
||||
VolumeRenderedObject obj = VolumeObjectFactory.CreateObject(dataset);
|
||||
}
|
||||
else
|
||||
|
|
|
@ -51,11 +51,21 @@ namespace UnityVolumeRendering
|
|||
DICOMImporter importer = new DICOMImporter(fileCandidates, Path.GetFileName(dir));
|
||||
List<DICOMImporter.DICOMSeries> seriesList = importer.LoadDICOMSeries();
|
||||
float numVolumesCreated = 0;
|
||||
|
||||
foreach (DICOMImporter.DICOMSeries series in seriesList)
|
||||
{
|
||||
VolumeDataset dataset = importer.ImportDICOMSeries(series);
|
||||
if (dataset != null)
|
||||
{
|
||||
if (EditorPrefs.GetBool("DownscaleDatasetPrompt"))
|
||||
{
|
||||
if (EditorUtility.DisplayDialog("Optional DownScaling",
|
||||
$"Do you want to downscale the dataset? The dataset's dimension is: {dataset.dimX} x {dataset.dimY} x {dataset.dimZ}", "Yes", "No"))
|
||||
{
|
||||
dataset.DownScaleData();
|
||||
}
|
||||
}
|
||||
|
||||
VolumeRenderedObject obj = VolumeObjectFactory.CreateObject(dataset);
|
||||
obj.transform.position = new Vector3(numVolumesCreated, 0, 0);
|
||||
numVolumesCreated++;
|
||||
|
@ -77,12 +87,24 @@ namespace UnityVolumeRendering
|
|||
static void ShowSequenceImporter()
|
||||
{
|
||||
string dir = EditorUtility.OpenFolderPanel("Select a folder to load", "", "");
|
||||
|
||||
if (Directory.Exists(dir))
|
||||
{
|
||||
ImageSequenceImporter importer = new ImageSequenceImporter(dir);
|
||||
|
||||
VolumeDataset dataset = importer.Import();
|
||||
if (dataset != null)
|
||||
{
|
||||
if (EditorPrefs.GetBool("DownscaleDatasetPrompt"))
|
||||
{
|
||||
if (EditorUtility.DisplayDialog("Optional DownScaling",
|
||||
$"Do you want to downscale the dataset? The dataset's dimension is: {dataset.dimX} x {dataset.dimY} x {dataset.dimZ}", "Yes", "No"))
|
||||
{
|
||||
dataset.DownScaleData();
|
||||
}
|
||||
}
|
||||
VolumeObjectFactory.CreateObject(dataset);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -134,5 +156,11 @@ namespace UnityVolumeRendering
|
|||
{
|
||||
ValueRangeEditorWindow.ShowWindow();
|
||||
}
|
||||
|
||||
[MenuItem("Volume Rendering/Settigs")]
|
||||
static void ShowSettingsWindow()
|
||||
{
|
||||
ImportSettingsEditorWindow.ShowWindow();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -65,7 +65,10 @@ namespace UnityVolumeRendering
|
|||
|
||||
// Load all DICOM files
|
||||
List<DICOMSliceFile> files = new List<DICOMSliceFile>();
|
||||
foreach (string filePath in fileCandidates)
|
||||
|
||||
IEnumerable<string> sortedFiles = fileCandidates.OrderBy(s => s);
|
||||
|
||||
foreach (string filePath in sortedFiles)
|
||||
{
|
||||
DICOMSliceFile sliceFile = ReadDICOMFile(filePath);
|
||||
if(sliceFile != null)
|
||||
|
@ -94,9 +97,6 @@ namespace UnityVolumeRendering
|
|||
{
|
||||
List<DICOMSliceFile> files = series.dicomFiles;
|
||||
|
||||
// Sort files by slice location
|
||||
files.Sort((DICOMSliceFile a, DICOMSliceFile b) => { return a.location.CompareTo(b.location); });
|
||||
|
||||
// Check if the series is missing the slice location tag
|
||||
bool needsCalcLoc = false;
|
||||
foreach (DICOMSliceFile file in files)
|
||||
|
@ -107,6 +107,9 @@ namespace UnityVolumeRendering
|
|||
// Calculate slice location from "Image Position" (0020,0032)
|
||||
if (needsCalcLoc)
|
||||
CalcSliceLocFromPos(files);
|
||||
|
||||
// Sort files by slice location
|
||||
files.Sort((DICOMSliceFile a, DICOMSliceFile b) => { return a.location.CompareTo(b.location); });
|
||||
|
||||
Debug.Log($"Importing {files.Count} DICOM slices");
|
||||
|
||||
|
|
|
@ -15,6 +15,7 @@ namespace UnityVolumeRendering
|
|||
{
|
||||
"*.png",
|
||||
"*.jpg",
|
||||
"*.jpeg"
|
||||
};
|
||||
|
||||
public ImageSequenceImporter(string directoryPath)
|
||||
|
|
|
@ -29,7 +29,6 @@ namespace UnityVolumeRendering
|
|||
private DataContentFormat contentFormat;
|
||||
private Endianness endianness;
|
||||
private int skipBytes;
|
||||
|
||||
public RawDatasetImporter(string filePath, int dimX, int dimY, int dimZ, DataContentFormat contentFormat, Endianness endianness, int skipBytes)
|
||||
{
|
||||
this.filePath = filePath;
|
||||
|
@ -44,7 +43,7 @@ namespace UnityVolumeRendering
|
|||
public VolumeDataset Import()
|
||||
{
|
||||
// Check that the file exists
|
||||
if(!File.Exists(filePath))
|
||||
if (!File.Exists(filePath))
|
||||
{
|
||||
Debug.LogError("The file does not exist: " + filePath);
|
||||
return null;
|
||||
|
@ -87,7 +86,7 @@ namespace UnityVolumeRendering
|
|||
fs.Close();
|
||||
|
||||
dataset.FixDimensions();
|
||||
|
||||
|
||||
return dataset;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
using System;
|
||||
using System;
|
||||
using UnityEngine;
|
||||
|
||||
namespace UnityVolumeRendering
|
||||
|
@ -15,7 +15,7 @@ namespace UnityVolumeRendering
|
|||
|
||||
[SerializeField]
|
||||
public int dimX, dimY, dimZ;
|
||||
|
||||
|
||||
[SerializeField]
|
||||
public float scaleX = 0.0f, scaleY = 0.0f, scaleZ = 0.0f;
|
||||
|
||||
|
@ -66,32 +66,40 @@ namespace UnityVolumeRendering
|
|||
{
|
||||
int MAX_DIM = 2048; // 3D texture max size. See: https://docs.unity3d.com/Manual/class-Texture3D.html
|
||||
|
||||
if (Mathf.Max(dimX, dimY, dimZ) > MAX_DIM)
|
||||
while (Mathf.Max(dimX, dimY, dimZ) > MAX_DIM)
|
||||
{
|
||||
Debug.LogWarning("Dimension exceeds limits. Cropping dataset. This might result in an incomplete dataset.");
|
||||
Debug.LogWarning("Dimension exceeds limits (maximum: "+MAX_DIM+"). Dataset is downscaled by 2 on each axis!");
|
||||
DownScaleData();
|
||||
}
|
||||
}
|
||||
|
||||
int newDimX = Mathf.Min(dimX, MAX_DIM);
|
||||
int newDimY = Mathf.Min(dimY, MAX_DIM);
|
||||
int newDimZ = Mathf.Min(dimZ, MAX_DIM);
|
||||
int[] newData = new int[dimX * dimY * dimZ];
|
||||
/// <summary>
|
||||
/// Downscales the data by averaging 8 voxels per each new voxel,
|
||||
/// and replaces downscaled data with the original data
|
||||
/// </summary>
|
||||
public void DownScaleData()
|
||||
{
|
||||
int halfDimX = dimX / 2 + dimX % 2;
|
||||
int halfDimY = dimY / 2 + dimY % 2;
|
||||
int halfDimZ = dimZ / 2 + dimZ % 2;
|
||||
int[] downScaledData = new int[halfDimX * halfDimY * halfDimZ];
|
||||
|
||||
for (int z = 0; z < newDimZ; z++)
|
||||
for (int x = 0; x < halfDimX; x++)
|
||||
{
|
||||
for (int y = 0; y < halfDimY; y++)
|
||||
{
|
||||
for (int y = 0; y < newDimY; y++)
|
||||
for (int z = 0; z < halfDimZ; z++)
|
||||
{
|
||||
for (int x = 0; x < newDimX; x++)
|
||||
{
|
||||
int oldIndex = (z * dimX * dimY) + (y * dimX) + x;
|
||||
int newIndex = (z * newDimX * newDimY) + (y * newDimX) + x;
|
||||
newData[newIndex] = data[oldIndex];
|
||||
}
|
||||
downScaledData[x + y * halfDimX + z * (halfDimX * halfDimY)] = Mathf.RoundToInt(GetAvgerageVoxelValues(x * 2, y * 2, z * 2));
|
||||
}
|
||||
}
|
||||
data = newData;
|
||||
dimX = newDimX;
|
||||
dimY = newDimY;
|
||||
dimZ = newDimZ;
|
||||
}
|
||||
|
||||
//Update data & data dimensions
|
||||
data = downScaledData;
|
||||
dimX = halfDimX;
|
||||
dimY = halfDimY;
|
||||
dimZ = halfDimZ;
|
||||
}
|
||||
|
||||
private void CalculateValueBounds()
|
||||
|
@ -112,7 +120,7 @@ namespace UnityVolumeRendering
|
|||
TextureFormat texformat = SystemInfo.SupportsTextureFormat(TextureFormat.RHalf) ? TextureFormat.RHalf : TextureFormat.RFloat;
|
||||
Texture3D texture = new Texture3D(dimX, dimY, dimZ, texformat, false);
|
||||
texture.wrapMode = TextureWrapMode.Clamp;
|
||||
|
||||
|
||||
int minValue = GetMinDataValue();
|
||||
int maxValue = GetMaxDataValue();
|
||||
int maxRange = maxValue - minValue;
|
||||
|
@ -197,5 +205,32 @@ namespace UnityVolumeRendering
|
|||
texture.Apply();
|
||||
return texture;
|
||||
}
|
||||
|
||||
public float GetAvgerageVoxelValues(int x, int y, int z)
|
||||
{
|
||||
// if a dimension length is not an even number
|
||||
bool xC = x + 1 == dimX;
|
||||
bool yC = y + 1 == dimY;
|
||||
bool zC = z + 1 == dimZ;
|
||||
|
||||
//if expression can only be true on the edges of the texture
|
||||
if (xC || yC || zC)
|
||||
{
|
||||
if (!xC && yC && zC) return (GetData(x, y, z) + GetData(x + 1, y, z)) / 2.0f;
|
||||
else if (xC && !yC && zC) return (GetData(x, y, z) + GetData(x, y + 1, z)) / 2.0f;
|
||||
else if (xC && yC && !zC) return (GetData(x, y, z) + GetData(x, y, z + 1)) / 2.0f;
|
||||
else if (!xC && !yC && zC) return (GetData(x, y, z) + GetData(x + 1, y, z) + GetData(x, y + 1, z) + GetData(x + 1, y + 1, z)) / 4.0f;
|
||||
else if (!xC && yC && !zC) return (GetData(x, y, z) + GetData(x + 1, y, z) + GetData(x, y, z + 1) + GetData(x + 1, y, z + 1)) / 4.0f;
|
||||
else if (xC && !yC && !zC) return (GetData(x, y, z) + GetData(x, y + 1, z) + GetData(x, y, z + 1) + GetData(x, y + 1, z + 1)) / 4.0f;
|
||||
else return GetData(x, y, z); // if xC && yC && zC
|
||||
}
|
||||
return (GetData(x, y, z) + GetData(x + 1, y, z) + GetData(x, y + 1, z) + GetData(x + 1, y + 1, z)
|
||||
+ GetData(x, y, z + 1) + GetData(x, y + 1, z + 1) + GetData(x + 1, y, z + 1) + GetData(x + 1, y + 1, z + 1)) / 8.0f;
|
||||
}
|
||||
|
||||
public int GetData(int x, int y, int z)
|
||||
{
|
||||
return data[x + y * dimX + z * (dimX * dimY)];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Загрузка…
Ссылка в новой задаче