前面有提到基于Mat变换的骨架提取,然而在实际的应用中处理稍微大点的图片的时候耗时较长就是个问题了,于是针对这个问题寻找了另外一种方法——基于红黑树的骨架提取,这种方法明显处理速度要快一些。
基于红黑树的骨架提取的思路如下:
1,对输入的二值图像进行延拓(直白的说就是在图像的外边界加一圈白点),得到二值图像P;
2,针对骨架提取有8种结构类型(S0~S7)的待删除点。骨架提取的过程实际上就是不断的找符合这8种结构的待删除点,删除待删除点。初始化8个空的红黑树结构T0~T7,初始化current=0,每一个红黑树对应存储一种类别的待删除点,例如:T0存储符合S0结构的待删除点,T1存储符合S1结构的待删除点......;
3,初始化标记矩阵L;
4,遍历P,确定所有待移除点及其对应的红黑树索引值,将待移除点的索引值插入对应的红黑树,;
5,判断T0~T7是否都为空,如果是,则已完成骨架提取,返回P。如果不是全为空,初始化round=0,到下一步;
6,判断round是否等于8,如果是返回第5步,如果不是,到下一步;
7,用数组R记录Tcurrent中的所有区域点索引值,清空Tcurrent,更新current=current+1;
8,根据R更新P,L和T0~T7,round=round+1,返回第6步。
1 package com.example.lenovo.linehough; 2 3 import android.graphics.Color; 4 5 public class Framework { 6 int width; 7 int height; 8 int Fpn; 9 int[] pixels; 10 int[] treeValue; 11 GRBTree[] grbTree; 12 int currentTree; 13 TList[] L; 14 int[] InfluencedPnt; 15 int lutTreeNum[] = { 16 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 17 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 1, 0, 0, 1, 2, 18 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 19 0, 0, 1, 1, 0, 0, 1, 2, 0, 0, 0, 0, 0, 0, 0, 1, 20 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 21 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 2, 0, 0, 1, 1, 22 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 23 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 24 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 25 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 26 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 27 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 28 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 29 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 0, 1, 0, 0, 0, 0, 30 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 31 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 33 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 34 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 35 0, 0, 1, 1, 1, 0, 2, 1, 0, 0, 0, 0, 0, 0, 0, 0, 36 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 37 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 38 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 39 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 40 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 41 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 42 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 43 1, 0, 0, 0, 2, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 44 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 45 1, 0, 0, 0, 0, 0, 0, 0, 2, 1, 0, 0, 0, 0, 0, 0, 46 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 47 2, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0 48 }; 49 50 int lutTreeIdx1[] = { 51 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 52 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 3, 3, 0, 0, 3, 2, 53 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 54 0, 0, 5, 5, 0, 0, 5, 2, 0, 0, 0, 0, 0, 0, 0, 2, 55 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 56 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 3, 3, 57 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 58 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 59 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 60 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 61 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 62 7, 0, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 63 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 64 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 65 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 66 7, 0, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 67 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 68 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 69 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 70 0, 0, 5, 5, 6, 0, 5, 5, 0, 0, 0, 0, 0, 0, 0, 0, 71 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 72 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 73 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 74 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 75 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 76 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 77 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 78 7, 0, 0, 0, 6, 0, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 79 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 80 4, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 81 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 82 4, 0, 0, 0, 7, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0 83 }; 84 85 int lutTreeIdx2[] = { 86 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 87 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 88 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 89 0, 0, 0, 0, 0, 0, 0, 5, 0, 0, 0, 0, 0, 0, 0, 0, 90 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 91 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 92 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 93 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 94 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 95 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 96 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 97 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 98 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 99 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 100 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 101 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 102 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 103 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 104 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 105 0, 0, 0, 0, 0, 0, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 106 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 107 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 108 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 109 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 110 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 111 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 112 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 113 0, 0, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 114 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 115 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 116 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 117 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 118 }; 119 120 public Framework() { 121 122 } 123 124 //TList建立一个矩阵,矩阵元素包含TreeIndex,Visited两个字段; 125 // TreeIndex记录待移除点对应的红黑树序号(0~7),TreeIndex为-1时,该点为非移除点;Visited标记点是否被访问过了 126 public class TList { 127 int TreeIndex; 128 int Visited; 129 130 public TList() { 131 this.TreeIndex = -1; 132 this.Visited = 0; 133 } 134 } 135 136 //提取骨架 137 public int[] getFramework(int w, int h, int[] inputs) { 138 width = w; 139 height = h; 140 pixels = new int[width * height]; 141 //黑色为1,白色为0 142 for (int y = 0; y < height; y++) { 143 for (int x = 0; x < width; x++) { 144 if (inputs[y * width + x] == Color.BLACK) 145 pixels[y * width + x] = 1; 146 else pixels[y * width + x] = 0; 147 } 148 } 149 //新建8个红黑树对象 150 grbTree = new GRBTree[8]; 151 for (int i = 0; i <= 7; i++) { 152 grbTree[i] = new GRBTree(); 153 } 154 currentTree = 0; 155 156 L = new TList[width * height]; 157 for (int i = 0; i < width * height; i++) { 158 L[i] = new TList(); 159 } 160 //第一次遍历二值化图像 161 FirstMatchTree(); 162 163 //当所有的树里面不再插入结点的时候,说明骨架已经生成,即n0~n7都为0 164 while (grbTree[0].root != null || grbTree[1].root != null || grbTree[2].root != null || grbTree[3].root != null || 165 grbTree[4].root != null || grbTree[5].root != null || grbTree[6].root != null || grbTree[7].root != null) { 166 for (int round = 0; round <= 7; round++) { 167 treeValue = new int[width * height]; 168 //前序遍历树currentTree的移除点索引值,将它们先存放在数组treeValue里面 169 treeValue = grbTree[currentTree].preOrder(width, height); 170 //存放在树currentTree里面的移除点总数 171 int valueCount = grbTree[currentTree].getiCount(); 172 173 //再清空树currentTree里面的数据,root置为null 174 grbTree[currentTree].Free(); 175 176 currentTree++; 177 if (currentTree == 8) currentTree = 0; 178 179 //更新pixels,L 180 for (int i1 = 0; i1 < valueCount; i1++) { 181 int pntIndex = treeValue[i1]; 182 pixels[pntIndex] = 0; 183 L[pntIndex].TreeIndex = -1;//TreeIndex为-1时,该点为非移除点 184 L[pntIndex].Visited = -1;//Visited为-1时,表明该点已成为非待移除点 185 } 186 //遍历数组treeValue,InfluencedPnt记录受影响的领域点坐标,Fpn为受影响点的个数 187 InfluencedPnt = new int[width * height]; 188 Fpn = 0; 189 for (int i1 = 0; i1 < valueCount; i1++) { 190 int pntIndex = treeValue[i1]; 191 //查找移除点的8领域点 192 for (int i2 = -1; i2 <= 1; i2++) { 193 for (int i3 = -1; i3 <= 1; i3++) { 194 if (i2 == 0 && i3 == 0) continue; 195 //8个领域点的下标索引值index 196 int index = pntIndex + i2 * width + i3; 197 if (index < 0) index = 0; 198 if (index > width * height - 1) 199 index = width * height - 1; 200 //该领域点为白点直接退出 201 if (pixels[index] == 0) continue; 202 //Visite为1时,表明该领域点已被访问过,直接退出; 203 if (L[index].Visited == 1) continue; 204 else { 205 L[index].Visited = 1; 206 InfluencedPnt[Fpn] = index; 207 Fpn++; 208 } 209 //更新树T0到T7,只要检测到8领域点是待移除点的话,首先将它从原来的树里面删除;不是的话,不操作; 210 // 之后再根据该领域点的3*3结构(可能发生变化)来判断它是否是待移除点,以及它该插入到哪棵红黑树里面 211 int treeindex = L[index].TreeIndex; 212 if (treeindex >= 0) 213 grbTree[treeindex].Delete(index); 214 //获取领域点index的3*3结构 215 int[] b = new int[9]; 216 for (int i4 = -1; i4 <= 1; i4++) { 217 for (int i5 = -1; i5 <= 1; i5++) { 218 int index8 = index + i5 * width + i4; 219 if (index8 < 0) index = 0; 220 if (index8 > width * height - 1) 221 index8 = width * height - 1; 222 b[(i4 + 1) * 3 + (i5 + 1)] = pixels[index8]; 223 } 224 } 225 //计算领域点index对应的lutTreeNum的下标值lutValue 226 int lutValue = 0; 227 for (int i = 0; i <= 8; i++) { 228 if (b[i] == 1) 229 lutValue += Math.pow(2, i); 230 } 231 //将待移除的领域点index插入对应的红黑树k里面,并将k值记录到L中 232 int k = Put(index, lutValue); 233 if (k >= 0) L[index].TreeIndex = k; 234 else L[index].TreeIndex = -1; 235 } 236 } 237 } 238 239 for (int i = 0; i < Fpn; i++) { 240 int f = InfluencedPnt[i]; 241 L[f].Visited = 0; 242 } 243 244 } 245 } 246 //while循环结束,再也没有移除点 247 for (int y = 0; y < height; y++) { 248 for (int x = 0; x < width; x++) { 249 if (pixels[y * width + x] == 1) inputs[y * width + x] = Color.BLACK; 250 else inputs[y * width + x] = Color.WHITE; 251 } 252 } 253 return inputs; 254 } 255 256 private void FirstMatchTree() { 257 //第一次遍历二值化图像,currentTree=0,把待移除点插入对应的红黑树里面 258 for (int y = 1; y < height - 1; y++) { 259 for (int x = 1; x < width - 1; x++) { 260 int pntIndex = y * width + x; 261 //是白色点的话直接退出本次循环,检测下一个点 262 if (pixels[pntIndex] == 0) continue; 263 //检测像素点的3*3领域结构,组成a[8]a[7]...a[1]a[0]的九位二进制数 264 int[] a = new int[9]; 265 for (int i = -1; i <= 1; i++) { 266 for (int j = -1; j <= 1; j++) { 267 a[(i + 1) * 3 + (j + 1)] = pixels[(y + j) * width + (x + i)]; 268 } 269 } 270 //通过上面的九位二进制数,计算出该像素点对应于lutTreeNum的下标值 271 int lutValue = 0; 272 for (int i = 0; i <= 8; i++) { 273 if (a[i] == 1) 274 lutValue += Math.pow(2, i); 275 } 276 //k为树的序号数,pntIndex为待移除点的话,k的范围为0到7;不是待移除点的话,k为-1 277 int k = Put(pntIndex, lutValue); 278 if (k >= 0) L[pntIndex].TreeIndex = k; 279 else L[pntIndex].TreeIndex = -1; 280 } 281 } 282 } 283 284 private int GetTreeIndex(int lutValue) { 285 if (lutTreeNum[lutValue] == 0) 286 return -1; 287 288 if (lutTreeNum[lutValue] == 1) 289 return lutTreeIdx1[lutValue]; 290 291 if (currentTree > lutTreeIdx1[lutValue] && currentTree <= lutTreeIdx2[lutValue]) 292 return lutTreeIdx2[lutValue]; 293 294 return lutTreeIdx1[lutValue]; 295 } 296 297 private int Put(int pntIndex, int lutValue) { 298 int ti = GetTreeIndex(lutValue); 299 300 if (ti < 0) 301 return -1; 302 303 grbTree[ti].Insert(pntIndex); 304 305 return ti; 306 } 307 308 private void Delete(int pntIndex, int lutValue) { 309 int ti = GetTreeIndex(lutValue); 310 311 if (ti < 0 || ti == currentTree) 312 return; 313 314 grbTree[ti].Delete(pntIndex); 315 } 316 317 318 }
时间: 2024-09-30 07:24:59