This commit is contained in:
2025-09-04 14:08:52 +08:00
parent 335216092e
commit d050ad9304

View File

@@ -0,0 +1,324 @@
package org.dromara.easyai.tools;
import org.dromara.easyai.config.WaterConfig;
import org.dromara.easyai.matrixTools.Matrix;
import org.dromara.easyai.entity.Box;
import org.dromara.easyai.entity.ThreeChannelMatrix;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* @author lidapeng
* @description 分水岭
* @date 10:25 上午 2020/1/13
*/
public class Watershed {
private final Matrix matrix;
private final Matrix rainfallMap;//降雨图
private final Matrix regionMap;//分区图
private final int xSize;//单元高度
private final int ySize;//单元宽度
private final float rainTh;//灰度阈值
private final int regionNub;//一张图分多少份
private final Map<Integer, RegionBody> regionBodyMap = new HashMap<>();
private final int xMax;
private final int yMax;
private final int cutMinXSize;//分水岭切割最小取样X
private final int cutMinYSize;//分水岭切割最小取样Y
private final int cutMaxXSize;//分水岭切割最大取样X
private final int cutMaxYSize;//分水岭切割最大取样Y
private final float th;//落差阈值阈值
public Watershed(ThreeChannelMatrix matrix, WaterConfig config) throws Exception {
if (matrix != null) {
th = config.getTh();
if (th < 0) {
throw new Exception("落差不能小于0");
}
cutMaxXSize = config.getCutMaxXSize();
cutMaxYSize = config.getCutMaxYSize();
cutMinXSize = config.getMinXSizeTh() + 2;
cutMinYSize = config.getMinYSizeTh() + 2;
rainTh = config.getRainTh();//降雨密度图
regionNub = config.getRegionNub();//区域大小
this.matrix = matrix.getH();
xSize = this.matrix.getX() / regionNub;
ySize = this.matrix.getY() / regionNub;
rainfallMap = new Matrix(this.matrix.getX(), this.matrix.getY());
regionMap = new Matrix(regionNub, regionNub);
xMax = rainfallMap.getX() - 1;
yMax = rainfallMap.getY() - 1;
} else {
throw new Exception("matrix is null");
}
}
private float[] getPixels(int x, int y) throws Exception {
//八方向取值
float left = -1;//左边
float leftTop = -1;//左上
float leftBottom = -1;//左下
float right = -1;//右边
float rightTop = -1;//右上
float rightBottom = -1;//右下
float top = -1;//上边
float bottom = -1;//下边
if (x == 0) {
top = 1;
leftTop = 1;
rightTop = 1;
}
if (y == 0) {
leftTop = 1;
left = 1;
leftBottom = 1;
}
if (x == xMax) {
leftBottom = 1;
bottom = 1;
rightBottom = 1;
}
if (y == yMax) {
rightTop = 1;
right = 1;
rightBottom = 1;
}
if (top == -1 && rainfallMap.getNumber(x - 1, y) == 0) {
top = matrix.getNumber(x - 1, y);
}
if (left == -1 && rainfallMap.getNumber(x, y - 1) == 0) {
left = matrix.getNumber(x, y - 1);
}
if (bottom == -1 && rainfallMap.getNumber(x + 1, y) == 0) {
bottom = matrix.getNumber(x + 1, y);
}
if (right == -1 && rainfallMap.getNumber(x, y + 1) == 0) {
right = matrix.getNumber(x, y + 1);
}
if (leftTop == -1 && rainfallMap.getNumber(x - 1, y - 1) == 0) {
leftTop = matrix.getNumber(x - 1, y - 1);
}
if (leftBottom == -1 && rainfallMap.getNumber(x + 1, y - 1) == 0) {
leftBottom = matrix.getNumber(x + 1, y - 1);
}
if (rightTop == -1 && rainfallMap.getNumber(x - 1, y + 1) == 0) {
rightTop = matrix.getNumber(x - 1, y + 1);
}
if (rightBottom == -1 && rainfallMap.getNumber(x + 1, y + 1) == 0) {
rightBottom = matrix.getNumber(x + 1, y + 1);
}
return new float[]{top, left, bottom, right, leftTop, leftBottom, rightBottom, rightTop};
}
private int[] rain(int x, int y) throws Exception {//先往下降,直到不能再降了为止
//有两种情况停止1最小值是自身。2周围已经灌满水了,包括自身
float[] pixels = getPixels(x, y);
int[] point = new int[8];
float mySelf;
mySelf = matrix.getNumber(x, y);
int index = getMinIndex(pixels, mySelf);//最低点下标
int row;
int column;
if (index > 0) {//存在可向下蔓延的点
for (int i = 0; i < 8; i++) {
int t = index & (1 << i);
if (t > 0) {
row = x;
column = y;
switch (i) {
case 0://上
row = x - 1;
break;
case 1://左
column = y - 1;
break;
case 2://下
row = x + 1;
break;
case 3://右
column = y + 1;
break;
case 4://左上
column = y - 1;
row = x - 1;
break;
case 5://左下
column = y - 1;
row = x + 1;
break;
case 6://右下
column = y + 1;
row = x + 1;
break;
case 7://右上
column = y + 1;
row = x - 1;
break;
}
int pixel = row << 12 | column;
//等待继续往下沉降的点
point[i] = pixel;
//降雨图修改
rainfallMap.setNub(row, column, 1);
}
}
}
return point;
}
private void pull(List<Integer> list, int[] points) {
for (int point : points) {
if (point != 0) {
list.add(point);
}
}
}
private void fall(int i, int j) throws Exception {
List<Integer> list = new ArrayList<>();
list.add((i << 12) | j);
do {
List<Integer> list2 = new ArrayList<>();
for (int pixel : list) {
int x = pixel >> 12;
int y = pixel & 0xfff;
int[] nodes = rain(x, y);
pull(list2, nodes);
}
list = list2;
} while (!list.isEmpty());
}
public List<Box> rainfall() throws Exception {//开始降雨
int x = matrix.getX();
int y = matrix.getY();
for (int i = 0; i < x; i++) {
for (int j = 0; j < y; j++) {
if (rainfallMap.getNumber(i, j) == 0) {//进行降雨
fall(i, j);
}
}
}
//进行区域提取
sigmaPixel();
List<Box> boxes = new ArrayList<>();
for (Map.Entry<Integer, RegionBody> entry : regionBodyMap.entrySet()) {
RegionBody regionBody = entry.getValue();
int minX = regionBody.getMinX();
int maxX = regionBody.getMaxX();
int minY = regionBody.getMinY();
int maxY = regionBody.getMaxY();
int xSize = maxX - minX;
int ySize = maxY - minY;
if (xSize >= cutMinXSize && ySize >= cutMinYSize && xSize < cutMaxXSize && ySize < cutMaxYSize) {
Box box = new Box();
box.setX(minX);
box.setY(minY);
box.setRealX(minX + xSize / 2);
box.setRealY(minY + ySize / 2);
box.setxSize(xSize);
box.setySize(ySize);
boxes.add(box);
}
}
return boxes;
}
private void merge() throws Exception {
int xSize = regionMap.getX();
int ySize = regionMap.getY();
for (int i = 0; i < xSize - 1; i++) {//132
List<Integer> list = new ArrayList<>();
for (int j = 0; j < ySize; j++) {
int type = (int) regionMap.getNumber(i, j);
if (type > 1 && i + 1 < regionNub) {
int otherType = (int) regionMap.getNumber(i + 1, j);
if (otherType > 1 && otherType != type) {
if (!list.contains(otherType)) {
RegionBody myRegion = regionBodyMap.get(type);
RegionBody otherRegion = regionBodyMap.get(otherType);
myRegion.mergeRegion(otherRegion);
regionBodyMap.remove(otherType);
list.add(otherType);
}
}
}
}
}
}
private void createMerge() throws Exception {
int x = regionMap.getX();
int y = regionMap.getY();
int t = 0;
boolean isZero = false;
for (int i = 0; i < x; i++) {
if (!isZero) {
t++;
}
boolean isFirstOne = false;
for (int j = 0; j < y; j++) {
int type = (int) regionMap.getNumber(i, j);
if (type == 1) {
RegionBody regionBody;
if (regionBodyMap.containsKey(t)) {
regionBody = regionBodyMap.get(t);
} else {
regionBody = new RegionBody(regionMap, t, xSize, ySize);
regionBodyMap.put(t, regionBody);
}
regionBody.setPoint(i, j);
isFirstOne = true;
isZero = false;
} else if (isFirstOne) {
if (!isZero) {
t++;
}
isZero = true;
}
}
}
}
private void sigmaPixel() throws Exception {//生成降雨密度图
int size = xSize * ySize;
for (int i = 0; i < xSize * regionNub; i += xSize) {
for (int j = 0; j < ySize * regionNub; j += ySize) {
Matrix myMatrix = rainfallMap.getSonOfMatrix(i, j, xSize, ySize);
int sigma = 0;
for (int t = 0; t < xSize; t++) {
for (int f = 0; f < ySize; f++) {
if (myMatrix.getNumber(t, f) > 0.1) {
sigma++;
}
}
}
float cover = (float) sigma / (float) size;//降雨率产生剧烈波动时则出现坐标
if (cover > rainTh) {//降雨密度图
regionMap.setNub(i / xSize, j / ySize, 1);
}
}
}
//System.out.println(regionMap.getString());
createMerge();//提取候选区
merge();//合并候选区
}
private int getMinIndex(float[] array, float mySelf) {//获取最小值
int minIdx = 0;
for (int i = 0; i < array.length; i++) {
float nub = array[i];
float sub = mySelf - nub;
if (nub > -1 && sub > th) {
minIdx = minIdx | (1 << i);
}
}
return minIdx;
}
}