AKTIVE

histogram.tcl at trunk
Login

histogram.tcl at trunk

File etc/transformer/statistics/histogram.tcl artifact 9cf8781e89 on branch trunk


     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
   100
   101
   102
   103
   104
   105
   106
   107
   108
   109
   110
   111
   112
   113
   114
   115
   116
   117
   118
   119
   120
   121
   122
   123
   124
   125
   126
   127
   128
   129
   130
   131
   132
   133
   134
   135
   136
   137
   138
   139
   140
   141
   142
   143
   144
   145
   146
   147
   148
   149
   150
   151
   152
   153
   154
   155
   156
   157
   158
   159
   160
   161
   162
   163
   164
   165
   166
   167
   168
   169
   170
   171
   172
   173
   174
   175
   176
   177
   178
   179
   180
   181
   182
   183
   184
   185
   186
   187
   188
   189
   190
   191
   192
   193
   194
   195
   196
   197
   198
   199
   200
   201
   202
   203
   204
   205
   206
   207
   208
   209
   210
   211
   212
   213
   214
   215
   216
   217
   218
   219
   220
   221
   222
   223
   224
   225
   226
   227
   228
   229
   230
   231
   232
   233
   234
   235
   236
   237
   238
   239
   240
   241
   242
   243
   244
   245
   246
   247
   248
   249
   250
## -*- mode: tcl ; fill-column: 90 -*-
# # ## ### ##### ######## ############# #####################
## Transformers -- Histogram Statistics

## Not handled in the by-* specs. Histogram statistics are sufficiently different in the
## details of their calculation from the others (aktive_histogram* client data, etc.) to
## warrant separate handling. Therefore sliced on the operation instead.
#
## NOTES
##
## - Caches are used to hold full row|column histograms. While this requires more memory
##   we get higher speed as no histogram is computed more than once.  Should we find that
##   memory usage is too high we can
##   (1) limit the cache size, at the at the cost of having to re-compute some histograms.
##   (2) spill to and reload from disk. Beware that this may be slower than a re-compute.
##
## - For band and tile histograms caches are not needed, as both return per-pixel results
##   and automatically do not re-compute anything.
#
## This is a place where future management of request types (i.e. by rows, by columns, by
## tiles, etc.) could be useful.

# # ## ### ##### ######## ############# #####################
## Semi-compress bands/rows/columns/tiles/image down to a statistic (histogram).

operator op::image::histogram {
    section transform statistics

    example {
	aktive op sdf 2image smooth [aktive op sdf ring [aktive image sdf triangle width 128 height 128 a {10 10} b {50 80} c {80 30}] thickness 4]
	@1 bins 8 | -matrix -int
    }

    int? 256 bins \
	The number of bins in the returned histogram. The pixel values are quantized \
	to fit. Only values in the range of \[0..1\] are considered valid. Values \
	outside of that range are placed into the smallest/largest bin. \
	\
	The default quantizes the image values to 8-bit.

    input

    note Returns image with the input transformed into a histogram of `bins` values.

    # Deprecated: Note: it is computed as the column sum of the row histograms of the input
    #
    # Note: it is computed as the transposed row sum of the column histograms of the input
    #
    #       in the current setup, where we concurrently scan over rows
    #       the column sum of the original implementation returns a single row,
    #       causing threading to be effectively disabled.
    #
    #       while the new form computes column histograms their vector cache ensures
    #       that each is calculated only once, and the row sums are concurrent.
    #
    # TODO FUTURE - ops declare prefered access pattern, sinks choose
    # TODO FUTURE - sink auto-chooses different patterns based on image geometry
    #               i.e. thin tall -> by rows, thin wide -> by columns, else ops preference

    body {
	if 0 {
	    set src [aktive op row histogram $src bins $bins]
	    set src [aktive op column sum $src]
	} else {
	    set src [aktive op column histogram $src bins $bins]
	    set src [aktive op row sum $src]
	    set src [aktive op transpose $src]
	}
	return $src
    }
}

operator {dim unchanged} {
    op::band::histogram   band {width and height}
    op::tile::histogram   {}   {}
} {
    op -> _ kind _
    section transform statistics

    if {$kind eq "tile"} {
	uint radius Tile size as radius from center. \
	    Full width and height of the tile are `2*radius+1`.
    }

    int? 256 bins \
	The number of bins held by a single histogram. The pixel values are quantized \
	to fit. Only values in the range of \[0..1\] are considered valid. Values \
	outside of that range are placed into the smallest/largest bin. \
	\
	The default quantizes the image values to 8-bit.

    input

    note Returns image with input ${kind}s transformed into a histogram of `bins` values.

    switch -exact -- $kind {
	band   {
	    note The result is an image of bin-sized histogram ${dim}s with {*}$unchanged of the input
	}
	tile   {
	    note Only single-band images are legal inputs. The result will have `bins` bands.

	    note Beware, the operator consumes overlapping tiles, not just adjacent.

	    note Beware, the result image is shrunken by 2*radius in width and height \
		relative to the input. Inputs smaller than that are rejected.

	    note If shrinkage is not desired add a border to the input using one of \
		the `aktive op embed ...` operators before applying this operator.

	    note The prefered embedding for histogram is black. \
		It is chosen to have minimal to no impact on the statistics \
		at the original input's borders.
	}
    }

    def fieldsetup [dict get {
	band   { state->size = domain->depth;  domain->depth  = param->bins; }
	tile   {
	    if (domain->depth > 1) {
		aktive_failf ("rejecting input with depth %d != 1", domain->depth);
	    }

	    aktive_uint len = 2*param->radius+1;
	    if ((domain->width  < len) ||
		(domain->height < len)) {
		aktive_failf ("Image %dx%d too small for %d-radius tiles",
			      domain->width, domain->height, param->radius);
	    }

	    state->size = len*len;

	    domain->x      += param->radius;
	    domain->y      += param->radius;
	    domain->width  -= 2*param->radius;
	    domain->height -= 2*param->radius;
	    domain->depth   = param->bins;
	}
    } $kind]

    state -fields {
	aktive_uint size; // quick access to the original size of the reduced @@kind@@s
    } -setup {
	aktive_geometry_copy (domain, aktive_image_get_geometry (srcs->v[0]));
	@@fieldsetup@@
    }

    switch -exact -- $kind {
	band {
	    blit histogrammer {
		{AH {y AY 1 up} {y 0 1 up}}
		{AW {x AX 1 up} {x 0 1 up}}
	    } {raw histogram-band {
		// dstvalue = row/col start - 1-strided band (full histogram)
		// srcvalue = row/col start - 1-strided full band vector
		aktive_reduce_histogram (srcvalue, SD, 1, &state->h);
		memcpy (dstvalue, state->h.count, DD * sizeof(double));
	    }}
	}
	tile {
	    # This assumes a single band input
	    blit histogrammer {
		{AH {y AY 1 up} {y radius 1 up}}
		{AW {x AX 1 up} {x radius 1 up}}
	    } {raw histogram-tile {
		// dstvalue = cells to set (bands = histogram)
		// srcvalue = center cell of tile
		aktive_tile_reduce_histogram (srcvalue, radius, srcpos, SRCCAP, srcpitch, srcstride, &state->h);
		memcpy (dstvalue, state->h.count, DD * sizeof(double));
	    }}
	}
    }

    def subrequest [dict get {
	band   {}
	tile   {
	    aktive_uint radius = param->radius;
	    subrequest.x      -= radius;
	    subrequest.y      -= radius;
	    subrequest.width  += 2 * radius;
	    subrequest.height += 2 * radius;
	}
    } $kind]

    pixels -state {
	aktive_histogram h;
    } -setup {
	state->h.bins   = param->bins;
	state->h.maxbin = param->bins - 1;
	state->h.count  = NALLOC (double, param->bins);
    } -cleanup {
	ckfree (state->h.count);
    } {
	TRACE("histogram actual %u bins", param->bins);
	aktive_rectangle_def_as (subrequest, request);
	@@subrequest@@

	TRACE_RECTANGLE_M("@@kind@@ hist", &subrequest);
	aktive_block* src = aktive_region_fetch_area (srcs->v[0], &subrequest);

	@@histogrammer@@

	TRACE_DO (__aktive_block_dump ("@@kind@@ histogram out", block));
    }
}

# # ## ### ##### ######## ############# #####################

operator oaxis {
    op::row::histogram    height
    op::column::histogram width
} {
    op -> _ kind _

    example {
	aktive op sdf 2image smooth [aktive op sdf ring [aktive image sdf triangle width 32 height 32 a {10 10} b {30 80} c {80 30}] thickness 4]
	@1 bins 8 | -matrix -int
    }

    section transform statistics

    int? 256 bins \
	The number of bins held by a single histogram. The pixel values are quantized \
	to fit. Only values in the range of \[0..1\] are considered valid. Values \
	outside of that range are placed into the smallest/largest bin. \
	\
	The default quantizes the image values to 8-bit.

    note Returns image with input ${kind}s transformed into a histogram of `bins` values.

    note The result is an image of `bins`-sized histogram ${kind}s with $oaxis and depth of the input.

    # !xref-mark histogram
    cached $kind histogram AKTIVE_HISTOGRAM_FILL -fields {
	aktive_histogram h;
    } -setup {
	state->h.bins    = param->bins;
	state->h.maxbin  = param->bins - 1;
	state->h.count   = NALLOC (double, param->bins);

	TRACE("histogram actual %u bins", param->bins);
    } -cleanup {
	ckfree (state->h.count);
    } -rsize "param->bins" -cdata "&state->h"
    # !xref-mark /end
}

##
# # ## ### ##### ######## ############# #####################
::return