Skip to content

Commit 82e594d

Browse files
torch smoothing
1 parent f3980ef commit 82e594d

5 files changed

Lines changed: 46 additions & 65 deletions

File tree

DESCRIPTION

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -315,3 +315,4 @@ Collate:
315315
'sits_xlsx.R'
316316
'zzz.R'
317317
Config/roxygen2/version: 8.1.0
318+
RoxygenNote: 7.3.2

NAMESPACE

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -650,9 +650,7 @@ export(sits_view)
650650
export(sits_xgboost)
651651
importFrom(Rcpp,sourceCpp)
652652
importFrom(dplyr,.data)
653-
importFrom(lubridate,
654-
"%m+%",
655-
"%within%"
656-
)
653+
importFrom(lubridate,"%m+%")
654+
importFrom(lubridate,"%within%")
657655
importFrom(utils,read.csv)
658656
useDynLib(sits, .registration = TRUE)

R/api_bstorch.R

Lines changed: 39 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
#' @name .torch_smooth_bayes_fraction
33
#' @keywords internal
44
#' @noRd
5-
#' @author Gilberto Camara, \email{gilberto.camara@@inpe.br}
5+
#' @author Alexandre Assuncao, \email{alexcarssuncao@@gmail.com}
66
#'
77
#' @description
88
#' Drop-in replacement for the C++ \code{bayes_smoother_fraction()} function.
@@ -34,7 +34,7 @@
3434
window_size,
3535
smoothness,
3636
neigh_fraction) {
37-
# Select compute device
37+
# Select device
3838
device <- if (torch::cuda_is_available()) {
3939
"cuda"
4040
} else if (torch::backends_mps_is_available()) {
@@ -43,93 +43,75 @@
4343
"cpu"
4444
}
4545

46-
nbands <- ncol(logits)
47-
npix <- nrows * ncols
48-
leg <- window_size %/% 2L
49-
win_sq <- as.integer(window_size * window_size)
46+
nbands <- ncol(logits) # Number of bands in data
47+
npix <- nrows * ncols # Number of pixels in data
48+
leg <- window_size %/% 2L # Floor of window size over two
49+
win_sq <- as.integer(window_size * window_size) # Number of pixels in window
5050

51-
# ---- Build input tensor [1, nbands, nrows, ncols] ----
51+
# Build input tensor [1, nbands, nrows, ncols]
5252
x <- torch::torch_tensor(
5353
t(logits),
5454
dtype = torch::torch_float32(),
5555
device = device
5656
)$view(c(nbands, nrows, ncols))$unsqueeze(1L)
5757

58-
# ---- Reflect-pad spatial dimensions (all bands at once — cheap) ----
58+
# Padding spatial dimensions with reflect [1, nbands, nrows+2*leg, ncols+2*leg]
59+
# NOTE: Due to overlapping tiles, the padded values are later discarded
5960
x_pad <- torch::nnf_pad(
6061
x,
6162
pad = c(leg, leg, leg, leg),
6263
mode = "reflect"
6364
)
64-
# x_pad: [1, nbands, nrows+2*leg, ncols+2*leg]
6565

66-
# ---- Reusable scalar tensors and position index ----
67-
zero_t <- torch::torch_tensor(
68-
0.0, dtype = torch::torch_float32(), device = device
69-
)
70-
neginf_t <- torch::torch_tensor(
71-
-Inf, dtype = torch::torch_float32(), device = device
72-
)
73-
# Position index for top-fraction selection [1, win_sq]
66+
# Aux zero tensor
67+
zero_t <- torch::torch_tensor(0.0, dtype = torch::torch_float32(), device = device)
68+
# Aux minus inf tensor
69+
neginf_t <- torch::torch_tensor(-Inf, dtype = torch::torch_float32(), device = device)
70+
# Aux index tensor for neighbor selection [1, win_sq]
7471
pos <- torch::torch_tensor(
7572
seq_len(win_sq),
7673
dtype = torch::torch_float32(),
7774
device = device
7875
)$view(c(1L, win_sq))
7976

80-
# ---- Original pixel values for all bands [npix, nbands] ----
81-
x0_all <- torch::torch_tensor(
82-
logits, dtype = torch::torch_float32(), device = device
83-
)
77+
# Original pixel values for all bands [npix, nbands]
78+
x0_all <- torch::torch_tensor(logits, dtype = torch::torch_float32(), device = device)
8479

85-
# ---- Process one band at a time to limit GPU memory ----
86-
# Without this, nnf_unfold on all bands produces a
87-
# [nbands, npix, win_sq] tensor that can easily exceed GPU memory.
80+
# Storage for results per-band (Avoid blowing up GPU memory)
8881
band_results <- vector("list", nbands)
82+
83+
# --- MAIN LOOP
8984
for (b in seq_len(nbands)) {
90-
# Extract single band from padded tensor: [1, 1, H_pad, W_pad]
85+
# Get band b from padded tensor, i.e. [1, 1, H_pad, W_pad]
9186
x_b <- x_pad[, b, , , drop = FALSE]
92-
93-
# Unfold windows: [1, win_sq, npix] → [npix, win_sq]
87+
# Unfold windows, i.e. [1, win_sq, npix] to [npix, win_sq]
9488
wins_b <- torch::nnf_unfold(
9589
x_b,
9690
kernel_size = window_size,
9791
stride = 1L,
9892
padding = 0L
9993
)$squeeze(1L)$t()
100-
# wins_b: [npix, win_sq]
101-
102-
# ---- NaN handling ----
94+
# Dealing with NA
10395
nan_mask_b <- torch::torch_isnan(wins_b)
104-
105-
# ---- Select neighbourhood values ----
96+
# Select neighbourhood values
10697
if (neigh_fraction == 1.0) {
10798
selected_b <- torch::torch_where(nan_mask_b, zero_t, wins_b)
10899
sel_mask_b <- !nan_mask_b
109-
n_b <- (!nan_mask_b)$sum(dim = -1L)$to(
110-
dtype = torch::torch_float32()
111-
)
100+
n_b <- (!nan_mask_b)$sum(dim = -1L)$to(dtype = torch::torch_float32())
112101
} else {
113102
wins_sort_b <- torch::torch_where(nan_mask_b, neginf_t, wins_b)
114-
sorted_b <- torch::torch_sort(
115-
wins_sort_b, dim = -1L, descending = TRUE
116-
)[[1L]]
103+
sorted_b <- torch::torch_sort(wins_sort_b, dim = -1L, descending = TRUE)[[1L]]
117104

118-
valid_b <- (!nan_mask_b)$sum(dim = -1L)$to(
119-
dtype = torch::torch_float32()
120-
)
121-
neigh_hi_b <- torch::torch_ceil(
122-
neigh_fraction * valid_b
123-
)$clamp_min(1L)
105+
valid_b <- (!nan_mask_b)$sum(dim = -1L)$to(dtype = torch::torch_float32())
106+
neigh_hi_b <- torch::torch_ceil(neigh_fraction * valid_b)$clamp_min(1L)
124107

125108
sel_mask_b <- pos$le(neigh_hi_b$unsqueeze(-1L))
126109
selected_b <- torch::torch_where(sel_mask_b, sorted_b, zero_t)
127110
n_b <- neigh_hi_b
128111
}
129-
130-
# ---- Unbiased mean and variance ----
112+
# Calculating empirical mean
131113
m0_b <- selected_b$sum(dim = -1L) / n_b
132-
114+
# Calculating empirical variance
133115
diff_sq_b <- torch::torch_where(
134116
sel_mask_b,
135117
(selected_b - m0_b$unsqueeze(-1L))$pow(2L),
@@ -140,25 +122,24 @@
140122
diff_sq_b$sum(dim = -1L) / (n_b - 1.0),
141123
zero_t
142124
)
143-
144-
# ---- Bayesian update for this band ----
145-
x0_b <- x0_all[, b]
146-
w_b <- s0_b / (s0_b + smoothness[b])
125+
# Bayesian update for band b
126+
x0_b <- x0_all[, b]
127+
w_b <- s0_b / (s0_b + smoothness[b])
147128
bayes_b <- w_b * x0_b + (1.0 - w_b) * m0_b
148129
use_m0 <- torch::torch_isnan(x0_b) | s0_b$lt(1e-4)
149-
130+
# Store results
150131
band_results[[b]] <- torch::torch_where(use_m0, m0_b, bayes_b)
151132
}
152133

153-
# ---- Stack bands and convert to R matrix [npix, nbands] ----
134+
# Stack all bands and convert to matrix with dim [npix, nbands]
154135
result <- torch::torch_stack(band_results, dim = 2L)
155136
as.matrix(
156137
result$cpu()$to(dtype = torch::torch_float64())
157138
)
158139
}
159140

160141
#' @title Torch Bayesian smoother closure factory
161-
#' @name .smooth_fn_bayes_torch
142+
#' @name .smooth_fn_bayes_torch
162143
#' @keywords internal
163144
#' @noRd
164145
#'
@@ -240,12 +221,16 @@
240221
output_dir,
241222
version,
242223
progress) {
224+
# Define Bayesian Smoothing function
243225
smooth_fn <- .smooth_fn_bayes_torch(
244226
window_size = window_size,
245227
neigh_fraction = neigh_fraction,
246228
smoothness = smoothness
247229
)
230+
# The overlap makes sure that the padded values added for torch
231+
# computations are discarded
248232
overlap <- ceiling(window_size / 2L) - 1L
233+
# Use sits cube api to iterate over tiles
249234
.cube_foreach_tile(cube, function(tile) {
250235
.smooth_tile(
251236
tile = tile,

R/sits_bstorch.R

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,7 @@
22
#'
33
#' @name sits_smooth_torch
44
#'
5-
#' @author Gilberto Camara, \email{gilberto.camara@@inpe.br}
6-
#' @author Rolf Simoes, \email{rolfsimoes@@gmail.com}
5+
#' @author Alexandre Assuncao, \email{alexcarssuncao@@gmail.com}
76
#'
87
#' @description
98
#' Torch-backed alternative to \code{\link[sits]{sits_smooth}}. Applies the
@@ -131,8 +130,8 @@ sits_smooth_torch.probs_cube <- function(cube,
131130
memsize = memsize,
132131
multicores = multicores
133132
)
134-
if (.parallel_start(workers = multicores))
135-
on.exit(.parallel_stop(), add = TRUE)
133+
started <- .parallel_start(workers = multicores)
134+
on.exit(.parallel_stop(started), add = TRUE)
136135

137136
# Call torch-backed orchestrator
138137
.smooth_torch(

man/sits_smooth_torch.Rd

Lines changed: 1 addition & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)