/// <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> /// Extract data as vtkImageData /// </summary> /// <returns>Converted data as vtkImageData variable.</returns> public vtkImageData GetData() { //Conver byte data to vtkImageData vtkdata = DataTypes.byteToVTK(data); return(vtkdata); }
/// <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> /// 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(); }
/// <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> /// 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; }
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)); }
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> /// 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)); }