165 lines
4.4 KiB
Tcl
165 lines
4.4 KiB
Tcl
package require Tcl 8.5
|
|
namespace path {::tcl::mathfunc ::tcl::mathop}
|
|
|
|
# Utility to extract the number of dimensions of a matrix
|
|
proc rank m {
|
|
for {set rank 0} {[llength $m] > 1} {incr rank} {
|
|
set m [lindex $m 0]
|
|
}
|
|
return $rank
|
|
}
|
|
|
|
# Utility to get the size of a matrix, as a list of lengths
|
|
proc size m {
|
|
set r [rank $m]
|
|
set index {}
|
|
set size {}
|
|
for {set i 0} {$i<$r} {incr i} {
|
|
lappend size [llength [lindex $m $index]]
|
|
lappend index 0
|
|
}
|
|
return $size
|
|
}
|
|
|
|
# Utility that iterates over the space of coordinates within a matrix.
|
|
#
|
|
# Arguments:
|
|
# var The name of the variable (in the caller's context) to set to each
|
|
# coordinate.
|
|
# size The size of matrix whose coordinates are to be iterated over.
|
|
# body The script to evaluate (in the caller's context) for each coordinate,
|
|
# with the variable named by 'var' set to the coordinate for the particular
|
|
# iteration.
|
|
proc loopcoords {var size body} {
|
|
upvar 1 $var v
|
|
set count [* {*}$size]
|
|
for {set i 0} {$i < $count} {incr i} {
|
|
set coords {}
|
|
set j $i
|
|
for {set s $size} {[llength $s]} {set s [lrange $s 0 end-1]} {
|
|
set dimension [lindex $s end]
|
|
lappend coords [expr {$j % $dimension}]
|
|
set j [expr {$j / $dimension}]
|
|
}
|
|
set v [lreverse $coords]
|
|
uplevel 1 $body
|
|
}
|
|
}
|
|
|
|
# Assembles a row, which is one of the simultaneous equations that needs
|
|
# to be solved by reducing the whole set to reduced row echelon form. Note
|
|
# that each row describes the equation for a single cell of the 'g' function.
|
|
#
|
|
# Arguments:
|
|
# g The "result" matrix of the convolution being undone.
|
|
# h The known "input" matrix of the convolution being undone.
|
|
# gs The size descriptor of 'g', passed as argument for efficiency.
|
|
# gc The coordinate in 'g' that we are generating the equation for.
|
|
# fs The size descriptor of 'f', passed as argument for efficiency.
|
|
# hs The size descriptor of 'h' (the unknown "input" matrix), passed
|
|
# as argument for efficiency.
|
|
proc row {g f gs gc fs hs} {
|
|
loopcoords hc $hs {
|
|
set fc {}
|
|
set ok 1
|
|
foreach a $gc b $fs c $hc {
|
|
set d [expr {$a - $c}]
|
|
if {$d < 0 || $d >= $b} {
|
|
set ok 0
|
|
break
|
|
}
|
|
lappend fc $d
|
|
}
|
|
if {$ok} {
|
|
lappend row [lindex $f $fc]
|
|
} else {
|
|
lappend row 0
|
|
}
|
|
}
|
|
return [lappend row [lindex $g $gc]]
|
|
}
|
|
|
|
# Utility for converting a matrix to Reduced Row Echelon Form
|
|
# From http://rosettacode.org/wiki/Reduced_row_echelon_form#Tcl
|
|
proc toRREF {m} {
|
|
set lead 0
|
|
set rows [llength $m]
|
|
set cols [llength [lindex $m 0]]
|
|
for {set r 0} {$r < $rows} {incr r} {
|
|
if {$cols <= $lead} {
|
|
break
|
|
}
|
|
set i $r
|
|
while {[lindex $m $i $lead] == 0} {
|
|
incr i
|
|
if {$rows == $i} {
|
|
set i $r
|
|
incr lead
|
|
if {$cols == $lead} {
|
|
# Tcl can't break out of nested loops
|
|
return $m
|
|
}
|
|
}
|
|
}
|
|
# swap rows i and r
|
|
foreach j [list $i $r] row [list [lindex $m $r] [lindex $m $i]] {
|
|
lset m $j $row
|
|
}
|
|
# divide row r by m(r,lead)
|
|
set val [lindex $m $r $lead]
|
|
for {set j 0} {$j < $cols} {incr j} {
|
|
lset m $r $j [/ [double [lindex $m $r $j]] $val]
|
|
}
|
|
|
|
for {set i 0} {$i < $rows} {incr i} {
|
|
if {$i != $r} {
|
|
# subtract m(i,lead) multiplied by row r from row i
|
|
set val [lindex $m $i $lead]
|
|
for {set j 0} {$j < $cols} {incr j} {
|
|
lset m $i $j \
|
|
[- [lindex $m $i $j] [* $val [lindex $m $r $j]]]
|
|
}
|
|
}
|
|
}
|
|
incr lead
|
|
}
|
|
return $m
|
|
}
|
|
|
|
# Deconvolve a pair of matrixes. Solves for 'h' such that 'g = f convolve h'.
|
|
#
|
|
# Arguments:
|
|
# g The matrix of data to be deconvolved.
|
|
# f The matrix describing the convolution to be removed.
|
|
# type Optional description of the type of data expected. Defaults to 32-bit
|
|
# integer data; use 'double' for floating-point data.
|
|
proc deconvolve {g f {type int}} {
|
|
# Compute the sizes of the various matrixes involved.
|
|
set gsize [size $g]
|
|
set fsize [size $f]
|
|
foreach gs $gsize fs $fsize {
|
|
lappend hsize [expr {$gs - $fs + 1}]
|
|
}
|
|
|
|
# Prepare the set of simultaneous equations to solve
|
|
set toSolve {}
|
|
loopcoords coords $gsize {
|
|
lappend toSolve [row $g $f $gsize $coords $fsize $hsize]
|
|
}
|
|
|
|
# Solve the equations
|
|
set solved [toRREF $toSolve]
|
|
|
|
# Make a dummy result matrix of the right size
|
|
set h 0
|
|
foreach hs [lreverse $hsize] {set h [lrepeat $hs $h]}
|
|
|
|
# Fill the results from the equations into the result matrix
|
|
set idx 0
|
|
loopcoords coords $hsize {
|
|
lset h $coords [$type [lindex $solved $idx end]]
|
|
incr idx
|
|
}
|
|
|
|
return $h
|
|
}
|