Spaces:
Build error
Build error
Download util/lua/utils.lua~ from rawanessam/FloorPlanTransformation: direct link, hf CLI and curl.
- Browser
- Download file 26.5 kB
-
https://huggingface.co/spaces/rawanessam/FloorPlanTransformation/resolve/main/util/lua/utils.lua~
- Command line
-
hf download 'hf://spaces/rawanessam/FloorPlanTransformation/util/lua/utils.lua~'
-
curl -L -o 'utils.lua~' 'https://huggingface.co/spaces/rawanessam/FloorPlanTransformation/resolve/main/util/lua/utils.lua~'
26.5 kB
| require 'torch' | |
| require 'lfs' | |
| cv = require 'cv' | |
| require 'cv.imgproc' | |
| local utils = {} | |
| function utils.to4DTensor(a) | |
| return utils.toXDTensor(a, 4) | |
| end | |
| function utils.toXDTensor(a, x) | |
| local sz = torch.LongStorage(x):fill(1) | |
| for i = 1, a:dim() do | |
| sz[i] = a:size(i) | |
| end | |
| return a:reshape(sz) | |
| end | |
| function utils.toNNSingleTensor(a) | |
| local opt = opt or {} | |
| opt.type = opt.type or 'CUDA' | |
| if opt.type == 'CPU' then | |
| return a:double() | |
| elseif opt.type == 'CUDA' then | |
| return a:cuda() | |
| else | |
| return a | |
| end | |
| end | |
| function utils.toNNTensor(a) | |
| if type(a) == 'userdata' then | |
| return utils.toNNSingleTensor(a) | |
| elseif type(a) == 'table' then | |
| local b = {} | |
| for key, value in pairs(a) do | |
| b[key] = utils.toNNTensor(value) | |
| end | |
| return b | |
| else | |
| return a | |
| end | |
| end | |
| function utils.getLength(a) | |
| if type(a) == 'userdata' then | |
| return a:size(1) | |
| elseif type(a) == 'table' then | |
| return #a | |
| end | |
| end | |
| function utils.getFirstDim(a) | |
| if type(a) == 'userdata' then | |
| return a:size(1) | |
| elseif type(a) == 'table' then | |
| return utils.getSize(a[1]) | |
| end | |
| end | |
| -- to be replaced | |
| function utils.getSize(a) | |
| return utils.getFirstDim(a) | |
| end | |
| function utils.getIndices(a, indices) | |
| if type(a) == 'userdata' then | |
| return a:index(1, indices) | |
| elseif type(a) == 'table' then | |
| local b = {} | |
| for key, value in pairs(a) do | |
| b[key] = utils.getIndices(value, indices) | |
| end | |
| return b | |
| end | |
| end | |
| function utils.loadData(fileName) | |
| if lfs.attributes(fileName, 'mode') == 'file' then | |
| return torch.load(fileName) | |
| else | |
| return nil | |
| end | |
| end | |
| -- all inputs are ByteTensors, 0 is false and 1 is true. | |
| function utils.isinf(value) | |
| return value == math.huge or value == -math.huge | |
| end | |
| function utils.isnan(value) | |
| return value ~= value | |
| end | |
| function utils.isfinite(value) | |
| return not utils.isinf(value) and not utils.isnan(value) | |
| end | |
| function utils.bitand(t1, t2) | |
| return torch.ge(t1 + t2, 2) | |
| end | |
| function utils.bitor(t1, t2) | |
| return torch.ge(t1 + t2, 1) | |
| end | |
| function utils.bitnot(t) | |
| return torch.lt(t, 1) | |
| end | |
| function utils.len(t) | |
| local n | |
| if type(t) == 'table' then | |
| n = #t | |
| elseif t:nDimension() == 1 then | |
| n = t:size(1) | |
| else | |
| error(string.format("t is a high-order tensor! t:nDimension() = %d", t:nDimension())) | |
| end | |
| return n | |
| end | |
| function utils.fromHead(t, k) | |
| local n = utils.len(t) | |
| local res = {} | |
| for i = 1, math.min(k, n) do | |
| table.insert(res, t[i]) | |
| end | |
| return res | |
| end | |
| function utils.fromTail(t, k) | |
| local n = utils.len(t) | |
| local res = {} | |
| for i = n, math.max(n - k, 0) + 1, -1 do | |
| table.insert(res, t[i]) | |
| end | |
| return res | |
| end | |
| -- Find nonzero and return a table. | |
| function utils.nonzero(t) | |
| local indices = {} | |
| local n = utils.len(t) | |
| for i = 1, n do | |
| if t[i] == 1 then | |
| table.insert(indices, i) | |
| end | |
| end | |
| return indices | |
| end | |
| -- Select rows and return | |
| function utils.selectRows(t, tb) | |
| assert(tb:size(1) == t:size(1), 'The first dimension of t and tb must be the same') | |
| local dims = t:size():totable() | |
| table.remove(dims, 1) | |
| local res = t.new():resize(tb:sum(), unpack(dims)) | |
| local counter = 0 | |
| for i = 1, t:size(1) do | |
| if tb[i] == 1 then | |
| counter = counter + 1 | |
| res[counter]:copy(t[i]) | |
| end | |
| end | |
| return res | |
| end | |
| function utils.permCompose(indices1, indices2) | |
| -- body | |
| local n1 = utils.len(indices1) | |
| local n2 = utils.len(indices2) | |
| local indices = {} | |
| for i = 1, n2 do | |
| indices[i] = indices1[indices2[i]] | |
| end | |
| return indices | |
| end | |
| function utils.removeKeys(t, exclude) | |
| local res = {} | |
| for k, v in pairs(t) do | |
| if not exclude[k] then res[k] = v end | |
| end | |
| return res | |
| end | |
| --[[ | |
| Find the correlation between two matrices. | |
| t1 : m1 by n | |
| t2 : m2 by n | |
| return matrix of size m1 by m2 | |
| --]] | |
| function utils.innerprod(t1, t2, func, reduction) | |
| local n = t1:size(2) | |
| assert(n == t2:size(2), string.format('The column of t1 [%d] must be the same as the column of t2 [%d]', t1:size(2), t2:size(2))) | |
| local m1 = t1:size(1) | |
| local m2 = t2:size(1) | |
| local res = torch.Tensor(m1, m2) | |
| for i = 1, m1 do | |
| for j = 1, m2 do | |
| res[i][j] = func(t1[i], t2[j]) | |
| end | |
| end | |
| -- Find the one with smallest distance | |
| local best, bestIndices | |
| if reduction then | |
| best, bestIndices = reduction(res, 2) | |
| end | |
| return res, best, bestIndices | |
| end | |
| ----------------------------------------------- | |
| -- Landmarks related. | |
| function utils.inRect(rect, p, margin) | |
| margin = margin or 0; | |
| return rect[1] + margin <= p[1] and p[1] <= rect[3] - margin and | |
| rect[2] + margin <= p[2] and p[2] <= rect[4] - margin; | |
| end | |
| function utils.fillKernel(m, x, y, r, c) | |
| assert(m, "Input image should not be null") | |
| assert(x and y, "Input coordinates should not be null") | |
| assert(r and c, "Input radius and color should not be null") | |
| assert(#m:size() == 2, 'fill_kernel: input m is not 2D!') | |
| -- fill a circle (x, y, r) with number c. | |
| local w = m:size(2) | |
| local h = m:size(1) | |
| local minX = math.min(math.max(x - r, 1), w) | |
| local maxX = math.min(math.max(x + r, 1), w) | |
| -- if minX > maxX then minX, maxX = maxX, minX end | |
| local minY = math.min(math.max(y - r, 1), h) | |
| local maxY = math.min(math.max(y + r, 1), h) | |
| -- if minY > maxY then minY, maxY = maxY, minY end | |
| -- local img_rect = {1, 1, m:size(2), m:size(1)} | |
| -- assert(util.rect_isin(img_rect, {minX, minY}) == true, string.format("out of bound, minX = %f, minY = %f", minX, minY)) | |
| -- assert(util.rect_isin(img_rect, {maxX, maxY}) == true, string.format("out of bound, maxX = %f, maxY = %f", maxX, maxY)) | |
| m:sub(minY, maxY, minX, maxX):fill(c); | |
| end | |
| -- Input a 2D mask, find its minimal value and associated location. | |
| function utils.imin(m) | |
| local min1, minI1 = torch.min(m, 1) | |
| local minVal, minI2 = torch.min(min1, 2) | |
| local x = minI2[1][1] | |
| local y = minI1[1][x] | |
| return x, y, minVal | |
| end | |
| -- Input a 2D mask, find its maximal value and associated location. | |
| function utils.imax(m) | |
| local max1, maxI1 = torch.max(m, 1) | |
| local maxVal, maxI2 = torch.max(max1, 2) | |
| local x = maxI2[1][1] | |
| local y = maxI1[1][x] | |
| return x, y, maxVal | |
| end | |
| -------------------------------------Save to json-------------------- | |
| function utils.set2Array(t) | |
| if type(t) ~= 'table' then return end | |
| t.__array = true | |
| for i, v in ipairs(t) do | |
| utils.set2Array(v) | |
| end | |
| end | |
| function utils.convert2Table(t) | |
| local res = {} | |
| if type(t) == 'table' then | |
| if debug then print("parsing table") end | |
| for k, v in pairs(t) do | |
| res[k] = utils.convert2Table(v) | |
| end | |
| elseif type(t) == 'number' then | |
| if debug then print("parsing number") end | |
| res = t | |
| elseif torch.typename(t) and torch.typename(t):match('Tensor') then | |
| -- if t is a tensor | |
| if debug then print("parsing tensor") end | |
| res = t:totable() | |
| utils.set2Array(t) | |
| -- Layer | |
| else | |
| local typename = type(t) | |
| typename = typename or torch.typename(t) | |
| error("Convert_to_table error, unsupported datatype = " .. typename) | |
| end | |
| return res | |
| end | |
| function utils.saveJson(t, f) | |
| -- save a table to json | |
| if type(t) == 'number' then | |
| f:write(tostring(t)) | |
| return | |
| end | |
| if t.__array then | |
| -- array must contain all numbers. | |
| f:write("[\n") | |
| for i, v in ipairs(t) do | |
| utils.saveJson(v, f) | |
| if i ~= #t then f:write(",") end | |
| end | |
| f:write("]\n") | |
| else | |
| local counter = 0 | |
| for k, v in pairs(t) do counter = counter + 1 end | |
| f:write("{\n") | |
| for k, v in pairs(t) do | |
| f:write(tostring(k) .. " : ") | |
| utils.saveJson(v, f) | |
| counter = counter - 1 | |
| if counter >= 1 then f:write(",\n") end | |
| end | |
| f:write("}\n") | |
| end | |
| end | |
| -------------------------------------Save to numpy----------------- | |
| -- run it using PATH=/usr/bin/python | |
| function utils.savePickle(f, t) | |
| local py = require 'fb.python' | |
| py.exec([=[ | |
| import numpy as np | |
| import cPickle | |
| with open(filename, "wb") as outfile: | |
| cPickle.dump(variable, outfile, protocol=cPickle.HIGHEST_PROTOCOL) | |
| ]=], {variable = t, filename = f}) | |
| end | |
| -- | |
| local function getTermLength() | |
| if sys.uname() == 'windows' then return 80 end | |
| local tputf = io.popen('tput cols', 'r') | |
| local w = tonumber(tputf:read('*a')) | |
| local rc = {tputf:close()} | |
| if rc[3] == 0 then return w | |
| else return 80 end | |
| end | |
| local barDone = true | |
| local previous = -1 | |
| local tm = '' | |
| local timer | |
| local times | |
| local indices | |
| local termLength = math.min(getTermLength(), 120) | |
| local function formatTime(seconds) | |
| -- decompose: | |
| local floor = math.floor | |
| local days = floor(seconds / 3600/24) | |
| seconds = seconds - days*3600*24 | |
| local hours = floor(seconds / 3600) | |
| seconds = seconds - hours*3600 | |
| local minutes = floor(seconds / 60) | |
| seconds = seconds - minutes*60 | |
| local secondsf = floor(seconds) | |
| seconds = seconds - secondsf | |
| local millis = floor(seconds*1000) | |
| -- string | |
| local f = '' | |
| local i = 1 | |
| if days > 0 then f = f .. days .. 'D' i=i+1 end | |
| if hours > 0 and i <= 2 then f = f .. hours .. 'h' i=i+1 end | |
| if minutes > 0 and i <= 2 then f = f .. minutes .. 'm' i=i+1 end | |
| if secondsf > 0 and i <= 2 then f = f .. secondsf .. 's' i=i+1 end | |
| if millis > 0 and i <= 2 then f = f .. millis .. 'ms' i=i+1 end | |
| if f == '' then f = '0ms' end | |
| -- return formatted time | |
| return f | |
| end | |
| function utils.progress(current, goal, addinfo) | |
| -- defaults: | |
| local barLength = termLength - 37 - #addinfo | |
| local smoothing = 100 | |
| local maxfps = 10 | |
| -- Compute percentage | |
| local percent = math.floor(((current) * barLength) / goal) | |
| -- start new bar | |
| if (barDone and ((previous == -1) or (percent < previous))) then | |
| barDone = false | |
| previous = -1 | |
| tm = '' | |
| timer = torch.Timer() | |
| times = {timer:time().real} | |
| indices = {current} | |
| else | |
| io.write('\r') | |
| end | |
| --if (percent ~= previous and not barDone) then | |
| if (not barDone) then | |
| previous = percent | |
| -- print bar | |
| io.write(' [') | |
| for i=1,barLength do | |
| if (i < percent) then io.write('=') | |
| elseif (i == percent) then io.write('>') | |
| else io.write('.') end | |
| end | |
| io.write('] ') | |
| for i=1,termLength-barLength-4 do io.write(' ') end | |
| for i=1,termLength-barLength-4 do io.write('\b') end | |
| -- time stats | |
| local elapsed = timer:time().real | |
| local step = (elapsed-times[1]) / (current-indices[1]) | |
| if current==indices[1] then step = 0 end | |
| local remaining = math.max(0,(goal - current)*step) | |
| table.insert(indices, current) | |
| table.insert(times, elapsed) | |
| if #indices > smoothing then | |
| indices = table.splice(indices) | |
| times = table.splice(times) | |
| end | |
| -- Print remaining time when running or total time when done. | |
| if (percent < barLength) then | |
| tm = ' ETA: ' .. formatTime(remaining) | |
| else | |
| tm = ' Tot: ' .. formatTime(elapsed) | |
| end | |
| tm = tm .. ' | Step: ' .. formatTime(step) .. ' | ' .. addinfo | |
| io.write(tm) | |
| -- go back to center of bar, and print progress | |
| for i=1,5+#tm+barLength/2 do io.write('\b') end | |
| io.write(' ', current, '/', goal, ' ') | |
| -- reset for next bar | |
| if (percent == barLength) then | |
| barDone = true | |
| io.write('\n') | |
| end | |
| -- flush | |
| io.write('\r') | |
| io.flush() | |
| end | |
| end | |
| function utils.drawHeatMapImage(image, heatMap, alpha, beta) | |
| local heatMapImage = image * alpha | |
| local scale = math.ceil(math.max((#image)[2] / (#heatMap)[1], (#image)[3] / (#heatMap)[2])) | |
| resizedHeatMap = require('nn').SpatialUpSamplingBilinear(scale):forward(heatMap:repeatTensor(1, 1, 1))[1] | |
| local paddingX = (#image)[2] - (#resizedHeatMap)[1] | |
| local paddingLeft = math.floor(paddingX / 2) | |
| local paddingRight = paddingX - paddingLeft | |
| local paddingY = (#image)[3] - (#resizedHeatMap)[2] | |
| local paddingTop = math.floor(paddingY / 2) | |
| local paddingBottom = paddingY - paddingTop | |
| resizedHeatMap = require('nn').SpatialZeroPadding(paddingLeft, paddingRight, paddingTop, paddingBottom):forward(resizedHeatMap:repeatTensor(1, 1, 1))[1] | |
| --print(scale) | |
| --print(padding) | |
| --print(#resizedHeatMap) | |
| --os.exit(1) | |
| local heatMapHSL = torch.DoubleTensor(3, (#resizedHeatMap)[1], (#resizedHeatMap)[2]) | |
| heatMapHSL[1] = 2 / 3 * (1 - resizedHeatMap) | |
| heatMapHSL[2] = 1 | |
| heatMapHSL[3] = 0.5 | |
| local heatMapRGB = require('image').hsl2rgb(heatMapHSL) | |
| heatMapImage = heatMapImage + heatMapRGB * beta | |
| heatMapImage:clamp(0, 1) | |
| return heatMapImage | |
| end | |
| function utils.segmentFloorplan(floorplan, binaryThreshold, numOpenOperations, reverse) | |
| local floorplanBinary = torch.ones((#floorplan)[2], (#floorplan)[3]) | |
| for c = 1, 3 do | |
| local mask = floorplan[c]:lt(binaryThreshold):double() | |
| floorplanBinary = torch.cmul(floorplanBinary, mask) | |
| end | |
| local kernel = torch.ones(3, 3) | |
| if numOpenOperations > 0 then | |
| for i = 1, numOpenOperations do | |
| floorplanBinary = image.erode(floorplanBinary) | |
| floorplanBinary = image.dilate(floorplanBinary) | |
| end | |
| elseif numOpenOperations < 0 then | |
| for i = 1, -numOpenOperations do | |
| floorplanBinary = image.dilate(floorplanBinary) | |
| floorplanBinary = image.erode(floorplanBinary) | |
| end | |
| end | |
| floorplanBinaryByte = (floorplanBinary * 255):byte() | |
| if reverse ~= nil and reverse then | |
| floorplanBinaryByte = 255 - floorplanBinaryByte | |
| end | |
| local floorplanComponent = torch.IntTensor(floorplanBinaryByte:size()) | |
| local numComponents = cv.connectedComponents{255 - floorplanBinaryByte, floorplanComponent} | |
| floorplanComponent = floorplanComponent + 1 | |
| return floorplanComponent, numComponents, floorplanBinary | |
| end | |
| function utils.drawSegmentation(floorplanComponent, numComponents) | |
| local colorMap = {} | |
| for i = 1, numComponents do | |
| colorMap[i] = torch.rand(3) | |
| end | |
| colorMap[0] = torch.zeros(3) | |
| local floorplanLabels = floorplanComponent:repeatTensor(3, 1, 1):double() | |
| for c = 1, 3 do | |
| floorplanLabels[c]:apply(function(x) return colorMap[x][c] end) | |
| end | |
| return floorplanLabels | |
| end | |
| function utils.parseFloorplan(floorplanInput) | |
| local binaryThreshold = 0.7 | |
| local floorplanSegmentation, numSegments, floorplanBinary = utils.segmentFloorplan(floorplanInput, binaryThreshold, 0) | |
| while (#floorplanSegmentation:eq(floorplanSegmentation[1][1]):nonzero())[1] > (#floorplanSegmentation)[1] * (#floorplanSegmentation)[2] * 0.8 and binaryThreshold < 1 do | |
| binaryThreshold = binaryThreshold + 0.1 | |
| floorplanSegmentation, numSegments, floorplanBinary = utils.segmentFloorplan(floorplanInput, binaryThreshold, 0) | |
| end | |
| --image.save('test/floorplan.png', floorplanInput) | |
| --image.save('test/floorplan_segmentation.png', ut.drawSegmentation(floorplanSegmentation, numSegments)) | |
| --image.save('test/floorplan_binary.png', floorplanBinary) | |
| local floorplanSegmentationReversed, numSegmentsReversed, floorplanBinaryReversed = utils.segmentFloorplan(floorplanInput, binaryThreshold, 0, true) | |
| --image.save('test/floorplan_binary_reversed.png', floorplanBinaryReversed) | |
| --image.save('test/floorplan.png', floorplanInput) | |
| --image.save('test/floorplan_segmentation_reversed.png', utils.drawSegmentation(floorplanSegmentationReversed, numSegmentsReversed)) | |
| local backgroundSegmentReversed = floorplanSegmentationReversed[1][1] | |
| --print(torch.min(floorplanSegmentationReversed)) | |
| --print(torch.max(floorplanSegmentationReversed)) | |
| --print(numSegmentsReversed) | |
| local maxCount = 0 | |
| local borderSegmentReversed = 0 | |
| for segmentReversed = 1, numSegmentsReversed do | |
| if segmentReversed ~= backgroundSegmentReversed then | |
| local indices = floorplanSegmentationReversed:eq(segmentReversed):nonzero() | |
| if ##indices > 0 then | |
| local count = (#indices)[1] | |
| if count > maxCount then | |
| borderSegmentReversed = segmentReversed | |
| maxCount = count | |
| end | |
| end | |
| end | |
| end | |
| if borderSegmentReversed == 0 then | |
| return | |
| end | |
| local characterMasks = torch.cmul(1 - floorplanSegmentationReversed:eq(backgroundSegmentReversed), 1 - floorplanSegmentationReversed:eq(borderSegmentReversed)) | |
| --image.save('test/invalid_mask_1.png', 1 - floorplanSegmentationReversed:eq(foregroundSegment)) | |
| --image.save('test/invalid_mask_2.png', 1 - floorplanSegmentationReversed:eq(maxCountSegment)) | |
| --image.save('test/character_masks.png', characterMasks:double()) | |
| local borderMask = floorplanSegmentationReversed:eq(borderSegmentReversed) | |
| local borderIndices = borderMask:nonzero() | |
| local borderSegment = floorplanSegmentation[{borderIndices[1][1], borderIndices[1][2]}] | |
| --[[ | |
| local borderThickness = 0 | |
| local borderMaskEroded = borderMask:clone() | |
| while true do | |
| if ##borderMaskEroded:eq(1):nonzero() > 0 then | |
| borderMaskEroded = image.erode(borderMaskEroded) | |
| borderThickness = borderThickness + 2 | |
| else | |
| borderThickness = borderThickness + 1 | |
| break | |
| end | |
| end | |
| ]]-- | |
| --print("borderThickness: " .. borderThickness) | |
| local borderThickness = 10 | |
| borderMask = image.dilate(borderMask) | |
| local floorplanSegmentationErodedOnce = torch.cmul(floorplanSegmentation, (1 - borderMask):int()) | |
| --[[ | |
| local numDilations = math.floor(borderThickness / 2) | |
| for i = 2, numDilations do | |
| borderMask = image.dilate(borderMask) | |
| end | |
| local floorplanSegmentationEroded = torch.cmul(floorplanSegmentation, (1 - borderMask):int()) | |
| ]]-- | |
| local floorplanSegmentationRefined = floorplanSegmentation:clone() | |
| local backgroundSegment = floorplanSegmentation[1][1] | |
| floorplanSegmentationRefined[floorplanSegmentation:eq(backgroundSegment)] = 1 | |
| floorplanSegmentationRefined[floorplanSegmentation:eq(borderSegment)] = 0 | |
| local segmentInfo = { | |
| mins = {}, | |
| maxs = {}, | |
| means = {}, | |
| nums = {}, | |
| samples = {}, | |
| } | |
| for segment = 1, numSegments do | |
| if segment ~= backgroundSegment and segment ~= borderSegment then | |
| local indices = floorplanSegmentation:eq(segment):nonzero() | |
| segmentInfo.mins[segment] = torch.min(indices, 1)[1] | |
| segmentInfo.maxs[segment] = torch.max(indices, 1)[1] | |
| segmentInfo.means[segment] = torch.mean(indices:double(), 1)[1] | |
| segmentInfo.nums[segment] = (#indices)[1] | |
| segmentInfo.samples[segment] = indices[1] | |
| end | |
| end | |
| local largeSegments = {} | |
| local smallSegments = {} | |
| for segment = 1, numSegments do | |
| if segment ~= backgroundSegment and segment ~= borderSegment then | |
| --if ##floorplanSegmentationEroded:eq(segment):nonzero() == 0 then | |
| --floorplanSegmentationRefined[floorplanSegmentation:eq(segment)] = 0 | |
| if ##floorplanSegmentationErodedOnce:eq(segment):nonzero() > 0 and (#floorplanSegmentationErodedOnce:eq(segment):nonzero())[1] == segmentInfo.nums[segment] then | |
| table.insert(smallSegments, segment) | |
| elseif segmentInfo.maxs[segment][1] - segmentInfo.mins[segment][1] < borderThickness or segmentInfo.maxs[segment][2] - segmentInfo.mins[segment][2] < borderThickness then | |
| floorplanSegmentationRefined[floorplanSegmentation:eq(segment)] = 0 | |
| else | |
| table.insert(largeSegments, segment) | |
| end | |
| end | |
| end | |
| local characterSegmentsReversed = {} | |
| for segmentReversed = 1, numSegmentsReversed do | |
| if segmentReversed ~= backgroundSegmentReversed and segmentReversed ~= borderSegmentReversed then | |
| table.insert(characterSegmentsReversed, segmentReversed) | |
| end | |
| end | |
| --print(smallSegments) | |
| local characterSegmentInfo = { | |
| mins = {}, | |
| maxs = {}, | |
| means = {}, | |
| nums = {}, | |
| } | |
| for _, characterSegmentReversed in pairs(characterSegmentsReversed) do | |
| local indices = floorplanSegmentationReversed:eq(characterSegmentReversed):nonzero() | |
| characterSegmentInfo.mins[characterSegmentReversed] = torch.min(indices, 1)[1] | |
| characterSegmentInfo.maxs[characterSegmentReversed] = torch.max(indices, 1)[1] | |
| characterSegmentInfo.means[characterSegmentReversed] = torch.mean(indices:double(), 1)[1] | |
| characterSegmentInfo.means[characterSegmentReversed] = (#indices)[1] | |
| end | |
| for _, smallSegment in pairs(smallSegments) do | |
| local minDistance | |
| local minDistanceLargeSegment | |
| for _, largeSegment in pairs(largeSegments) do | |
| if segmentInfo.mins[smallSegment][1] >= segmentInfo.mins[largeSegment][1] and segmentInfo.mins[smallSegment][2] >= segmentInfo.mins[largeSegment][2] and segmentInfo.maxs[smallSegment][1] <= segmentInfo.maxs[largeSegment][1] and segmentInfo.maxs[smallSegment][2] <= segmentInfo.maxs[largeSegment][2] then | |
| local distance = torch.norm(segmentInfo.means[smallSegment] - segmentInfo.means[largeSegment]) | |
| if minDistance == nil or distance < minDistance then | |
| minDistanceLargeSegment = largeSegment | |
| minDistance = distance | |
| end | |
| end | |
| end | |
| if minDistanceLargeSegment == nil then | |
| minDistanceLargeSegment = 1 | |
| end | |
| floorplanSegmentationRefined[floorplanSegmentation:eq(smallSegment)] = minDistanceLargeSegment | |
| end | |
| for _, characterSegmentReversed in pairs(characterSegmentsReversed) do | |
| local minDistance | |
| local minDistanceLargeSegment | |
| for _, largeSegment in pairs(largeSegments) do | |
| if characterSegmentInfo.mins[characterSegmentReversed][1] >= segmentInfo.mins[largeSegment][1] and characterSegmentInfo.mins[characterSegmentReversed][2] >= segmentInfo.mins[largeSegment][2] and characterSegmentInfo.maxs[characterSegmentReversed][1] <= segmentInfo.maxs[largeSegment][1] and characterSegmentInfo.maxs[characterSegmentReversed][2] <= segmentInfo.maxs[largeSegment][2] then | |
| local distance = torch.norm(characterSegmentInfo.means[characterSegmentReversed] - segmentInfo.means[largeSegment]) | |
| if minDistance == nil or distance < minDistance then | |
| minDistanceLargeSegment = largeSegment | |
| minDistance = distance | |
| end | |
| end | |
| end | |
| if minDistanceLargeSegment == nil then | |
| minDistanceLargeSegment = 1 | |
| end | |
| floorplanSegmentationRefined[floorplanSegmentationReversed:eq(characterSegmentReversed)] = minDistanceLargeSegment | |
| characterMasks[floorplanSegmentationReversed:eq(characterSegmentReversed)] = minDistanceLargeSegment | |
| end | |
| --[[ | |
| segmentInfo.indices = {} | |
| for _, largeSegment in pairs(largeSegments) do | |
| local indices = floorplanSegmentationRefined:eq(largeSegment):nonzero() | |
| segmentInfo.indices[largeSegment] = indices | |
| segmentInfo.mins[largeSegment] = torch.min(indices, 1)[1] | |
| segmentInfo.maxs[largeSegment] = torch.max(indices, 1)[1] | |
| segmentInfo.means[largeSegment] = torch.mean(indices:double(), 1)[1] | |
| segmentInfo.nums[largeSegment] = (#indices)[1] | |
| end | |
| ]]-- | |
| local borderMask = floorplanSegmentationRefined:eq(0) | |
| borderMask[floorplanSegmentationRefined:eq(1)] = 1 | |
| borderMask = image.erode(borderMask) | |
| local borderMaskByte = (borderMask * 255):byte() | |
| local floorplanSegmentationCoarse = torch.IntTensor(borderMaskByte:size()) | |
| local numSegmentCoarse = cv.connectedComponents{255 - borderMaskByte, floorplanSegmentationCoarse} | |
| floorplanSegmentationCoarse = floorplanSegmentationCoarse + 1 | |
| --image.save('test/floorplan_segmentation_coarse.png', ut.drawSegmentation(floorplanSegmentationCoarse, numSegmentCoarse)) | |
| local coarseToFineSegmentMap = {} | |
| for segment = 1, numSegmentCoarse do | |
| coarseToFineSegmentMap[segment] = {} | |
| end | |
| for _, largeSegment in pairs(largeSegments) do | |
| table.insert(coarseToFineSegmentMap[floorplanSegmentationCoarse[segmentInfo.samples[largeSegment][1]][segmentInfo.samples[largeSegment][2]]], largeSegment) | |
| end | |
| for segmentCoarse = 1, numSegmentCoarse do | |
| local mins | |
| local maxs | |
| local maxNum | |
| local maxNumSegment | |
| local count = 0 | |
| for _, largeSegment in pairs(coarseToFineSegmentMap[segmentCoarse]) do | |
| count = count + 1 | |
| if maxNum == nil or segmentInfo.nums[largeSegment] > maxNum then | |
| maxNumSegment = largeSegment | |
| maxNum = segmentInfo.nums[largeSegment] | |
| end | |
| if mins == nil then | |
| mins = segmentInfo.mins[largeSegment] | |
| maxs = segmentInfo.maxs[largeSegment] | |
| else | |
| for i = 1, 2 do | |
| mins[i] = math.min(mins[i], segmentInfo.mins[largeSegment][i]) | |
| maxs[i] = math.max(maxs[i], segmentInfo.maxs[largeSegment][i]) | |
| end | |
| end | |
| end | |
| if count > 1 then | |
| local newSegmentWidth = maxs[1] - mins[1] + 1 | |
| local newSegmentHeight = maxs[2] - mins[2] + 1 | |
| local cornerMask = torch.zeros((#floorplanSegmentation)[1], (#floorplanSegmentation)[2]) | |
| cornerMask:narrow(1, mins[1], newSegmentWidth):narrow(2, mins[2], newSegmentHeight):fill(1) | |
| cornerMask = torch.cmul(cornerMask:byte(), 1 - floorplanSegmentationCoarse:eq(segmentCoarse)) | |
| cornerMask:narrow(1, segmentInfo.mins[maxNumSegment][1], segmentInfo.maxs[maxNumSegment][1] - segmentInfo.mins[maxNumSegment][1] + 1):narrow(2, segmentInfo.mins[maxNumSegment][2], segmentInfo.maxs[maxNumSegment][2] - segmentInfo.mins[maxNumSegment][2] + 1):fill(0) | |
| --local oriSegmentArea = (segmentInfo.maxs[maxNumSegment][1] - segmentInfo.mins[maxNumSegment][1] + 1) * (segmentInfo.maxs[maxNumSegment][2] - segmentInfo.mins[maxNumSegment][2] + 1) | |
| if ##cornerMask:nonzero() == 0 or (#cornerMask:nonzero())[1] < newSegmentWidth * newSegmentHeight * 0.1 then | |
| for _, largeSegment in pairs(coarseToFineSegmentMap[segmentCoarse]) do | |
| if largeSegment ~= maxNumSegment then | |
| floorplanSegmentationRefined[floorplanSegmentationRefined:eq(largeSegment)] = maxNumSegment | |
| characterMasks[characterMasks:eq(largeSegment)] = maxNumSegment | |
| end | |
| end | |
| end | |
| end | |
| end | |
| local newSegment = 1 | |
| for segment = 1, numSegments do | |
| if ##floorplanSegmentationRefined:eq(segment):nonzero() > 0 then | |
| floorplanSegmentationRefined[floorplanSegmentationRefined:eq(segment)] = newSegment | |
| characterMasks[characterMasks:eq(segment)] = newSegment | |
| newSegment = newSegment + 1 | |
| end | |
| end | |
| local numSegmentsRefined = newSegment - 1 | |
| return floorplanSegmentationRefined, characterMasks, numSegmentsRefined | |
| end | |
| return utils | |