Skip to content

Commit 87f85e6

Browse files
authored
Fix several issues (but not all) with bwconncomp and image layouts. (#1837)
1 parent a9991c6 commit 87f85e6

3 files changed

Lines changed: 65 additions & 31 deletions

File tree

src/imgmorph/cc2bw.jl

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,17 @@
11
"""
2+
BW = cc2bw(cc::GMTConComp; obj2keep::Union{Int, Vector{Int}}=0)
3+
4+
Convert connected components to binary image
25
"""
3-
function cc2bw(cc::GMTConComp)
6+
function cc2bw(cc::GMTConComp; obj2keep::Union{Int, Vector{Int}}=0)
47
# Create a binary image of the same size as the original
58
I = mat2img(zeros(Bool, cc.image_size), x=cc.x, y=cc.y, inc=cc.inc, layout=cc.layout,
69
is_transposed=(cc.layout[2] == 'R'), proj4=cc.proj4, wkt=cc.wkt, epsg=cc.epsg)
710

811
# Mark pixels belonging to any connected component as true
9-
for pixel_list in cc.pixel_list
10-
for rc in pixel_list
12+
plist = (obj2keep == 0) ? (1:cc.num_objects) : isvector(obj2keep) ? obj2keep : (obj2keep:obj2keep)
13+
for k = 1:numel(plist)
14+
for rc in cc.pixel_list[plist[k]]
1115
I[rc] = true
1216
end
1317
end

src/lepto_funs.jl

Lines changed: 47 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1190,8 +1190,10 @@ Base.@kwdef struct GMTConComp
11901190
proj4::String=""
11911191
wkt::String=""
11921192
epsg::Int=0
1193-
bboxs::Vector{GMTdataset{Float64,2}}=GMTdataset{Float64,2}[] # The bounding boxes as datasets
1194-
pixel_list::Vector{Vector{Int32}}=Vector{Int32}[] # The list of pixel indices for each component
1193+
bbox::Vector{GMTdataset{Float64,2}}=GMTdataset{Float64,2}[] # The bounding boxes as datasets
1194+
pixel_list::Vector{Vector{Int32}}=Vector{Int32}[] # The list of pixel indices for each component
1195+
centroid::Matrix{Float64}=Matrix{Float64}(undef, 0, 0) # The centroids of each component
1196+
area::Vector{Float64}=Float64[] # The areas of each component
11951197
end
11961198

11971199
"""
@@ -1220,8 +1222,10 @@ A `GMTConComp` structure with the following fields:
12201222
- `proj4`: Projection definition of the image used to find the components.
12211223
- `wkt`: Well-known text definition of the image used to find the components.
12221224
- `epsg`: EPSG code of the image used to find the components.
1223-
- `bboxs`: The bounding boxes as a vector of GMTdataset.
1224-
- `pixel_list`: A vevtor of vectors with the list of linear pixel indices for each component.
1225+
- `bbox`: The bounding boxes as a vector of GMTdataset.
1226+
- `pixel_list`: A vector of vectors with the list of linear pixel indices for each component.
1227+
- `centroid`: A vector of Float64 Tuples with the x,y coordinates of the centroids for each component.
1228+
- `area`: A vector of Float64 with the areas of each component. This area is approximated by the mean lat for geog images.
12251229
"""
12261230
bwconncomp(mat::BitMatrix; conn::Int=8) = bwconncomp(mat2img(collect(mat)); conn=conn)
12271231
bwconncomp(mat::Matrix{<:Bool}; conn::Int=8) = bwconncomp(mat2img(mat); conn=conn)
@@ -1241,30 +1245,53 @@ function bwconncomp(I::GMTimage; conn::Int=8)
12411245
#ppix = unsafe_load(unsafe_load(pixa).pix, 1)
12421246
#return pix2img(Sppix(ppix))
12431247

1244-
boxa = unsafe_load(pboxa)
1245-
D = Vector{GMTdataset{Float64,2}}(undef, boxa.n)
1248+
is_geog = isgeog(I)
1249+
cellsize = I.inc[1] * I.inc[2]
1250+
boxa = unsafe_load(pboxa)
1251+
D = Vector{GMTdataset{Float64,2}}(undef, boxa.n)
1252+
centroid = Matrix{Float64}(undef, boxa.n, 2)
1253+
area = Vector{Float64}(undef, boxa.n)
12461254
pixel_list = [Int32[] for _ in 1:boxa.n]
12471255
width, height = getsize(I)
1256+
is_col_maj = (I.layout[2] == 'C')
1257+
is_top_down = (I.layout[1] == 'T')
1258+
12481259
@inbounds for k = 1:boxa.n
1249-
b = unsafe_load(unsafe_load(boxa.box, k)) # A Box, x,y counting seem to be 0-based
1250-
y2 = I.y[Int32(width - b.y)]
1251-
y1 = I.y[Int32(width - b.y - b.h)]
1252-
x1, x2 = I.x[b.x + I.registration], I.x[b.x + b.w + I.registration] # Still not very clear where/why +1's are needed
1253-
D[k] = mat2ds([x1 y1; x1 y2; x2 y2; x2 y1; x1 y1]) # The bounding box
1254-
D[k].bbox = [x1, x2, y1, y2] # (xmin, xmax, ymin, ymax)
1255-
if (I.layout[2] == 'C') # Column major
1256-
jj = (I.layout[1] == 'T') ? (b.y+1:b.y+b.h) : (height-b.y:-1:height - b.y - b.h + 1)
1257-
@inbounds for i = b.x+1:b.x+b.w, j = jj
1258-
(I[j,i] != 0) && append!(pixel_list[k], (i-1) * height + j)
1260+
b = unsafe_load(unsafe_load(boxa.box, k)) # A Box, x,y counting seem to be 0-based
1261+
y2 = I.y[Int32(height - b.y) + 1]
1262+
y1 = I.y[Int32(height - b.y - b.h) + 1]
1263+
x1, x2 = I.x[b.x + I.registration], I.x[b.x + b.w + I.registration]
1264+
D[k] = mat2ds([x1 y1; x1 y2; x2 y2; x2 y1; x1 y1]) # The bounding box
1265+
D[k].bbox = [x1, x2, y1, y2] # (xmin, xmax, ymin, ymax)
1266+
half_pix_x, half_pix_y = I.registration * I.inc[1]/2, I.registration * I.inc[2]/2
1267+
im, jm = 0.0, 0.0
1268+
1269+
if (is_col_maj) # Column major
1270+
jj = (is_top_down) ? (b.y+1:b.y+b.h) : (height-b.y:-1:height - b.y - b.h + 1)
1271+
@inbounds for i in b.x+1:b.x+b.w # Xaxis
1272+
@inbounds for j in jj
1273+
(I[j,i] != 0) && (append!(pixel_list[k], (i-1) * height + j); jm += j; im += i)
1274+
end
12591275
end
1260-
else # Row major
1261-
@inbounds for j = b.y+1:b.y+b.h, i = b.x+1:b.x+b.w # Oddly here we do not need to care about 'T' or 'B'
1262-
(I[i,j] != 0) && append!(pixel_list[k], (j-1) * width + i)
1276+
n_px = length(pixel_list[k])
1277+
t = (is_top_down) ? (I.range[4] - half_pix_y) - (jm / n_px - 1)*I.inc[2] : (I.range[3] + half_pix_x) + (jm / n_px - 1)*I.inc[2]
1278+
centroid[k,1:2] .= (I.range[1] + half_pix_x) + (im / n_px - 1)*I.inc[1], t
1279+
else # Row major
1280+
@inbounds for j = b.y+1:b.y+b.h # Yaxis.
1281+
@inbounds for i = b.x+1:b.x+b.w
1282+
(I[i,j] != 0) && (append!(pixel_list[k], (j-1) * width + i); im += i; jm += j)
1283+
end
12631284
end
1285+
n_px = length(pixel_list[k])
1286+
t = (is_top_down) ? (I.range[2] - half_pix_y) - (jm / n_px - 1)*I.inc[1] : (I.range[1] + half_pix_y) + (jm / n_px - 1)*I.inc[1]
1287+
centroid[k,1:2] .= (I.range[3] + half_pix_x) + (im / n_px - 1)*I.inc[2], t
12641288
end
1289+
lat_fact = (is_geog) ? cosd(centroid[k][2]) : 1.0 # Big approximation for the geog case but exact solution is too complex here
1290+
area[k] = n_px * cellsize * lat_fact
12651291
end
12661292
set_dsBB!(D, true) # Add the bounding box
1267-
GMTConComp(conn, pcount[], size(I), I.range, I.inc, I.registration, I.x, I.y, I.layout, I.proj4, I.wkt, I.epsg, D, pixel_list)
1293+
GMTConComp(conn, pcount[], size(I), I.range, I.inc, I.registration, I.x, I.y, I.layout, I.proj4, I.wkt, I.epsg,
1294+
D, pixel_list, centroid, area)
12681295
end
12691296

12701297
function get_ppixI(I)

test/test_lepto_funs.jl

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -62,21 +62,24 @@ end
6262
@test filled_gmt.image[1, 1] == 255
6363
end
6464

65-
#=
65+
##
6666
@testset "bwconncomp tests" begin
6767
I = gdalread(TESTSDIR * "assets/packman.png");
6868
cc1 = bwconncomp(I);
6969
@test cc1.num_objects == 2
7070
I = gdalread(TESTSDIR * "assets/packman.png", layout="BCBa");
7171
cc2 = bwconncomp(I);
7272
@test cc2.num_objects == 2
73-
@test cc1.bboxs == cc2.bboxs
74-
#I = gdalread(TESTSDIR * "assets/packman.png", layout="TCBa"); # Need to fix bug in gdalread for this to work
75-
#cc2 = bwconncomp(I);
76-
#@test cc2.num_objects == 2
77-
#@test cc1.bboxs == cc2.bboxs
73+
@test cc1.bbox == cc2.bbox
74+
I = gdalread(TESTSDIR * "assets/packman.png", layout="TCBa");
75+
cc2 = bwconncomp(I);
76+
@test cc2.num_objects == 2
77+
@test cc1.bbox == cc2.bbox # This guy fails
7878

79-
mat = zeros(Bool,8,9); mat[2:5,2:3] .= true; mat[4:5,4] .= true; mat[5:8,6:8] .= true; mat[4,7] = true;
79+
# LEPTONICA is BUGGED. This trims last column of second object
80+
#mat = zeros(Bool,8,10); mat[2:5,2:3] .= true; mat[4:5,4] .= true; mat[5:8,7:9] .= true; mat[4,8] = true;
81+
# But this one (moving second obj one pixel to the left) works
82+
mat = zeros(Bool,8,10); mat[2:5,2:3] .= true; mat[4:5,4] .= true; mat[5:8,6:8] .= true; mat[4,7] = true;
8083
gdalwrite("c2.png", mat2img(mat))
8184
I = gdalread("c2.png"); # Comes in with layout TRBa
8285
cc = bwconncomp(I);
@@ -94,7 +97,7 @@ end
9497
I2 = cc2bw(cc);
9598
@test I == I2
9699
end
97-
=#
100+
##
98101

99102
G = peaks();
100103
G2 = fillsinks(G);

0 commit comments

Comments
 (0)