/** * Compute initial cluster centers for clusters in all 3 categories: vol, dbl, suf. * * @param srcBandList the input bands * @param tileRectangles Array of rectangles for all source tiles of the image * @param op the operator */ private void computeInitialTerrainClusterCenters( final double[][] fdd, final java.util.List<ClusterInfo> pvCenterList, final java.util.List<ClusterInfo> pdCenterList, final java.util.List<ClusterInfo> psCenterList, final PolBandUtils.PolSourceBand srcBandList, final Rectangle[] tileRectangles, final PolarimetricClassificationOp op) { try { // Step 1. Create initial 30 clusters in each of the 3 categories (vol, dbl, surf). // System.out.println("Step 1"); createInitialClusters(fdd, srcBandList, tileRectangles, op); // Step 2. Compute cluster centers for all 90 clusters in the 3 categories // System.out.println("Step 2"); getClusterCenters(pvCenterList, pdCenterList, psCenterList, srcBandList, tileRectangles, op); // Step 3. Merge small clusters in each category until user specified total number of clusters // is reached // System.out.println("Step 3"); mergeInitialClusters(pvCenterList, pdCenterList, psCenterList); } catch (Throwable e) { OperatorUtils.catchOperatorException(op.getId() + " computeInitialClusterCenters ", e); } }
/** * Compute centers for all numClasses clusters * * @param srcBandList the input bands * @param op the operator */ private synchronized void computeTerrainClusterCenters( final PolBandUtils.PolSourceBand srcBandList, final PolarimetricClassificationOp op) { if (clusterCentersComputed) { return; } mask = new byte[srcHeight][srcWidth]; final double[][] fdd = new double[srcHeight][srcWidth]; final java.util.List<ClusterInfo> pvCenterList = new ArrayList<>(numInitialClusters); final java.util.List<ClusterInfo> pdCenterList = new ArrayList<>(numInitialClusters); final java.util.List<ClusterInfo> psCenterList = new ArrayList<>(numInitialClusters); maxClusterSize = 2 * srcHeight * srcWidth / numFinalClasses; final Dimension tileSize = new Dimension(256, 256); final Rectangle[] tileRectangles = OperatorUtils.getAllTileRectangles(op.getSourceProduct(), tileSize, 0); computeInitialTerrainClusterCenters( fdd, pvCenterList, pdCenterList, psCenterList, srcBandList, tileRectangles, op); computeFinalTerrainClusterCenters( fdd, pvCenterList, pdCenterList, psCenterList, srcBandList, tileRectangles, op); clusterCentersComputed = true; }
/** * Compute final cluster centers for all clusters using K-mean clustering method * * @param srcBandList the input bands * @param tileRectangles Array of rectangles for all source tiles of the image * @param op the operator */ private void computeFinalTerrainClusterCenters( final double[][] fdd, final java.util.List<ClusterInfo> pvCenterList, final java.util.List<ClusterInfo> pdCenterList, final java.util.List<ClusterInfo> psCenterList, final PolBandUtils.PolSourceBand srcBandList, final Rectangle[] tileRectangles, final PolarimetricClassificationOp op) { boolean endIteration = false; final StatusProgressMonitor status = new StatusProgressMonitor(StatusProgressMonitor.TYPE.SUBTASK); status.beginTask("Computing Final Cluster Centres... ", tileRectangles.length * maxIterations); final int pvNumClusters = pvCenterList.size(); final int pdNumClusters = pdCenterList.size(); final int psNumClusters = psCenterList.size(); final int maxNumClusters = Math.max(pvNumClusters, Math.max(pdNumClusters, psNumClusters)); final int[][] clusterCounter = new int[3][maxNumClusters]; final ThreadManager threadManager = new ThreadManager(); try { for (int it = 0; (it < maxIterations && !endIteration); ++it) { // System.out.println("Iteration: " + it); // final long startTime = System.nanoTime(); // final long endTime; final double[][][] pvSumRe = new double[pvNumClusters][3][3]; final double[][][] pvSumIm = new double[pvNumClusters][3][3]; final double[][][] pdSumRe = new double[pdNumClusters][3][3]; final double[][][] pdSumIm = new double[pdNumClusters][3][3]; final double[][][] psSumRe = new double[psNumClusters][3][3]; final double[][][] psSumIm = new double[psNumClusters][3][3]; java.util.Arrays.fill(clusterCounter[0], 0); java.util.Arrays.fill(clusterCounter[1], 0); java.util.Arrays.fill(clusterCounter[2], 0); for (final Rectangle rectangle : tileRectangles) { final Thread worker = new Thread() { final Tile[] sourceTiles = new Tile[srcBandList.srcBands.length]; final ProductData[] dataBuffers = new ProductData[srcBandList.srcBands.length]; final double[][] Tr = new double[3][3]; final double[][] Ti = new double[3][3]; @Override public void run() { op.checkIfCancelled(); final int x0 = rectangle.x; final int y0 = rectangle.y; final int w = rectangle.width; final int h = rectangle.height; final int xMax = x0 + w; final int yMax = y0 + h; final Rectangle sourceRectangle = getSourceRectangle(x0, y0, w, h); for (int i = 0; i < sourceTiles.length; ++i) { sourceTiles[i] = op.getSourceTile(srcBandList.srcBands[i], sourceRectangle); dataBuffers[i] = sourceTiles[i].getDataBuffer(); } final TileIndex srcIndex = new TileIndex(sourceTiles[0]); for (int y = y0; y < yMax; ++y) { for (int x = x0; x < xMax; ++x) { PolOpUtils.getMeanCoherencyMatrix( x, y, halfWindowSizeX, halfWindowSizeY, srcWidth, srcHeight, sourceProductType, srcIndex, dataBuffers, Tr, Ti); int clusterIdx; synchronized (clusterCounter) { if (mask[y][x] < -64) { // pv clusterIdx = findClosestCluster(Tr, Ti, pvCenterList); computeSummationOfT3(clusterIdx + 1, Tr, Ti, pvSumRe, pvSumIm); clusterCounter[0][clusterIdx] += 1; mask[y][x] = (byte) (-128 + clusterIdx); } else if (mask[y][x] < 0) { // pd clusterIdx = findClosestCluster(Tr, Ti, pdCenterList); computeSummationOfT3(clusterIdx + 1, Tr, Ti, pdSumRe, pdSumIm); clusterCounter[1][clusterIdx] += 1; mask[y][x] = (byte) (-64 + clusterIdx); } else if (mask[y][x] < 64) { // ps clusterIdx = findClosestCluster(Tr, Ti, psCenterList); computeSummationOfT3(clusterIdx + 1, Tr, Ti, psSumRe, psSumIm); clusterCounter[2][clusterIdx] += 1; mask[y][x] = (byte) clusterIdx; } else { // mixed java.util.List<ClusterInfo> allCenterList = new ArrayList<>(); allCenterList.addAll(pvCenterList); allCenterList.addAll(pdCenterList); allCenterList.addAll(psCenterList); clusterIdx = findClosestCluster(Tr, Ti, allCenterList); if (clusterIdx >= pvNumClusters + pdNumClusters) { // ps clusterIdx -= pvNumClusters + pdNumClusters; computeSummationOfT3(clusterIdx + 1, Tr, Ti, psSumRe, psSumIm); clusterCounter[2][clusterIdx] += 1; mask[y][x] = (byte) clusterIdx; } else if (clusterIdx >= pvNumClusters) { // pd clusterIdx -= pvNumClusters; computeSummationOfT3(clusterIdx + 1, Tr, Ti, pdSumRe, pdSumIm); clusterCounter[1][clusterIdx] += 1; mask[y][x] = (byte) (-64 + clusterIdx); } else { // pv computeSummationOfT3(clusterIdx + 1, Tr, Ti, pvSumRe, pvSumIm); clusterCounter[0][clusterIdx] += 1; mask[y][x] = (byte) (-128 + clusterIdx); } } } } } } }; threadManager.add(worker); status.worked(1); } threadManager.finish(); /* endTime = System.nanoTime(); final long duration = endTime - startTime; System.out.println("duration = " + duration); */ updateClusterCenter(pvCenterList, clusterCounter[0], pvSumRe, pvSumIm); updateClusterCenter(pdCenterList, clusterCounter[1], pdSumRe, pdSumIm); updateClusterCenter(psCenterList, clusterCounter[2], psSumRe, psSumIm); } /* System.out.println("# of clusters in Pv: " + pvNumClusters); System.out.print("Pixels in each Pv cluster: "); for (int i = 0; i < pvNumClusters; i++) { System.out.print(clusterCounter[0][i] + ", "); } System.out.println(); System.out.println("# of clusters in Pd: " + pdNumClusters); System.out.print("Pixels in each Pd cluster: "); for (int i = 0; i < pdNumClusters; i++) { System.out.print(clusterCounter[1][i] + ", "); } System.out.println(); System.out.println("# of clusters in Ps: " + psNumClusters); System.out.print("Pixels in each Ps cluster: "); for (int i = 0; i < psNumClusters; i++) { System.out.print(clusterCounter[2][i] + ", "); } System.out.println(); */ // todo the average cluster power should be used in colour selection for each cluster, not // used now // compute average power for each cluster final double[] pvAvgClusterPower = new double[pvNumClusters]; final double[] pdAvgClusterPower = new double[pdNumClusters]; final double[] psAvgClusterPower = new double[psNumClusters]; int clusterIdx = -1; for (int y = 0; y < srcHeight; y++) { for (int x = 0; x < srcWidth; x++) { if (mask[y][x] < -64) { // pv clusterIdx = mask[y][x] + 128; pvAvgClusterPower[clusterIdx] += fdd[y][x]; } else if (mask[y][x] < 0) { // pd clusterIdx = mask[y][x] + 64; pdAvgClusterPower[clusterIdx] += fdd[y][x]; } else { // ps clusterIdx = mask[y][x]; psAvgClusterPower[clusterIdx] += fdd[y][x]; } } } for (int c = 0; c < pvNumClusters; c++) { pvAvgClusterPower[c] /= clusterCounter[0][c]; } for (int c = 0; c < pdNumClusters; c++) { pdAvgClusterPower[c] /= clusterCounter[1][c]; } for (int c = 0; c < psNumClusters; c++) { psAvgClusterPower[c] /= clusterCounter[2][c]; } // map cluster index to colour index, colour index ranges for the 3 categories are given by // surf: [0,29], vol: [30, 59] and dbl: [60,89], currently the clusters are evenly distributed // in the // colour range pvColourIndexMap = new int[pvNumClusters]; pdColourIndexMap = new int[pdNumClusters]; psColourIndexMap = new int[psNumClusters]; for (int c = 0; c < pvNumClusters; c++) { pvColourIndexMap[c] = numInitialClusters + getColourIndex(c, pvAvgClusterPower, numInitialClusters) + 1; } for (int c = 0; c < pdNumClusters; c++) { pdColourIndexMap[c] = 2 * numInitialClusters + getColourIndex(c, pdAvgClusterPower, numInitialClusters) + 1; } for (int c = 0; c < psNumClusters; c++) { psColourIndexMap[c] = getColourIndex(c, psAvgClusterPower, numInitialClusters) + 1; } } catch (Throwable e) { OperatorUtils.catchOperatorException(op.getId() + " computeInitialClusterCenters ", e); } finally { status.done(); } }
/** * Compute the centers of the 90 clusters in the 3 categories. * * @param srcBandList the input bands * @param tileRectangles Array of rectangles for all source tiles of the image * @param op the operator */ private void getClusterCenters( final java.util.List<ClusterInfo> pvCenterList, final java.util.List<ClusterInfo> pdCenterList, final java.util.List<ClusterInfo> psCenterList, final PolBandUtils.PolSourceBand srcBandList, final Rectangle[] tileRectangles, final PolarimetricClassificationOp op) { final StatusProgressMonitor status = new StatusProgressMonitor(StatusProgressMonitor.TYPE.SUBTASK); status.beginTask("Computing Initial Cluster Centres... ", tileRectangles.length); final ThreadManager threadManager = new ThreadManager(); final double[][][] pvSumRe = new double[numInitialClusters][3][3]; final double[][][] pvSumIm = new double[numInitialClusters][3][3]; final double[][][] pdSumRe = new double[numInitialClusters][3][3]; final double[][][] pdSumIm = new double[numInitialClusters][3][3]; final double[][][] psSumRe = new double[numInitialClusters][3][3]; final double[][][] psSumIm = new double[numInitialClusters][3][3]; // counter for the number of pixels in each cluster in the 3 categories: vol, dbl, suf final int[][] clusterCounter = new int[3][numInitialClusters]; try { for (final Rectangle rectangle : tileRectangles) { op.checkIfCancelled(); final Thread worker = new Thread() { final Tile[] sourceTiles = new Tile[srcBandList.srcBands.length]; final ProductData[] dataBuffers = new ProductData[srcBandList.srcBands.length]; final double[][] Sr = new double[2][2]; final double[][] Si = new double[2][2]; final double[][] Tr = new double[3][3]; final double[][] Ti = new double[3][3]; @Override public void run() { final int x0 = rectangle.x; final int y0 = rectangle.y; final int w = rectangle.width; final int h = rectangle.height; final int xMax = x0 + w; final int yMax = y0 + h; // System.out.println("x0 = " + x0 + ", y0 = " + y0 + ", w = " + w + ", h = " + h); for (int i = 0; i < sourceTiles.length; ++i) { sourceTiles[i] = op.getSourceTile(srcBandList.srcBands[i], rectangle); dataBuffers[i] = sourceTiles[i].getDataBuffer(); } final TileIndex srcIndex = new TileIndex(sourceTiles[0]); for (int y = y0; y < yMax; ++y) { srcIndex.calculateStride(y); for (int x = x0; x < xMax; ++x) { PolOpUtils.getT3(srcIndex.getIndex(x), sourceProductType, dataBuffers, Tr, Ti); synchronized (clusterCounter) { int clusterIdx; if (mask[y][x] < -64) { // pv clusterIdx = mask[y][x] + 128; computeSummationOfT3(clusterIdx + 1, Tr, Ti, pvSumRe, pvSumIm); clusterCounter[0][clusterIdx]++; } else if (mask[y][x] < 0) { // pd clusterIdx = mask[y][x] + 64; computeSummationOfT3(clusterIdx + 1, Tr, Ti, pdSumRe, pdSumIm); clusterCounter[1][clusterIdx]++; } else if (mask[y][x] < 64) { // ps clusterIdx = mask[y][x]; computeSummationOfT3(clusterIdx + 1, Tr, Ti, psSumRe, psSumIm); clusterCounter[2][clusterIdx]++; } } } } } }; threadManager.add(worker); status.worked(1); } threadManager.finish(); } catch (Throwable e) { OperatorUtils.catchOperatorException(op.getId() + " getClusterCenters ", e); } finally { status.done(); } // compute centers for all 90 clusters for (int c = 0; c < numInitialClusters; c++) { double[][] centerRe = new double[3][3]; double[][] centerIm = new double[3][3]; if (clusterCounter[0][c] > 0) { for (int i = 0; i < 3; i++) { for (int j = 0; j < 3; j++) { centerRe[i][j] = pvSumRe[c][i][j] / clusterCounter[0][c]; centerIm[i][j] = pvSumIm[c][i][j] / clusterCounter[0][c]; } } ClusterInfo clusterCenter = new ClusterInfo(); clusterCenter.setClusterCenter(c, centerRe, centerIm, clusterCounter[0][c]); pvCenterList.add(clusterCenter); } if (clusterCounter[1][c] > 0) { for (int i = 0; i < 3; i++) { for (int j = 0; j < 3; j++) { centerRe[i][j] = pdSumRe[c][i][j] / clusterCounter[1][c]; centerIm[i][j] = pdSumIm[c][i][j] / clusterCounter[1][c]; } } ClusterInfo clusterCenter = new ClusterInfo(); clusterCenter.setClusterCenter(c, centerRe, centerIm, clusterCounter[1][c]); pdCenterList.add(clusterCenter); } if (clusterCounter[2][c] > 0) { for (int i = 0; i < 3; i++) { for (int j = 0; j < 3; j++) { centerRe[i][j] = psSumRe[c][i][j] / clusterCounter[2][c]; centerIm[i][j] = psSumIm[c][i][j] / clusterCounter[2][c]; } } ClusterInfo clusterCenter = new ClusterInfo(); clusterCenter.setClusterCenter(c, centerRe, centerIm, clusterCounter[2][c]); psCenterList.add(clusterCenter); } } }
/** * Create 30 initial clusters in each of the 3 categories (vol, dbl and surf). The pixels are * first classified into 4 categories (vol, dbl, urf and mixed) based on its Freeman-Durder * decomposition result. Then pixels in each category (not include mixed) are grouped into 30 * clusters based on their power values. * * @param srcBandList the input bands * @param tileRectangles Array of rectangles for all source tiles of the image * @param op the operator */ private void createInitialClusters( final double[][] fdd, final PolBandUtils.PolSourceBand srcBandList, final Rectangle[] tileRectangles, final PolarimetricClassificationOp op) { // Here mask[][] is used in recording the category index for each pixel with -128 for vol, -64 // for dbl, // 0 for surf and 64 for mixed. Later mask[][] will be used in recording for each pixel the // cluster index // in each category. [-128, -63] are for clusters in vol category, [-64, -1] are for clusters in // dbl // category and [0, 63] are for clusters in surf category. // // fdd[][] is used in recording the dominant power of the Freeman-Durden decomposition result // for each // pixel. final StatusProgressMonitor status = new StatusProgressMonitor(StatusProgressMonitor.TYPE.SUBTASK); status.beginTask("Creating Initial Clusters... ", tileRectangles.length); final int[] counter = new int[4]; // number of pixels in each of the 4 categories: vol, dbl, suf, mix final ThreadManager threadManager = new ThreadManager(); final double[] pv = new double[srcHeight * srcWidth]; final double[] pd = new double[srcHeight * srcWidth]; final double[] ps = new double[srcHeight * srcWidth]; try { for (final Rectangle rectangle : tileRectangles) { op.checkIfCancelled(); final Thread worker = new Thread() { final Tile[] sourceTiles = new Tile[srcBandList.srcBands.length]; final ProductData[] dataBuffers = new ProductData[srcBandList.srcBands.length]; final double[][] Cr = new double[3][3]; final double[][] Ci = new double[3][3]; @Override public void run() { final int x0 = rectangle.x; final int y0 = rectangle.y; final int w = rectangle.width; final int h = rectangle.height; final int xMax = x0 + w; final int yMax = y0 + h; // System.out.println("x0 = " + x0 + ", y0 = " + y0 + ", w = " + w + ", h = " + h); final Rectangle sourceRectangle = getSourceRectangle(x0, y0, w, h); for (int i = 0; i < sourceTiles.length; ++i) { sourceTiles[i] = op.getSourceTile(srcBandList.srcBands[i], sourceRectangle); dataBuffers[i] = sourceTiles[i].getDataBuffer(); } for (int y = y0; y < yMax; ++y) { for (int x = x0; x < xMax; ++x) { PolOpUtils.getMeanCovarianceMatrix( x, y, halfWindowSizeX, halfWindowSizeY, sourceProductType, sourceTiles, dataBuffers, Cr, Ci); final FreemanDurden.FDD data = FreemanDurden.getFreemanDurdenDecomposition(Cr, Ci); synchronized (counter) { if (!Double.isNaN(data.pv) && !Double.isNaN(data.pd) && !Double.isNaN(data.ps)) { Categories cat = getCategory(data.pv, data.pd, data.ps, mixedCategoryThreshold); if (cat == Categories.vol) { mask[y][x] = -128; fdd[y][x] = data.pv; pv[counter[0]] = data.pv; counter[0] += 1; } else if (cat == Categories.dbl) { mask[y][x] = -64; fdd[y][x] = data.pd; pd[counter[1]] = data.pd; counter[1] += 1; } else if (cat == Categories.suf) { mask[y][x] = 0; fdd[y][x] = data.ps; ps[counter[2]] = data.ps; counter[2] += 1; } else { // cat == Categories.mix mask[y][x] = 64; fdd[y][x] = (data.pv + data.pd + data.ps) / 3.0; counter[3] += 1; } } } } } } }; threadManager.add(worker); status.worked(1); } threadManager.finish(); } catch (Throwable e) { OperatorUtils.catchOperatorException(op.getId() + " createInitialClusters ", e); } finally { status.done(); } // for each category, compute 29 thresholds which will be used later in dividing each category // into 30 small // clusters with roughly equal number of pixels based on the pixel values. final int pvClusterSize = counter[0] / numInitialClusters; final int pdClusterSize = counter[1] / numInitialClusters; final int psClusterSize = counter[2] / numInitialClusters; if (pvClusterSize > 0) { Arrays.sort(pv, 0, counter[0] - 1); } if (pdClusterSize > 0) { Arrays.sort(pd, 0, counter[1] - 1); } if (psClusterSize > 0) { Arrays.sort(ps, 0, counter[2] - 1); } final double[] pvThreshold = new double[numInitialClusters - 1]; final double[] pdThreshold = new double[numInitialClusters - 1]; final double[] psThreshold = new double[numInitialClusters - 1]; for (int i = 0; i < numInitialClusters - 1; i++) { pvThreshold[i] = pv[(i + 1) * pvClusterSize]; pdThreshold[i] = pd[(i + 1) * pdClusterSize]; psThreshold[i] = ps[(i + 1) * psClusterSize]; } // classify pixels into 30 clusters within each category, record number of pixels in each // cluster int clusterIdx = -1; for (int y = 0; y < srcHeight; y++) { for (int x = 0; x < srcWidth; x++) { if (mask[y][x] == -128) { clusterIdx = computePixelClusterIdx(fdd[y][x], pvThreshold, numInitialClusters); mask[y][x] += clusterIdx; } else if (mask[y][x] == -64) { clusterIdx = computePixelClusterIdx(fdd[y][x], pdThreshold, numInitialClusters); mask[y][x] += clusterIdx; } else if (mask[y][x] == 0) { clusterIdx = computePixelClusterIdx(fdd[y][x], psThreshold, numInitialClusters); mask[y][x] += clusterIdx; } } } }