/// <summary> /// Rotates volume along given axis. /// </summary> /// <param name="volume"></param> /// <param name="angles"></param> public static void RotateData(ref vtkImageData volume, double[] angles) { // Rotation along image center double[] center = volume.GetExtent().Divide(2); // Dimensions of rotated image int[] outExtent = volume.GetExtent().Multiply(1.1).Round().ToInt32(); // Rotation parameters var rotate = new vtkTransform(); rotate.Translate(center[1], center[3], center[5]); rotate.RotateX(angles[0]); rotate.RotateY(angles[1]); rotate.RotateZ(angles[2]); // z angle should be 0 rotate.Translate(-center[1], -center[3], -center[5]); // Perform rotation var slice = new vtkImageReslice(); slice.SetInput(volume); slice.SetResliceTransform(rotate); slice.SetInterpolationModeToCubic(); slice.SetOutputSpacing(volume.GetSpacing()[0], volume.GetSpacing()[1], volume.GetSpacing()[2]); slice.SetOutputOrigin(volume.GetOrigin()[0], volume.GetOrigin()[1], volume.GetOrigin()[2]); slice.SetOutputExtent(outExtent[0], outExtent[1], outExtent[2], outExtent[3], outExtent[4], outExtent[5]); }
/// <summary> /// Function for removing sample edges. /// </summary> /// <param name="stack"></param> /// <param name="side"></param> /// <param name="get_center"></param> /// <returns></returns> public static vtkImageData center_crop(vtkImageData stack, int side = 400, bool get_center = true) { //Get input dimensions int[] dims = stack.GetExtent(); //Find the center of the sample int x1; int x2; int y1; int y2; int[] center = find_center(stack, 70, new int[] { 0, (dims[5] - dims[4] + 1) / 3 });//GetCenter(bytedata,80); //Compute new volume sides y2 = Math.Min(center[0] + (side / 2), dims[1]); y1 = Math.Max(y2 - side + 1, dims[0]); x2 = Math.Min(center[1] + (side / 2), dims[3]); x1 = Math.Max(x2 - side + 1, dims[2]); //Create VOI extractor vtkExtractVOI cropper = vtkExtractVOI.New(); cropper.SetVOI(y1, y2, x1, x2, dims[4], dims[5]); cropper.SetInput(stack); cropper.Update(); return(cropper.GetOutput()); }
void CreateData(ref vtkImageData data) { data.SetExtent(-25, 25, -25, 25, 0, 0); #if VTK_MAJOR_VERSION_5 data.SetNumberOfScalarComponents(1); data.SetScalarTypeToDouble(); #else data.AllocateScalars(VTK_DOUBLE, 1); #endif int[] extent = data.GetExtent(); for (int y = extent[2]; y <= extent[3]; y++) { for (int x = extent[0]; x <= extent[1]; x++) { IntPtr ptr = data.GetScalarPointer(x, y, 0); double[] pixel = new double[] { Math.Sqrt(Math.Pow(x, 2.0) + Math.Pow(y, 2.0)) }; Marshal.Copy(pixel, 0, ptr, 1); } } vtkXMLImageDataWriter writer = vtkXMLImageDataWriter.New(); writer.SetFileName(@"c:\vtk\vtkdata-5.8.0\Data\testIsoContours.vti"); #if VTK_MAJOR_VERSION_5 writer.SetInputConnection(data.GetProducerPort()); #else writer.SetInputData(data); #endif writer.Write(); }
/// <summary> /// Calculates average columns from 3D volume. Obsolete. /// </summary> /// <param name="averages"></param> /// <param name="steps"></param> /// <param name="input"></param> /// <param name="n_tiles"></param> public static void average_tiles(out double[,,] averages, out int[] steps, vtkImageData input, int n_tiles = 16) { //Get dimensions int[] dims = input.GetExtent(); int h = dims[1] - dims[0] + 1; int w = dims[3] - dims[2] + 1; int d = dims[5] - dims[4] + 1; //Input to byte array byte[] bytedata = DataTypes.vtkToByte(input); //Generate tile coordinates int N = (int)Math.Sqrt(n_tiles); int wh = h / N; int ww = w / N; steps = new int[] { wh, ww }; List <int[]> tiles = new List <int[]>(); for (int kh = 0; kh < N; kh++) { for (int kw = 0; kw < N; kw++) { int[] tmp = new int[] { kh *wh, (kh + 1) * wh, kw *ww, (kw + 1) * ww }; tiles.Add(tmp); } } //Iterate over tiles, and average the grayscale values //Empty array for averages double[,,] _averages = new double[N, N, d]; //Number of elements N = wh * ww; foreach (int[] tile in tiles) { for (int z = 0; z < d; z++) { Parallel.For(tile[0], tile[1], (int y) => { Parallel.For(tile[2], tile[3], (int x) => { int pos = z * (h * w) + x * (h) + y; byte val = bytedata[pos]; _averages[y / wh, x / ww, z] += (double)val; }); }); _averages[(tile[1] - 1) / wh, (tile[3] - 1) / ww, z] /= (double)N; } } averages = _averages; }
/// <summary> /// Removes surface artefacts from the sample. /// </summary> /// <param name="volume">Sample volume data.</param> /// <param name="points">Points for removal line.</param> /// <param name="axis">Axis of removal.</param> /// <returns>Sample data without artefacts</returns> public static vtkImageData remove_artefacts(vtkImageData volume, double[] points, int axis) { //Get input volume dimensions int[] dims = volume.GetExtent(); int h = dims[1] - dims[0] + 1; int w = dims[3] - dims[2] + 1; int d = dims[5] - dims[4] + 1; Console.WriteLine("Got dims: {0},{1},{2}", h, w, d); //Convert volume to byte array byte[] bytedata = DataTypes.vtkToByte(volume); Console.WriteLine("Got bytedata"); //Compute slope (k) and zero crossing (b) from given point for a line y=kx+b //Compute the slope of the line from points double slope = (points[3] - points[1]) / (points[2] - points[0]); //Compute zero crossing double b = points[3] - slope * points[2] - dims[4]; Console.WriteLine("Got line equation"); //Iterate over the data Parallel.For(0, h, (int y) => { Parallel.For(0, w, (int x) => { //Compute extent for zeroing int zstart = 0; int zstop = d; if (axis == 0) { zstart = (int)((double)(x + dims[2]) * slope + b); zstart = Math.Max(zstart, 0); } if (axis == 1) { zstart = (int)((double)(y + dims[0]) * slope + b); zstart = Math.Max(zstart, 0); } //Iterate over z-axis for (int z = zstart; z < zstop; z++) { int pos = z * (h * w) + x * h + y; bytedata[pos] = 0; } }); }); Console.WriteLine("Zeroed"); //Convert byte data back to vtkdata vtkImageData output = DataTypes.byteToVTK1D(bytedata, dims); return(output); }
/// <summary> /// Calculates mean and standard deviation images from volume-of-interest /// along third axis (z). /// </summary> /// <param name="input">Input volume as vtkImageData.</param> /// <param name="meanImage">Mean 2D image.</param> /// <param name="stdImage">Standard deviation 2D image.</param> public static void MeanAndStd(vtkImageData input, out double[,] meanImage, out double[,] stdImage) { //Get data extent int[] ext = input.GetExtent(); int[] dims = new int[] { ext[3] - ext[2] + 1, ext[1] - ext[0] + 1, ext[5] - ext[4] + 1 }; Console.WriteLine("Input shape: {0}, {1}, {2}".Format(dims[0], dims[1], dims[2])); // Convert to byte volume byte[] bytedata = DataTypes.vtkToByte(input); byte[,,] bytevolume = DataTypes.VectorToVolume(bytedata, dims); //int[] dims = new int[] { bytedata.GetLength(0), bytedata.GetLength(1), bytedata.GetLength(2) }; double[,] mean = new double[dims[0], dims[1]]; double[,] std = new double[dims[0], dims[1]]; Parallel.For(0, dims[0], i => { Parallel.For(0, dims[1], j => { //double[] temp = new double[dims[2]]; // has to be initialized in the loop double[] temp = new double[0]; // has to be initialized in the loop for (int k = 0; k < dims[2]; k++) { //temp[k] = bytevolume[i, j, k]; if (bytevolume[i, j, k] > 0) { temp.Concatenate(bytevolume[i, j, k]); } } if (temp.Length > 0) { mean[i, j] = temp.Average(); std[i, j] = Math.Sqrt(temp .Subtract(temp.Average()) .Pow(2) .Sum() / (temp.Length - 1)); } else { mean[i, j] = 0; std[i, j] = 0; } }); }); meanImage = mean; stdImage = std; }
/// <summary> /// Calculates orientation of 2D slice. Obsolete. /// </summary> /// <param name="slice"></param> /// <param name="threshold"></param> /// <returns></returns> public static double find_slice_ori(vtkImageData slice, double threshold = 70.0) { //Get dimensions int[] dims = slice.GetExtent(); int h = dims[1] - dims[0] + 1; int w = dims[3] - dims[2] + 1; //Convert slice to mat byte[] bytedata = DataTypes.vtkToByte(slice); List <int[]> _idx = new List <int[]>(); byte[] cont = new byte[h * w]; //for (int k = 0; k < cont.Length; k++) for (int k = 0; k < bytedata.Length; k++) { int _x = k / h; int _y = k % h; if ((double)bytedata[k] > threshold) { _idx.Add(new int[] { _y, _x }); cont[k] = 255; } } Mat im = new Mat(w, h, MatType.CV_8UC1, cont); using (var window = new Window("Indices", image: im, flags: WindowMode.AutoSize)) { Cv2.WaitKey(); } //Convert list to double array double[][] idx = new double[_idx.Count()][]; //Iterate over the list and collect indices to the array for (int k = 0; k < _idx.Count(); k++) { int[] _tmp = _idx.ElementAt(k); idx[k] = new double[] { _tmp[0], _tmp[1] }; } double theta = find_ori_pca(idx, 0); return(theta); }
/// <summary> /// Function for scaling sample data. Used in automatic rotation to reduce calculation time in gradient descent. /// </summary> /// <param name="input">Input volume data</param> /// <param name="scale">Scaling factor, e.g. 0.1 = downsampling with factor of 10.</param> /// <returns></returns> public static vtkImageData rescale_sample(vtkImageData input, double scale) { //Get sample dimensions int[] dims = input.GetExtent(); vtkImageResample samplery = vtkImageResample.New(); samplery.SetInput(input); samplery.SetOutputSpacing(input.GetSpacing()[0], input.GetSpacing()[1], input.GetSpacing()[2]); samplery.SetOutputOrigin(input.GetOrigin()[0], input.GetOrigin()[1], input.GetOrigin()[2]); samplery.SetOutputExtent((int)(scale * dims[0]), (int)(scale * dims[1]), dims[2], dims[3], dims[4], dims[5]); samplery.SetInterpolationModeToCubic(); samplery.SetAxisMagnificationFactor(0, scale); samplery.Update(); vtkImageResample samplerx = vtkImageResample.New(); samplerx.SetInputConnection(samplery.GetOutputPort()); samplerx.SetOutputSpacing(samplery.GetOutputSpacing()[0], samplery.GetOutputSpacing()[1], samplery.GetOutputSpacing()[2]); samplerx.SetOutputOrigin(samplery.GetOutputOrigin()[0], samplery.GetOutputOrigin()[1], samplery.GetOutputOrigin()[2]); samplerx.SetOutputExtent((int)(scale * dims[0]), (int)(scale * dims[1]), (int)(scale * dims[2]), (int)(scale * dims[3]), dims[4], dims[5]); samplerx.SetInterpolationModeToCubic(); samplerx.SetAxisMagnificationFactor(1, scale); samplerx.Update(); vtkImageResample samplerz = vtkImageResample.New(); samplerz.SetInputConnection(samplerx.GetOutputPort()); samplerz.SetOutputSpacing(samplerx.GetOutputSpacing()[0], samplerx.GetOutputSpacing()[1], samplerx.GetOutputSpacing()[2]); samplerz.SetOutputOrigin(samplerx.GetOutputOrigin()[0], samplerx.GetOutputOrigin()[1], samplerx.GetOutputOrigin()[2]); samplerz.SetOutputExtent((int)(scale * dims[0]), (int)(scale * dims[1]), (int)(scale * dims[2]), (int)(scale * dims[3]), (int)(scale * dims[4]), (int)(scale * dims[5])); samplerz.SetInterpolationModeToCubic(); samplerz.SetAxisMagnificationFactor(2, scale); samplerz.Update(); vtkImageData output = vtkImageData.New(); output.DeepCopy(samplerz.GetOutput()); samplerz.Dispose(); samplerx.Dispose(); samplery.Dispose(); return(output); }
/// <summary> /// Create scalar copy of vtkImageData. /// </summary> /// <param name="data">Input data.</param> /// <returns>Copied data.</returns> public static vtkImageData scalarCopy(vtkImageData data) { /*DEPRECATED!!*/ //Get data extent int[] dims = data.GetExtent(); vtkImageData newdata = vtkImageData.New(); newdata.SetExtent(dims[0], dims[1], dims[2], dims[3], dims[4], dims[5]); for (int h = dims[0]; h <= dims[1]; h++) { for (int w = dims[2]; w <= dims[3]; w++) { for (int d = dims[4]; d <= dims[5]; d++) { double scalar = data.GetScalarComponentAsDouble(h, w, d, 0); newdata.SetScalarComponentFromDouble(h, w, d, 0, scalar); } } } return(newdata); }
/// <summary> /// Converts 3D vtkImageData to 1D byte array. /// </summary> /// <param name="vtkdata">Input data.</param> /// <returns>Converted 1D array of vtkImageData.</returns> public static byte[] vtkToByte(vtkImageData vtkdata) { //Get vtk data dimensions int[] extent = vtkdata.GetExtent(); int[] dims = new int[] { extent[1] - extent[0] + 1, extent[3] - extent[2] + 1, extent[5] - extent[4] + 1 }; //New byte array for conversions byte[] bytedata = new byte[dims[0] * dims[1] * dims[2]]; //pin bytedata to memory GCHandle pinnedArray = GCHandle.Alloc(bytedata, GCHandleType.Pinned); //Get pointer to pinned array IntPtr ptr = pinnedArray.AddrOfPinnedObject(); //VTK exporter vtkImageExport exporter = vtkImageExport.New(); exporter.SetInput(vtkdata); exporter.Update(); //Export data to byte array exporter.Export(ptr); //Free pinned array pinnedArray.Free(); //Return byte array return(bytedata); }
/// <summary> /// Calculates loss function for gradient descent. /// </summary> /// <param name="input"></param> /// <returns></returns> public static double circle_loss(vtkImageData input) { //Get dimensions int[] dims = input.GetExtent(); int h = dims[1] - dims[0] + 1; int w = dims[3] - dims[2] + 1; int d = dims[5] - dims[4] + 1; //Get non zero indices form projection image byte[] bytedata = DataTypes.vtkToByte(input); byte[] surf = new byte[h * w]; Parallel.For(0, h, (int ky) => { Parallel.For(0, w, (int kx) => { for (int kz = d - 1; kz > 0; kz -= 1) { int pos = kz * (h * w) + kx * h + ky; int val = bytedata[pos]; if (val > 70.0) { surf[ky * w + kx] = 255; break; } } }); }); //Get nonzero indices Mat surfcv = new Mat(h, w, MatType.CV_8UC1, surf); //Find largest blob surfcv = Functions.largest_connected_component(surfcv); //Indices Mat nonzero = surfcv.FindNonZero(); //Fit circle Point2f center; float R; nonzero.MinEnclosingCircle(out center, out R); //Generate a circle byte[] circle = new byte[h * w]; Parallel.For(0, h, (int ky) => { Parallel.For(0, w, (int kx) => { float val = (ky - center.Y) * (ky - center.Y) + (kx - center.X) * (kx - center.X); if (val < R * R) { circle[ky * w + kx] = 255; } }); }); //Get dice score double dice = Functions.dice_score_2d(surf, circle); surfcv.Dispose(); nonzero.Dispose(); bytedata = null; return(1 - dice); }
/// <summary> /// Calculates mean and standard deviation images from volume-of-interest. Obsolete. /// </summary> /// <param name="output"></param> /// <param name="mu"></param> /// <param name="std"></param> /// <param name="input"></param> /// <param name="depth"></param> /// <param name="threshold"></param> public static void get_voi_mu_std(out vtkImageData output, out double[,] mu, out double[,] std, vtkImageData input, int depth, double threshold = 70.0) { //Get data extent int[] dims = input.GetExtent(); int h = dims[1] - dims[0] + 1; int w = dims[3] - dims[2] + 1; int d = dims[5] - dims[4] + 1; //Compute strides int stridew = 1; int strideh = w; int strided = h * w; byte[] bytedata = DataTypes.vtkToByte(input); byte[] voidata = new byte[bytedata.Length]; double[,] _mu = new double[h, w]; double[,] _std = new double[h, w]; //Get voi indices Parallel.For(24, h - 24, (int y) => { Parallel.For(24, w - 24, (int x) => { int start = d - 1; int stop = 0; //Compute mean for (int z = d - 1; z > 0; z -= 1) { int pos = z * strided + y * strideh + x * stridew; double val = (double)bytedata[pos]; if (val >= threshold) { start = z; stop = Math.Max(z - depth, 0); //Compute mean and std for (int zz = start; zz > stop; zz -= 1) { int newpos = zz * strided + y * strideh + x * stridew; double newval = (double)bytedata[newpos]; voidata[newpos] = (byte)newval; _mu[y, x] = newval / (double)depth; } for (int zz = start; zz > stop; zz -= 1) { int newpos = zz * strided + y * strideh + x * stridew; double newval = (double)bytedata[newpos]; _std[y, x] += ((double)newval - _mu[y, x]) * ((double)newval - _mu[y, x]); } _std[y, x] = Math.Pow(_std[y, x] / (depth - 1), 0.5); break; } } }); }); mu = _mu; std = _std; output = vtkImageData.New(); //Copy voi data to input array vtkUnsignedCharArray charArray = vtkUnsignedCharArray.New(); //Pin byte array GCHandle pinnedArray = GCHandle.Alloc(voidata, GCHandleType.Pinned); //Set character array input charArray.SetArray(pinnedArray.AddrOfPinnedObject(), h * w * d, 1); //Set vtkdata properties and connect array //Data from char array output.GetPointData().SetScalars(charArray); //Number of scalars/pixel output.SetNumberOfScalarComponents(1); //Data extent, 1st and last axis are swapped from the char array //Data is converted back to original orientation output.SetExtent(dims[0], dims[1], dims[2], dims[3], dims[4], dims[5]); //Scalar type output.SetScalarTypeToUnsignedChar(); output.Update(); }
public static vtkImageData SWFPSuppression(vtkImageData BCI, int[] extent, double threshold = 0.7 * 255.0, int min = 0, int max = 650, int step = 4, int ws = 50, double wt = 20.0, double minones = 192.0) { //Get dimensions int[] dims = BCI.GetExtent(); int h = dims[1] - dims[0] + 1; int w = dims[3] - dims[2] + 1; int d = dims[5] - dims[4] + 1; //Make array of extents List <int[]> regions = new List <int[]>(); for (int k1 = 0; k1 < 4; k1++) { for (int k2 = 0; k2 < 4; k2++) { int step1 = (extent[1] - extent[0] + 1) / 8; int step2 = (extent[3] - extent[2] + 1) / 8; int[] _tmp = new int[] { extent[0] + k1 * step1, extent[0] + (k1 + 1) * step1, extent[2] + k2 * step2, extent[2] + (k2 + 1) * step2 }; regions.Add(_tmp); Console.WriteLine("Region:"); Console.WriteLine("{0},{1},{2},{3}", _tmp[0], _tmp[1], _tmp[2], _tmp[3]); } } //Get byte data from input samples byte[] BCIByte = DataTypes.vtkToByte(BCI); List <byte[]> sums = new List <byte[]>(); //Itereate over pixels foreach (int[] region in regions) { byte[] tmp = new byte[max - min]; for (int k = min; k < max; k += step) { int w1 = k; int w2 = w1 + ws; int roisum = 0; Parallel.For(w1, w2, (int kz) => { Parallel.For(region[0], region[1], (int ky) => { Parallel.For(region[2], region[3], (int kx) => { int pos = kz * (h * w) + ky * w + kx; if ((double)BCIByte[pos] > threshold) { roisum += (int)BCIByte[pos]; } }); }); }); //Set sliding window index at k to 1 if enough non-zero pixels were found if (((double)roisum / (minones * minones)) > wt) { tmp[k] = 1; } //Check if there's a drop between consecutive results if (k > min + step && tmp[k] == 0 && tmp[k - step] == 1) { sums.Add(tmp); break; } //Otherswise append the data at the end else { if (k + step >= max) { sums.Add(tmp); } } } } int[] idx = new int[sums.Count]; //Scan sums vector find last non-zero index int c = 0; foreach (byte[] sum in sums) { for (int k = sum.Length - 1; k >= 0; k -= 1) { if (sum[k] == 1) { idx[c] = k; c++; break; } } } byte[,,] output = new byte[d, h, w]; //Set array indices below idx to outputvalues c = 0; foreach (int[] region in regions) { int _idx = idx[c]; Console.WriteLine("Max idx: {0} | Height: {1}", _idx, d); int z = 0; if (_idx == 0) { z = d; } else { z = Math.Min(_idx + 20, d); } Parallel.For(0, z, (int kz) => { Parallel.For(region[0], region[1], (int ky) => { Parallel.For(region[2], region[3], (int kx) => { int pos = kz * (h * w) + ky * (w) + kx; output[kz, ky, kx] = BCIByte[pos]; }); }); }); c++; } return(DataTypes.byteToVTK(output)); }
/// <summary> /// Method to calculate sample orientation using gradient descent. /// </summary> /// <param name="data"></param> /// <param name="alpha"></param> /// <param name="h"></param> /// <param name="n_iter"></param> /// <returns></returns> public static double[] grad_descent(vtkImageData data, double alpha = 0.5, double h = 5.0, int n_iter = 60) { //Starting orientation double[] ori = new double[2]; int[] dims = data.GetExtent(); int y = dims[1] - dims[0] + 1; int x = dims[3] - dims[2] + 1; int z = dims[5] - dims[4] + 1; for (int k = 1; k < n_iter + 1; k++) { //Iintialize gradient double[] grads = new double[2]; //Rotate the sample vtkImageData rotated1 = rotate_sample(data, ori[0] + h, 0, 1); if (ori[1] != 0) { rotated1 = rotate_sample(rotated1, ori[1], 1, 1); } vtkImageData rotated2 = rotate_sample(data, ori[0] - h, 0, 1); if (ori[1] != 0) { rotated2 = rotate_sample(rotated2, ori[1], 1, 1); } //Get losses double d1 = circle_loss(rotated1); double d2 = circle_loss(rotated2); //Compute gradient grads[0] = (d1 - d2) / (2 * h); //Rotate the sample vtkImageData rotated3 = rotate_sample(data, ori[1] + h, 1, 1); if (ori[0] != 0) { rotated3 = rotate_sample(rotated3, ori[0], 0, 1); } vtkImageData rotated4 = rotate_sample(data, ori[1] - h, 1, 1); if (ori[0] != 0) { rotated4 = rotate_sample(rotated4, ori[0], 0, 1); } //Get losses double d3 = circle_loss(rotated3); double d4 = circle_loss(rotated4); //Compute gradient grads[1] = (d3 - d4) / (2 * h); //Update the orientation ori[0] -= Math.Sign(grads[0]) * alpha; ori[1] -= Math.Sign(grads[1]) * alpha; if (k % n_iter / 2 == 0) { alpha /= 2; } rotated1.Dispose(); rotated2.Dispose(); rotated3.Dispose(); rotated4.Dispose(); } return(ori); }
/// <summary> /// Otsu thresholding in 3D. /// </summary> /// <param name="input"></param> /// <param name="axis"></param> /// <param name="threshold"></param> /// <returns></returns> public static vtkImageData otsu3D(vtkImageData input, int axis = 0, double threshold = 60.0) { //Get dimensions int[] dims = input.GetExtent(); int h = dims[1] - dims[0] + 1; int w = dims[3] - dims[2] + 1; int d = dims[5] - dims[4] + 1; //Convert to byte array byte[] bytedata = DataTypes.vtkToByte(input); byte[] output = new byte[bytedata.Length]; //Iterate over axis if (axis == 0) { for (int x = 0; x < w; x++) { byte[,] plane = new byte[d, h]; Parallel.For(0, h, (int y) => { Parallel.For(0, d, (int z) => { int pos = z * (h * w) + y * w + x; plane[z, y] = bytedata[pos]; }); }); //Convert 2D byte array to opencv Mat Mat image = new Mat(d, h, MatType.CV_8UC1, plane); Mat BW = image.Threshold(threshold, 255.0, ThresholdTypes.Otsu); IntPtr pointer = BW.Data; byte[] tmp = new byte[h * d]; Marshal.Copy(pointer, tmp, 0, h * d); Parallel.For(0, h, (int y) => { Parallel.For(0, d, (int z) => { int tmppos = z * h + y; int pos = z * (h * w) + y * w + x; output[pos] = tmp[tmppos]; }); }); } } else { for (int y = 0; y < h; y++) { byte[,] plane = new byte[d, w]; Parallel.For(0, w, (int x) => { Parallel.For(0, d, (int z) => { int pos = z * (h * w) + y * w + x; plane[z, x] = bytedata[pos]; }); }); //Convert 2D byte array to opencv Mat Mat image = new Mat(d, w, MatType.CV_8UC1, plane); Mat BW = image.Threshold(threshold, 255.0, ThresholdTypes.Otsu); IntPtr pointer = BW.Data; byte[] tmp = new byte[w * d]; Marshal.Copy(pointer, tmp, 0, w * d); Parallel.For(0, w, (int x) => { Parallel.For(0, d, (int z) => { int tmppos = z * w + x; int pos = z * (h * w) + y * w + x; output[pos] = tmp[tmppos]; }); }); } } return(DataTypes.byteToVTK1D(output, dims)); }
/// <summary> /// Method to calculate mean and standard deviation images from 3D volume-of-interest along depth-axis. /// </summary> /// <param name="mean">Resulting mean image.</param> /// <param name="sd">Resulting standard deviation image.</param> /// <param name="VOI">Input volume-of-interest.</param> /// <param name="voi_depth"></param> /// <param name="crop_size"></param> public static void get_mean_sd(out double[,] mean, out double[,] sd, vtkImageData VOI, int voi_depth = 0, int crop_size = 0) { //Get input extent int[] dims = VOI.GetExtent(); //Compute dimensions int h = dims[1] - dims[0] + 1; int w = dims[3] - dims[2] + 1; int d = dims[5] - dims[4] + 1; //Get byte data from vtkImageData byte[] bytedata = DataTypes.vtkToByte(VOI); double[,] mu = new double[h - crop_size * 2, w - crop_size * 2]; byte[,] muim = new byte[h - crop_size * 2, w - crop_size * 2]; int[,] Ns = new int[h - crop_size * 2, w - crop_size * 2]; //Set void depth if (voi_depth == 0) { voi_depth = d; } //Iterate over data and compute mean image for (int y = crop_size; y < h - crop_size; y++) { for (int x = crop_size; x < w - crop_size; x++) { double sum = 0.0; int N = 0; for (int z = d - 1; z >= 0; z -= 1) { //Compute position int pos = z * (h * w) + x * h + y; //Get byte value byte val = bytedata[pos]; //Add to sum sum += (double)val; //If value is nonzero, increase count if (val > 0) { N += 1; } //If count is equal to VOI depth, break if (N == voi_depth) { break; } } mu[y - crop_size, x - crop_size] = sum / ((double)N + 1e-9); muim[y - crop_size, x - crop_size] = (byte)mu[y - crop_size, x - crop_size]; Ns[y - crop_size, x - crop_size] = N; } } double[,] sigma = new double[h - crop_size * 2, w - crop_size * 2]; byte[,] sdim = new byte[h - crop_size * 2, w - crop_size * 2]; //Iterate over data and compute sd image for (int y = crop_size; y < h - crop_size; y++) { for (int x = crop_size; x < w - crop_size; x++) { double sum = 0.0; int N = 0; for (int z = d - 1; z >= 0; z -= 1) { //Compute position int pos = z * (h * w) + x * h + y; //Get byte value byte val = bytedata[pos]; //If value is non-zero, subtract from value and square if (val > 0) { double tmp = (double)val - mu[y - crop_size, x - crop_size]; sum += tmp * tmp; N += 1; } //If count is equal to VOI depth, break if (N == voi_depth) { break; } } sigma[y - crop_size, x - crop_size] = Math.Sqrt(sum / ((double)Ns[y - crop_size, x - crop_size] - 1.0 + 1e-9)); if (N == 0) { sigma[y - crop_size, x - crop_size] = 0.0; } sdim[y - crop_size, x - crop_size] = (byte)sigma[y - crop_size, x - crop_size]; } } //Return mu and sd mean = mu; sd = sigma; }
/// <summary> /// Rotates 3D vtk volume around x and y axes. /// </summary> /// <param name="input"></param> /// <param name="angle"></param> /// <param name="axis"></param> /// <param name="out_extent"></param> /// <returns></returns> public static vtkImageData rotate_sample(vtkImageData input, double angle, int axis, int out_extent = 0) { //get input data dimensions int[] dims = input.GetExtent(); //Compute centers int[] centers = new int[] { (dims[1] + dims[0]) / 2, (dims[3] + dims[2]) / 2, (dims[5] + dims[4]) / 2 }; //Set rotation axis int[] axes = new int[3]; axes[axis] = 1; int[] new_dims = new int[] { dims[0], dims[1], dims[2], dims[3], dims[4], dims[5] }; int[] new_centers = new int[] { centers[0], centers[1], centers[2] }; //Compute new sample dimensions if (axis == 0) { new_dims[3] = (int)(Math.Cos(Math.Abs(angle / 180) * Math.PI) * new_dims[3] + Math.Sin(Math.Abs(angle / 180) * Math.PI) * new_dims[5]); new_dims[5] = (int)(Math.Sin(Math.Abs(angle / 180) * Math.PI) * new_dims[3] + Math.Cos(Math.Abs(angle / 180) * Math.PI) * new_dims[5]); new_centers[1] = (Math.Abs(new_dims[3]) + Math.Abs(new_dims[2])) / 2; new_centers[2] = (Math.Abs(new_dims[5]) + Math.Abs(new_dims[4])) / 2; } if (axis == 1) { new_dims[1] = (int)(Math.Cos(Math.Abs(angle / 180) * Math.PI) * new_dims[1] + Math.Sin(Math.Abs(angle / 180) * Math.PI) * new_dims[5]); new_dims[5] = (int)(Math.Sin(Math.Abs(angle / 180) * Math.PI) * new_dims[1] + Math.Cos(Math.Abs(angle / 180) * Math.PI) * new_dims[5]); new_centers[0] = (Math.Abs(new_dims[0]) + Math.Abs(new_dims[1])) / 2; new_centers[2] = (Math.Abs(new_dims[5]) + Math.Abs(new_dims[4])) / 2; } //Image transformation vtkTransform transform = vtkTransform.New(); transform.Translate(centers[0], centers[1], centers[2]); transform.RotateWXYZ(angle, axes[0], axes[1], axes[2]); if (out_extent == 0) { transform.Translate(-centers[0], -centers[1], -centers[2]); } else { transform.Translate(-new_centers[0], -new_centers[1], -new_centers[2]); } //Console.ReadKey(); transform.Update(); //Compute new data extent int[] diff = new int[] { new_dims[1] - dims[1], new_dims[3] - dims[3], new_dims[5] - dims[5] }; new_dims[0] += diff[0] / 2; new_dims[1] -= diff[0] / 2; new_dims[2] += diff[1] / 2; new_dims[3] -= diff[1] / 2; new_dims[4] += diff[2] / 2; new_dims[5] -= diff[2] / 2; //Image reslicing vtkImageReslice rotater = vtkImageReslice.New(); rotater.SetInput(input); rotater.SetInformationInput(input); rotater.SetResliceTransform(transform); rotater.SetInterpolationModeToCubic(); //rotater.SetInterpolationModeToLinear(); if (out_extent == 1) { rotater.SetOutputSpacing(input.GetSpacing()[0], input.GetSpacing()[1], input.GetSpacing()[2]); rotater.SetOutputOrigin(input.GetOrigin()[0], input.GetOrigin()[1], input.GetOrigin()[2]); rotater.SetOutputExtent(new_dims[0], new_dims[1], new_dims[2], new_dims[3], new_dims[4], new_dims[5]); } rotater.Update(); vtkImageData output = vtkImageData.New(); output.DeepCopy(rotater.GetOutput()); rotater.Dispose(); transform.Dispose(); return(output); }
public static vtkImageData OCVFPSuppression(vtkImageData BCI, vtkImageData sample, int axis = 0, double BC_threshold = 0.7 * 255.0, double sample_threshold = 80) { //Get dimensions int[] dims = BCI.GetExtent(); //Get byte data from input samples byte[] BCIByte = DataTypes.vtkToByte(BCI); byte[] SampleByte = DataTypes.vtkToByte(sample); //Output byte array byte[,,] OutByte = new byte[(dims[1] - dims[0] + 1), (dims[3] - dims[2] + 1), (dims[5] - dims[4] + 1)]; //Loop parameters int start = 0; int stop = 0; int h = 0; int w = 0; int strideh = 0; int stridew = 0; int pos = 0; if (axis == 0) { start = dims[0]; stop = dims[1] + 1; h = dims[5] + 1; w = dims[3] + 1; strideh = (dims[1] + 1) * (dims[3] + 1); stridew = 1; } if (axis == 1) { start = dims[2]; stop = dims[3] + 1; h = dims[5] + 1; w = dims[1] + 1; strideh = (dims[1] + 1) * (dims[3] + 1); stridew = (dims[3] + 1); } if (axis == 2) { start = dims[4]; stop = dims[5] + 1; h = dims[1] + 1; w = dims[3] + 1; strideh = dims[3] + 1; stridew = 1; } //Iterate over samples in parallel Parallel.For(start, stop, (int k) => { //Get slices to OpenCV matrices Mat BCMat = new Mat(h, w, MatType.CV_8UC1); var BCIndexer = BCMat.GetGenericIndexer <Vec2b>(); Mat SampleMat = new Mat(h, w, MatType.CV_8UC1); var SampleIndexer = SampleMat.GetGenericIndexer <Vec2b>(); //Iterate over indices Parallel.For(0, h, (int kh) => { Parallel.For(0, w, (int kw) => { //Current slice index if (axis == 0) { pos = k * (dims[1] + 1); } ; if (axis == 1) { pos = k; } ; if (axis == 2) { pos = k * (dims[1] + 1) * (dims[3] + 1); } ; //Current index int cur = pos + kh * strideh + kw * stridew; //Get values Vec2b bcval = BCIndexer[kh, kw]; bcval.Item0 = BCIByte[cur]; bcval.Item1 = BCIByte[cur]; Vec2b sampleval = SampleIndexer[kh, kw]; sampleval.Item0 = SampleByte[cur]; sampleval.Item1 = SampleByte[cur]; //Update matrices BCIndexer[kh, kw] = bcval; SampleIndexer[kh, kw] = sampleval; }); }); if (k == 500) { using (var window = new Window("BCMat", image: BCMat, flags: WindowMode.AutoSize)) { Cv2.WaitKey(); } using (var window = new Window("Sample", image: SampleMat, flags: WindowMode.AutoSize)) { Cv2.WaitKey(); } } //Get binary masks Mat BCBW = BCMat.Threshold(BC_threshold, 255.0, ThresholdTypes.Binary); Mat SampleBW = SampleMat.Threshold(sample_threshold, 255.0, ThresholdTypes.Binary); if (k == 500) { using (var window = new Window("BCMat", image: BCBW, flags: WindowMode.AutoSize)) { Cv2.WaitKey(); } using (var window = new Window("Sample", image: SampleBW, flags: WindowMode.AutoSize)) { Cv2.WaitKey(); } } //Subtract BCI mask from sample mask Mat D = SampleBW - BCBW; if (k == 500) { using (var window = new Window("D", image: D, flags: WindowMode.AutoSize)) { Cv2.WaitKey(); } } //Closing //Generate structuring element InputArray element = InputArray.Create(new Mat(1, 50, MatType.CV_8UC1)); D = D.Dilate(element); D = D.Erode(element); if (k == 500) { using (var window = new Window("D", image: D, flags: WindowMode.AutoSize)) { Cv2.WaitKey(); } } //Subtract closed mask from BCI mask BCBW = BCBW - D; if (k == 500) { using (var window = new Window("Cleaned BC", image: BCBW, flags: WindowMode.AutoSize)) { Cv2.WaitKey(); } } //Dilate BCBW = BCBW.Dilate(element); if (k == 500) { using (var window = new Window("Dilated BC", image: BCBW, flags: WindowMode.AutoSize)) { Cv2.WaitKey(); } } //Multiply BCI matrix with the mask BCMat = BCMat.Mul(BCBW); BCIndexer = BCMat.GetGenericIndexer <Vec2b>(); if (k == 500) { using (var window = new Window("Dilated BC", image: BCBW, flags: WindowMode.AutoSize)) { Cv2.WaitKey(); } } //Return the elements to output byte array Parallel.For(0, h, (int kh) => { Parallel.For(0, w, (int kw) => { //Get value Vec2b bcval = BCIndexer[kh, kw]; //Update outputarray if (axis == 0 && bcval.Item0 > BC_threshold) { OutByte[kh, k, kw] = 255; } ; if (axis == 1 && bcval.Item0 > BC_threshold) { OutByte[kh, kw, k] = 255; } ; if (axis == 2 && bcval.Item0 > BC_threshold) { OutByte[k, kh, kw] = 255; } ; }); }); }); //Convert outputarray to VTK data return(DataTypes.byteToVTK(OutByte)); }
/// <summary> /// Calculates center of volume data along z-axis. /// </summary> /// <param name="stack"></param> /// <param name="threshold"></param> /// <param name="zrange"></param> /// <returns></returns> public static int[] find_center(vtkImageData stack, double threshold = 70.0, int[] zrange = null) { //Get byte data byte[] bytedata = DataTypes.vtkToByte(stack); //Get data dimensions int[] dims = stack.GetExtent(); int h = dims[1] - dims[0] + 1; int w = dims[3] - dims[2] + 1; int d = dims[5] - dims[4] + 1; //Get strides int stride_d = h * w; int stride_h = 1; int stride_w = h; //Empty array for binary mask byte[,] BW = new byte[h, w]; //z range int zstart = 0; int zstop = 0; if (zrange == null) { zstart = 0; zstop = d; } else { zstart = zrange[0]; zstop = zrange[1]; } Parallel.For(0, h, (int y) => { Parallel.For(0, w, (int x) => { for (int z = zstart; z < zstop; z++) { int pos = (z * stride_d) + (x * stride_w) + (y * stride_h); byte val = bytedata[pos]; if (val > threshold) { BW[y, x] = 255; } } }); }); Mat sumim = new Mat(h, w, MatType.CV_8UC1, BW); //Get largest binary object sumim = Functions.largest_connected_component(sumim); int x1; int x2; int y1; int y2; Functions.get_bbox(out x1, out x2, out y1, out y2, sumim); //Compute center int[] center = new int[2]; center[0] = (y2 + y1) / 2 + dims[0]; center[1] = (x2 + x1) / 2 + dims[2]; return(center); }
/// <summary> /// Scans the input mask slice by slice and selects the largest binary component of each slice. /// Return cleaned mask as vtkImageData /// </summary> /// <param name="input"></param> /// <param name="extent"></param> /// <param name="threshold"></param> /// <param name="axes"></param> /// <returns></returns> public static vtkImageData FalsePositiveSuppresion(vtkImageData input, int[] extent, double threshold, int axis, double scale = 1.0) { //Slice extractor vtkExtractVOI slicer = vtkExtractVOI.New(); //Permuter vtkImagePermute permuter = vtkImagePermute.New(); //List of outputs List <byte[, , ]> outputs = new List <byte[, , ]>(); //List of output orientations List <int[]> orientations = new List <int[]>(); //vtkImageData size int[] full_extent = input.GetExtent(); //Set range for slices int start = 0, stop = 0; int[] size = new int[2]; int[] outextent = new int[4]; if (axis == 0) { start = extent[0]; stop = extent[1]; size = new int[] { extent[3] - extent[2] + 1, extent[5] - extent[4] + 1 }; outextent = new int[] { extent[2], extent[3] + 1, extent[4], extent[5] + 1 }; } if (axis == 1) { start = extent[2]; stop = extent[3]; size = new int[] { extent[1] - extent[0] + 1, extent[5] - extent[4] + 1 }; outextent = new int[] { extent[0], extent[1] + 1, extent[4], extent[5] + 1 }; } if (axis == 2) { start = extent[4]; stop = extent[5]; size = new int[] { extent[1] - extent[0] + 1, extent[3] - extent[2] + 1 }; outextent = new int[] { extent[0], extent[1] + 1, extent[2], extent[3] + 1 }; } //Temporary array for output byte[,,] output = new byte[full_extent[1] + 1, full_extent[3] + 1, full_extent[5] + 1]; int[] outsize = new int[] { size[0], size[1], stop - start + 1 }; //Loop over current axis for (int k = start; k < stop; k++) { byte[] bytedata = new byte[size[0] * size[1]]; //Select slice if (axis == 0) { slicer.Dispose(); slicer = vtkExtractVOI.New(); slicer.SetInput(input); slicer.SetVOI(k, k, extent[2], extent[3], extent[4], extent[5]); slicer.Update(); permuter.Dispose(); permuter = vtkImagePermute.New(); permuter.SetInput(slicer.GetOutput()); permuter.SetFilteredAxes(1, 2, 0); permuter.Update(); } if (axis == 1) { slicer.Dispose(); slicer = vtkExtractVOI.New(); slicer.SetInput(input); slicer.SetVOI(extent[0], extent[1], k, k, extent[4], extent[5]); slicer.Update(); permuter.Dispose(); permuter = vtkImagePermute.New(); permuter.SetInput(slicer.GetOutput()); permuter.SetFilteredAxes(0, 2, 1); permuter.Update(); } if (axis == 2) { slicer.Dispose(); slicer = vtkExtractVOI.New(); slicer.SetInput(input); slicer.SetVOI(extent[0], extent[1], extent[2], extent[3], k, k); slicer.Update(); permuter.Dispose(); permuter = vtkImagePermute.New(); permuter.SetInput(slicer.GetOutput()); permuter.SetFilteredAxes(0, 1, 2); permuter.Update(); } //Convert data to byte bytedata = DataTypes.vtkToByte(permuter.GetOutput()); slicer.Dispose(); permuter.Dispose(); //convert data to Mat Mat image = new Mat(size[1], size[0], MatType.CV_8UC1, bytedata); //Get largest binary object Mat bw = Processing.LargestBWObject(image, 0.7 * 255.0); //Set slice to byte array if (bw.Sum().Val0 > 0) { output = DataTypes.setByteSlice(output, bw, outextent, axis, k); } } return(DataTypes.byteToVTK(output)); }