|
172 | 172 | filters_output = ODOG.bank.apply(stimulus)
|
173 | 173 | weighted_outputs = ODOG.weight_outputs(filters_output)
|
174 | 174 |
|
175 |
| -norm_outputs = LODOG.normalize_outputs(weighted_outputs) |
| 175 | +norm_outputs = LODOG.normalize_outputs(weighted_outputs, eps=1e-6) |
176 | 176 | output_LODOG = norm_outputs.sum(axis=(0, 1))
|
177 | 177 |
|
178 | 178 | # %% Extract target prediction
|
|
261 | 261 | assert np.array_equal(norm_weights, LODOG.normalization_weights)
|
262 | 262 |
|
263 | 263 | # %% Normalizing images as weighted combination (tensor dot-product) of filter outputs
|
264 |
| -normalizing_coefficients = multyscale.normalization.normalizers(weighted_outputs, norm_weights) |
| 264 | +normalizing_coefficients = multyscale.normalization.norm_coeffs(weighted_outputs, norm_weights) |
| 265 | +assert np.array_equal(normalizing_coefficients, LODOG.norm_coeffs(weighted_outputs)) |
265 | 266 |
|
266 |
| -# Visualize each normalizing coefficient n_{o,s}, i.e. |
267 |
| -# the normalizer image for each individual filter f_{o,s} |
| 267 | +# Visualize each normalizing coefficient n_{o,s}, i.e. for each individual filter f_{o,s} |
268 | 268 | fig, axs = plt.subplots(*normalizing_coefficients.shape[:2], sharex="all", sharey="all")
|
269 | 269 | for o, s in np.ndindex(normalizing_coefficients.shape[:2]):
|
270 | 270 | axs[o, s].imshow(normalizing_coefficients[o, s], cmap="coolwarm", extent=visextent)
|
|
346 | 346 | plt.show()
|
347 | 347 |
|
348 | 348 | # %% [markdown]
|
349 |
| -# The function `multyscale.normalization.spatial_avg_windows_gaussian()` |
| 349 | +# The function `multyscale.normalization.spatial_kernel_gaussian()` |
350 | 350 | # generates a ( $O\times S$ set of) Gaussian filters $G$,
|
351 |
| -# where each Gaussian $G_{o',s'}$ is used to locally average filter $f_{o',s'}$. |
| 351 | +# where each Gaussian spatial averaging window |
| 352 | +# $G_{o',s'}$ is used to locally average filter $f_{o',s'}$. |
352 | 353 | # In the LODOG model, all $G$ are identical.
|
353 | 354 | #
|
354 | 355 | # The parameter $\sigma$ controls the spatial size of each $G(\sigma)$ Gaussian filter;
|
355 | 356 | # thus this function takes in $O \times S$ $\sigma_{o', s'}$.
|
356 | 357 | # In the LODOG model, all $\sigma_{o', s'}$ are identical.
|
357 | 358 |
|
358 |
| -# %% Spatial Gaussian |
359 |
| -sigmas = LODOG.window_sigmas |
360 |
| -G = multyscale.normalization.spatial_avg_windows_gaussian(LODOG.bank.x, LODOG.bank.y, sigmas) |
| 359 | +# %% All sigmas identical |
| 360 | +print(LODOG.window_sigmas) |
361 | 361 |
|
362 |
| -assert np.array_equal(G[0, 0, :], window) |
| 362 | +# %% Generate Gaussian filters |
| 363 | +spatial_windows = np.ndarray(filters_output.shape) |
| 364 | +for o, s in np.ndindex(LODOG.window_sigmas.shape[:2]): |
| 365 | + spatial_windows[o, s, :] = multyscale.normalization.spatial_kernel_gaussian( |
| 366 | + LODOG.bank.x, LODOG.bank.y, LODOG.window_sigmas[o, s, :] |
| 367 | + ) |
| 368 | + |
| 369 | +assert np.array_equal(spatial_windows[0, 0, :], window) |
| 370 | +assert np.array_equal(spatial_windows, LODOG.spatial_kernels()) |
363 | 371 |
|
364 | 372 | idx = (2, 3)
|
365 | 373 |
|
366 |
| -plt.imshow(G[*idx], cmap="coolwarm", extent=visextent) |
| 374 | +plt.imshow(spatial_windows[*idx], cmap="coolwarm", extent=visextent) |
367 | 375 | plt.show()
|
368 | 376 |
|
369 | 377 | # %% [markdown]
|
370 | 378 | # Applying this Gaussian window gives the _local_ (estimate of) of energy
|
371 | 379 |
|
372 |
| -# %% Local RMS |
373 |
| -normalization_local_RMS = np.square(normalizing_coefficients.copy()) |
| 380 | +# %% Local energy estimates |
| 381 | +normalization_local_energies = np.ndarray(normalizing_coefficients.shape) |
374 | 382 | for o, s in np.ndindex(normalizing_coefficients.shape[:2]):
|
375 |
| - normalization_local_RMS[o, s] = multyscale.filters.apply( |
376 |
| - window, normalization_local_RMS[o, s], padval=0 |
| 383 | + coeff = normalizing_coefficients[o, s] ** 2 |
| 384 | + energy = multyscale.filters.apply( |
| 385 | + spatial_windows[o, s], coeff, padval=0 |
377 | 386 | )
|
| 387 | + energy = np.sqrt(energy + 1e-6) # minor offset to avoid negatives/0's |
| 388 | + normalization_local_energies[o, s, :] = energy |
378 | 389 |
|
379 |
| -normalization_local_RMS = ( |
380 |
| - np.sqrt(normalization_local_RMS + 1e-6) + 1e-6 |
381 |
| -) # minor offset to avoid negatives/0's |
382 |
| - |
383 |
| -assert np.array_equal(normalization_local_RMS, LODOG.normalizers_to_RMS(normalizing_coefficients)) |
| 390 | +assert np.allclose(normalization_local_energies, LODOG.norm_energies(normalizing_coefficients, eps=1e-6)) |
384 | 391 |
|
385 | 392 | # Visualize each local RMS
|
386 |
| -fig, axs = plt.subplots(*normalization_local_RMS.shape[:2], sharex="all", sharey="all") |
387 |
| -for o, s in np.ndindex(normalization_local_RMS.shape[:2]): |
388 |
| - axs[o, s].imshow(normalization_local_RMS[o, s], cmap="coolwarm", extent=visextent) |
| 393 | +fig, axs = plt.subplots(*normalization_local_energies.shape[:2], sharex="all", sharey="all") |
| 394 | +for o, s in np.ndindex(normalization_local_energies.shape[:2]): |
| 395 | + axs[o, s].imshow(normalization_local_energies[o, s], cmap="coolwarm", extent=visextent) |
389 | 396 | fig.supxlabel("Spatial scale/freq. $s'$")
|
390 | 397 | fig.supylabel("Orientation $o'$")
|
391 | 398 | plt.show()
|
|
420 | 427 | # get normalized quite differently.
|
421 | 428 | #
|
422 | 429 | # We now divide each filter(output) $f_{o',s'}$
|
423 |
| -# by the spatial RMS of the normalizer coefficient $n_{o',s'}$: |
| 430 | +# by the spatial RMS of the normalizing coefficient $n_{o',s'}$: |
424 | 431 | # $$f'_{o',s'} = \frac{f_{o',s'}}{RMS(n_{o',s'})}$$
|
425 | 432 |
|
426 | 433 | # %% Divisive normalization
|
427 | 434 | # Since the local RMSs tensor is the same (O, S, X, Y) shape as the filter outputs
|
428 | 435 | # we can simply divide
|
429 |
| -normalized_outputs = weighted_outputs / normalization_local_RMS |
430 |
| -assert np.array_equal(normalized_outputs, norm_outputs) |
| 436 | +normalized_outputs = weighted_outputs / (normalization_local_energies + 1e-6) |
| 437 | +assert np.allclose(normalized_outputs, norm_outputs) |
431 | 438 |
|
432 | 439 | # Visualize each normalized f'_{o',s'}
|
433 | 440 | fig, axs = plt.subplots(*normalized_outputs.shape[:2], sharex="all", sharey="all")
|
|
456 | 463 |
|
457 | 464 | # %% Recombine
|
458 | 465 | recombined_outputs = np.sum(normalized_outputs, axis=(0, 1))
|
459 |
| -assert np.array_equal(recombined_outputs, output_LODOG) |
| 466 | +assert np.allclose(recombined_outputs, output_LODOG) |
460 | 467 |
|
461 | 468 | plt.subplot(2, 2, 1)
|
462 | 469 | plt.imshow(recombined_outputs, cmap="coolwarm", extent=visextent)
|
|
0 commit comments