|
@@ -1,3 +1,18 @@
|
|
|
+#Copyright (C) [2023] [ZHANG Jing, Université du Littoral Côte d'Opale]
|
|
|
+#
|
|
|
+#This program is free software: you can redistribute it and/or modify
|
|
|
+#it under the terms of the GNU General Public License as published by
|
|
|
+#the Free Software Foundation, either version 3 of the License, or
|
|
|
+#(at your option) any later version.
|
|
|
+#
|
|
|
+#This program is distributed in the hope that it will be useful,
|
|
|
+#but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
+#MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
+#GNU General Public License for more details.
|
|
|
+#
|
|
|
+#You should have received a copy of the GNU General Public License
|
|
|
+#along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
|
+
|
|
|
#!/usr/bin/env python
|
|
|
# coding: utf-8
|
|
|
|
|
@@ -15,7 +30,14 @@ from skimage.transform import resize
|
|
|
|
|
|
from PIL import Image, ImageDraw, ImageColor
|
|
|
|
|
|
-
|
|
|
+## compute image gradient map, take the absolute difference values in 4 dimensions for each pixel
|
|
|
+
|
|
|
+# --------------------------
|
|
|
+# | |
|
|
|
+# | i, j i, j+1 |
|
|
|
+# | i+1,j-1 i+1,j i+1,j+1 |
|
|
|
+# --------------------------
|
|
|
+
|
|
|
def gradient4D(img):
|
|
|
(row, col) = img.shape
|
|
|
g4d = np.zeros((row, col))
|
|
@@ -26,18 +48,22 @@ def gradient4D(img):
|
|
|
|
|
|
return npNormalise(g4d)
|
|
|
|
|
|
-
|
|
|
+# normalise values to [0, 1]
|
|
|
def npNormalise(xArray):
|
|
|
XNorm = (xArray - xArray.min()) / (xArray.max() - xArray.min())
|
|
|
return XNorm
|
|
|
|
|
|
+# compute all potential lines for a square of size ws*ws
|
|
|
+# input : length of the square
|
|
|
+# output : 1. a set of binary images, each image contains only one line
|
|
|
+# 2. a set containing the coordinates of the start points and the end points of the line
|
|
|
def getBaseLines(ws):
|
|
|
baseLineList = []
|
|
|
baseLineListIdex = []
|
|
|
+ # Skip 5 pixels to avoid lines near edges
|
|
|
for i in range(0,ws-5):
|
|
|
- #if i == 1: break
|
|
|
for j in range(5,ws): # cut bord
|
|
|
- #if j == 6: break
|
|
|
+
|
|
|
# 1
|
|
|
# -------------
|
|
|
# | |
|
|
@@ -46,33 +72,39 @@ def getBaseLines(ws):
|
|
|
# | |
|
|
|
# --------------
|
|
|
# 3
|
|
|
- # adjacent edge
|
|
|
+ # adjacent edge (like edge 1 and edge 2, edge 1 and edge 4)
|
|
|
img12 = Image.new('F', (ws,ws),0)
|
|
|
draw12 = ImageDraw.Draw(img12)
|
|
|
+ # lines that the start point in edge 1 and the end point in edge 2
|
|
|
draw12.line(xy=(i, 0, ws-1, j),
|
|
|
fill=(1), width = 1)
|
|
|
baseLineList.append(np.asarray(img12))
|
|
|
baseLineListIdex.append(np.asarray([[i, 0],[ws-1, j]]))
|
|
|
+ # lines that the start point in edge 4 and the end point in edge 1
|
|
|
baseLineList.append(np.rot90(np.asarray(img12), 1, axes=(0, 1)))
|
|
|
baseLineListIdex.append(np.asarray([[j, 0],[0, ws-1-i]]))
|
|
|
+ # lines that the start point in edge 3 and the end point in edge 4
|
|
|
baseLineList.append(np.rot90(np.asarray(img12), 2, axes=(0, 1)))
|
|
|
baseLineListIdex.append(np.asarray([[0, ws-1-j],[ws-1-i, ws-1]]))
|
|
|
+ # lines that the start point in edge 2 and the end point in edge 3
|
|
|
baseLineList.append(np.rot90(np.asarray(img12), 3, axes=(0, 1)))
|
|
|
baseLineListIdex.append(np.asarray([[ws-1, i],[ws-1-j, ws-1]]))
|
|
|
|
|
|
# opposite side
|
|
|
img13 = Image.new('F', (ws,ws),0)
|
|
|
draw13 = ImageDraw.Draw(img13)
|
|
|
+ # lines that the start point in edge 4 and the end point in edge 2
|
|
|
draw13.line(xy=(i, 0, j, ws-1),
|
|
|
fill=(1), width = 1)
|
|
|
baseLineList.append(np.asarray(img13))
|
|
|
baseLineListIdex.append(np.asarray([[i,0],[j, ws-1]]))
|
|
|
+ # lines that the start point in edge 1 and the end point in edge 3
|
|
|
baseLineList.append(np.asarray(img13).T)
|
|
|
baseLineListIdex.append(np.asarray([[0,i],[ws-1, j]]))
|
|
|
print('base line number :', len(baseLineList))
|
|
|
return np.asarray(baseLineList), np.asarray(baseLineListIdex)
|
|
|
|
|
|
-
|
|
|
+# Calculate the slope of the line formed by vertex1 and vertex2
|
|
|
def calculSlope(v1,v2):
|
|
|
difX = v2[0] - v1[0]
|
|
|
difY = v2[1] - v1[1]
|
|
@@ -82,14 +114,17 @@ def calculSlope(v1,v2):
|
|
|
lk = difY / difX
|
|
|
return lk
|
|
|
|
|
|
+# Compute the band mask of a line
|
|
|
def clusterRegion(centerLine, scale = 4, windowSize=64):
|
|
|
H = windowSize
|
|
|
W = windowSize
|
|
|
sMask = np.zeros([H,W])
|
|
|
ix = int(centerLine[0][0])
|
|
|
iy = int(centerLine[0][1])
|
|
|
+ # calculate the width of band mask
|
|
|
pixelRange = int(min(H,W) / scale) # scale = 10
|
|
|
|
|
|
+ # get the slope of line
|
|
|
k = calculSlope(centerLine[0],centerLine[1])
|
|
|
|
|
|
if abs(k) > 1:
|
|
@@ -125,32 +160,41 @@ def clusterRegion(centerLine, scale = 4, windowSize=64):
|
|
|
return sMask
|
|
|
|
|
|
|
|
|
-
|
|
|
+# fonction for display all the lines
|
|
|
def drawGroupLine(file, lineList, flineListCluster, scale, functionName, colorSTR, outputPath):
|
|
|
c = ImageColor.colormap
|
|
|
cList = list(c.items())
|
|
|
(inputPath,inputFile) = os.path.split(file)
|
|
|
print(inputPath)
|
|
|
print(inputFile)
|
|
|
+
|
|
|
+ # read the orignal file for draw
|
|
|
with Image.open(file) as img4draw:
|
|
|
w, h = img4draw.size
|
|
|
+ if w >h: # add lineWidth to adapt the visibility of drawing results to different image sizes
|
|
|
+ lineWidth = int(h/40)
|
|
|
+ else:
|
|
|
+ lineWidth = int(w/40)
|
|
|
+
|
|
|
scale = 1/64
|
|
|
wScale = np.ceil(w*scale)
|
|
|
hScale = np.ceil(h*scale)
|
|
|
|
|
|
img1 = ImageDraw.Draw(img4draw)
|
|
|
-
|
|
|
+
|
|
|
+ # draw the cluster result
|
|
|
# for n,lineSet in enumerate(flineListCluster):
|
|
|
# for [v1,v2],w,_ in lineSet[1:]:
|
|
|
# img1.line([(v1[0]*wScale,v1[1]*hScale), (v2[0]*wScale,v2[1]*hScale)], fill = cList[int(n*2)+2][1], width = 4)
|
|
|
-
|
|
|
+ # draw all the centers
|
|
|
for [v1,v2] in lineList:
|
|
|
- img1.line([(v1[0],v1[1]), (v2[0],v2[1])], fill = colorSTR, width = 8)
|
|
|
+ img1.line([(v1[0],v1[1]), (v2[0],v2[1])], fill = colorSTR, width = lineWidth)
|
|
|
|
|
|
img4draw.save(os.path.join(outputPath, inputFile[:-4] + '_' + str(functionName) + inputFile[-4:] ))
|
|
|
|
|
|
-
|
|
|
+# sort the slope of lines, inutile for version 0
|
|
|
def sortSlope(lineListArray):
|
|
|
+ print('lineListArray', lineListArray)
|
|
|
slopeList = []
|
|
|
groupWeight = 0
|
|
|
for l in lineListArray:
|
|
@@ -161,23 +205,26 @@ def sortSlope(lineListArray):
|
|
|
slopeList.append(k)
|
|
|
groupWeight = groupWeight + l[1]
|
|
|
# print('weight = ', l[1])
|
|
|
+ print('slopeList : ', slopeList)
|
|
|
index = np.argsort(np.array(slopeList))
|
|
|
+ print('sortSlope index : ', index)
|
|
|
+ print('sortSlope index median : ', int(np.median(index)))
|
|
|
#groupWeight = np.mean(groupWeight)
|
|
|
|
|
|
-
|
|
|
return [lineListArray[int(np.median(index))][0], lineListArray[0][1], lineListArray[int(np.median(index))][2]]
|
|
|
+ # return [lineListArray[int(len(index)/2)][0], lineListArray[0][1], lineListArray[int(len(index)/2)][2]]
|
|
|
# index[len(index)//2]
|
|
|
|
|
|
+# extraction the center of each group
|
|
|
def forceLinesClusterIntegration(cluster):
|
|
|
forceL = []
|
|
|
|
|
|
for i,lineSet in enumerate(cluster):
|
|
|
-
|
|
|
- forceL.append(sortSlope(lineSet[1:]))
|
|
|
+ forceL.append(lineSet[1])
|
|
|
return forceL
|
|
|
|
|
|
|
|
|
-
|
|
|
+# refine the cluster result
|
|
|
def refine(lineList, fg, wg, iP, ws):
|
|
|
wlist = []
|
|
|
forceList = []
|
|
@@ -198,18 +245,21 @@ def refine(lineList, fg, wg, iP, ws):
|
|
|
flList = forceLinesClusterIntegration(forceList)
|
|
|
return flList
|
|
|
|
|
|
-
|
|
|
+# the main process of clustering the lines
|
|
|
def findSaliantLineCluster(gradient4d,allLines,allLinesIndex,ws, orgW, orgH ):
|
|
|
weightList = []
|
|
|
- fineGrained0 = 8
|
|
|
- intePrec0 = 0.8
|
|
|
+ fineGrained0 = 8 # initial refine grained = ws / 8
|
|
|
+ intePrec0 = 0.8 # initial intersection precision
|
|
|
forceLinesCluster = []
|
|
|
+
|
|
|
+ # compute weights of lines
|
|
|
for l in allLines:
|
|
|
w = np.sum(gradient4d*l)
|
|
|
weightList.append(w)
|
|
|
|
|
|
npWeightList = np.array(weightList)
|
|
|
- sortWeightList = npWeightList.argsort()[::-1] # [::-1] inverse a list
|
|
|
+ sortWeightList = npWeightList.argsort()[::-1] # [::-1] inverse a list, range from large to small
|
|
|
+ # top 300 weighted candidates, about 0.14% of the total lines
|
|
|
for n,wId in enumerate(sortWeightList[:300]):
|
|
|
if n == 0:
|
|
|
groupMask = clusterRegion(allLinesIndex[wId], fineGrained0, ws)
|