PO 2010/11/28
Test suite for the poPhotoUtil package for image processing with pure Tk commands.
Screenshots from the test suite:
package require Tk package require poPhotoUtil package require Img proc bmpFirst {} { return { #define first_width 16 #define first_height 16 static unsigned char first_bits[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1c, 0x04, 0x1c, 0x06, 0x1c, 0x07, 0x9c, 0x3f, 0xdc, 0x3f, 0x9c, 0x3f, 0x1c, 0x07, 0x1c, 0x06, 0x1c, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; } } proc bmpLast {} { return { #define last_width 16 #define last_height 16 static unsigned char last_bits[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x38, 0x60, 0x38, 0xe0, 0x38, 0xfc, 0x39, 0xfc, 0x3b, 0xfc, 0x39, 0xe0, 0x38, 0x60, 0x38, 0x20, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; } } proc bmpLeft {} { return { #define left_width 16 #define left_height 16 static unsigned char left_bits[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x80, 0x01, 0xc0, 0x01, 0xe0, 0x0f, 0xf0, 0x0f, 0xe0, 0x0f, 0xc0, 0x01, 0x80, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; } } proc bmpRight {} { return { #define right_width 16 #define right_height 16 static unsigned char right_bits[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x80, 0x01, 0x80, 0x03, 0xf0, 0x07, 0xf0, 0x0f, 0xf0, 0x07, 0x80, 0x03, 0x80, 0x01, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; } } proc bmpPlay {} { return { #define play_width 16 #define play_height 16 static unsigned char play_bits[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0x00, 0xe0, 0x00, 0xe0, 0x01, 0xe0, 0x03, 0xe0, 0x07, 0xe0, 0x03, 0xe0, 0x01, 0xe0, 0x00, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; } } proc bmpHalt {} { return { #define halt_width 16 #define halt_height 16 static unsigned char halt_bits[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x30, 0x0c, 0x60, 0x06, 0xc0, 0x03, 0x80, 0x01, 0xc0, 0x03, 0x60, 0x06, 0x30, 0x0c, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; } } proc ui_initToolhelp { w { bgColor yellow } { fgColor black } } { global ui_helpWidget # Create Toolbar help window with a simple label in it. if { [winfo exists $w] } { destroy $w } toplevel $w set ui_helpWidget $w label $w.l -text "??" -bg $bgColor -fg $fgColor -relief ridge pack $w.l wm overrideredirect $w true if {[string equal [tk windowingsystem] aqua]} { ::tk::unsupported::MacWindowStyle style $w help none } wm geometry $w [format "+%d+%d" -100 -100] } proc ui_showToolhelp { x y str } { global ui_helpWidget $ui_helpWidget.l configure -text $str raise $ui_helpWidget wm geometry $ui_helpWidget [format "+%d+%d" $x [expr $y +10]] } proc ui_hideToolhelp {} { global ui_helpWidget wm geometry $ui_helpWidget [format "+%d+%d" -100 -100] } proc ui_button { btnName bmpData cmd helpStr } { set imgData [image create bitmap -data $bmpData] eval button $btnName -image $imgData -command [list $cmd] -relief flat bind $btnName <Enter> "ui_showToolhelp %X %Y [list $helpStr]" bind $btnName <Leave> { ui_hideToolhelp } bind $btnName <Button> { ui_hideToolhelp } } proc ui_init {title {winPos "+0+0"} } { global ui_curImgNo ui_noImgs ui_top catch {wm withdraw .} set ui_top .testWindow ui_initToolhelp .testToolhelp toplevel $ui_top wm title $ui_top $title wm geometry $ui_top $winPos frame $ui_top.imgfr -bg lightgrey frame $ui_top.menufr -relief raised -bg lightgrey label $ui_top.imgfr.img -bg white text $ui_top.imgfr.txt -height 2 -width 60 -state disabled pack $ui_top.imgfr.txt -side top -expand 1 -fill x -expand 1 -fill x pack $ui_top.imgfr.img -side top ui_button $ui_top.menufr.quit [bmpHalt] ui_exit "Quit test (Esc)" pack $ui_top.menufr.quit -in $ui_top.menufr -side left pack $ui_top.menufr $ui_top.imgfr -side top -pady 2 -anchor w bind $ui_top <Key-Escape> ui_exit wm protocol $ui_top WM_DELETE_WINDOW ui_exit P "Visual: [winfo screenvisual $ui_top]" P "Depth: [winfo depth $ui_top]" set ui_curImgNo 0 set ui_noImgs 0 } proc ui_addphoto { phImg str } { global ui_curImgNo ui_noImgs ui_strings ui_images ui_photos global gTime set ui_strings($ui_curImgNo) "$str ($gTime(end) secs)" set ui_images($ui_curImgNo) "none" set ui_photos($ui_curImgNo) $phImg showimg $ui_curImgNo incr ui_curImgNo set ui_noImgs $ui_curImgNo } proc showimg { imgNo } { global ui_strings ui_top ui_photos $ui_top.imgfr.img config -image $ui_photos($imgNo) $ui_top.imgfr.txt configure -state normal $ui_top.imgfr.txt delete 1.0 end $ui_top.imgfr.txt insert end $ui_strings($imgNo) $ui_top.imgfr.txt configure -state disabled update } proc show_first {} { global ui_curImgNo ui_noImgs set ui_curImgNo 0 showimg $ui_curImgNo } proc show_last {} { global ui_curImgNo ui_noImgs set ui_curImgNo [expr ($ui_noImgs -1)] showimg $ui_curImgNo } proc show_play {} { global ui_curImgNo ui_noImgs while { $ui_curImgNo < [expr ($ui_noImgs -1)] } { incr ui_curImgNo showimg $ui_curImgNo } } proc show_prev {} { global ui_curImgNo if { $ui_curImgNo > 0 } { incr ui_curImgNo -1 showimg $ui_curImgNo } } proc show_next {} { global ui_curImgNo ui_noImgs if { $ui_curImgNo < [expr ($ui_noImgs -1)] } { incr ui_curImgNo 1 showimg $ui_curImgNo } } proc ui_show {} { global ui_curImgNo ui_noImgs ui_strings ui_top PrintMachineInfo set ui_noImgs $ui_curImgNo incr ui_curImgNo -1 if { $ui_noImgs > 0 } { set fr $ui_top.menufr ui_button $fr.first [bmpFirst] show_first "Show first image" ui_button $fr.prev [bmpLeft] show_prev "Show previous image (<-)" ui_button $fr.next [bmpRight] show_next "Show next image (->)" ui_button $fr.last [bmpLast] show_last "Show last image" ui_button $fr.play [bmpPlay] show_play "Play image sequence (p)" pack $fr.first $fr.prev $fr.next $fr.last \ -in $fr -side left -padx 0 pack $fr.play -in $fr -side left -padx 0 bind $ui_top <Key-Right> show_next bind $ui_top <Key-Left> show_prev bind $ui_top <Key-p> show_play } } proc ui_delete {} { global ui_noImgs ui_strings ui_images ui_photos ui_top for { set i 0 } { $i < $ui_noImgs } { incr i } { image delete $ui_photos($i) if { [info commands $ui_images($i)] != {} } { deleteimg $ui_images($i) } set ui_strings($i) {} } destroy $ui_top.imgfr destroy $ui_top.menufr } proc ui_exit {} { ui_delete if { [info commands memcheck] != {} } { memcheck } exit } proc P { str } { catch {puts $str ; flush stdout} } proc PH { str } { P "" P "Test: $str" PS } proc PS { } { P "" P "------------------------------------------------------------" P "" } proc PSec { msg sec } { P [format "%s: %.4f seconds" $msg $sec] } proc PrintMachineInfo {} { global tcl_platform P "Machine specific information:" P "platform : $tcl_platform(platform)" P "os : $tcl_platform(os)" P "osVersion : $tcl_platform(osVersion)" P "machine : $tcl_platform(machine)" P "byteOrder : $tcl_platform(byteOrder)" P "wordSize : $tcl_platform(wordSize)" P "user : $tcl_platform(user)" P "hostname : [info hostname]" P "Tcl version : [info patchlevel]" P "Visuals : [winfo visualsavailable .]" } proc ClockStart {} { global gTime set gTime(start) [clock milliseconds] } proc ClockLookup {} { global gTime set gTime(end) [expr ([clock milliseconds] - $gTime(start)) / 1000.0] return $gTime(end) } if { $argc == 0 } { puts "Usage: $argv0 TestImageFile" exit 1 } PH "Image processing with pure Tk" set testProc 1 set testBlur 1 set testReduce 1 set testStats 1 ui_init "PhotoUtilTest" set phImg [image create photo -file [lindex $argv 0]] set w [image width $phImg] set h [image height $phImg] set msg "Original image (Size: $w x $h)" P $msg if { $testProc } { set msg "Creating white photo image" P $msg ClockStart set phColorWhite [::poPhotoUtil::ColorImg 200 150] PSec "Required time" [ClockLookup] ui_addphoto $phColorWhite $msg set msg "Creating red photo image" P $msg ClockStart set phColorRed [::poPhotoUtil::ColorImg 120 180 255 0 0] PSec "Required time" [ClockLookup] ui_addphoto $phColorRed $msg set msg "Flipping horizontally" P $msg ClockStart set phFlipHori [::poPhotoUtil::FlipHorizontal $phImg] PSec "Required time" [ClockLookup] ui_addphoto [::poPhotoUtil::Compose 2 $phImg $phFlipHori] $msg set msg "Flipping vertically" P $msg ClockStart set phFlipVert [::poPhotoUtil::FlipVertical $phImg] PSec "Required time" [ClockLookup] ui_addphoto [::poPhotoUtil::Compose 2 $phImg $phFlipVert] $msg set msg "Rotating by 90 degrees" P $msg ClockStart set phRot90 [::poPhotoUtil::Rotate $phImg 90] PSec "Required time" [ClockLookup] ui_addphoto [::poPhotoUtil::Compose 2 $phImg $phRot90] $msg set msg "Rotating by 180 degrees" P $msg ClockStart set phRot180 [::poPhotoUtil::Rotate $phImg 180] PSec "Required time" [ClockLookup] ui_addphoto [::poPhotoUtil::Compose 2 $phImg $phRot180] $msg set msg "Rotating by 270 degrees" P $msg ClockStart set phRot270 [::poPhotoUtil::Rotate $phImg 270] PSec "Required time" [ClockLookup] ui_addphoto [::poPhotoUtil::Compose 2 $phImg $phRot270] $msg set msg "Rotating by -90 degrees" P $msg ClockStart set phRotMin90 [::poPhotoUtil::Rotate $phImg -90] PSec "Required time" [ClockLookup] ui_addphoto [::poPhotoUtil::Compose 2 $phImg $phRotMin90] $msg set nw [expr int($w * 0.75)] set nh [expr int($h * 0.75)] set msg "Resizing image to ($nw x $nh)" P $msg ClockStart set phResizeDown [::poPhotoUtil::Resize $phImg $nw $nh] PSec "Required time" [ClockLookup] ui_addphoto [::poPhotoUtil::Compose 2 $phImg $phResizeDown] $msg set nw [expr int($w * 1.5)] set nh [expr int($h * 1.5)] set msg "Resizing image to ($nw x $nh)" P $msg ClockStart set phResizeUp [::poPhotoUtil::Resize $phImg $nw $nh] PSec "Required time" [ClockLookup] ui_addphoto [::poPhotoUtil::Compose 2 $phImg $phResizeUp] $msg set msg "Tiling by 3x2" P $msg ClockStart set phTile3x2 [::poPhotoUtil::Tile $phImg 3 2] PSec "Required time" [ClockLookup] ui_addphoto $phTile3x2 $msg set msg "Tiling by 3x2 (Mirroring in x)" P $msg ClockStart set phTile3x2Mirror [::poPhotoUtil::Tile $phImg 3 2 true false] PSec "Required time" [ClockLookup] ui_addphoto $phTile3x2Mirror $msg } if { $testBlur } { set msg "Brightening image" P $msg ClockStart set phHSVBright [::poPhotoUtil::HSV $phImg 1.4] PSec "Required time" [ClockLookup] ui_addphoto [::poPhotoUtil::Compose 2 $phImg $phHSVBright] $msg set msg "Darkening image" P $msg ClockStart set phHSVDark [::poPhotoUtil::HSV $phImg 0.6] PSec "Required time" [ClockLookup] ui_addphoto [::poPhotoUtil::Compose 2 $phImg $phHSVDark] $msg set msg "Saturating image" P $msg ClockStart set phHSVSat [::poPhotoUtil::HSV $phImg 1.0 1.7] PSec "Required time" [ClockLookup] ui_addphoto [::poPhotoUtil::Compose 2 $phImg $phHSVSat] $msg set msg "Blurring image" P $msg ClockStart set phBlur [::poPhotoUtil::Blur $phImg 0.8] PSec "Required time" [ClockLookup] ui_addphoto [::poPhotoUtil::Compose 2 $phImg $phBlur] $msg } if { $testReduce } { set msg "Reducing color depth to 8-bit" P $msg ClockStart set phReduce8 [::poPhotoUtil::Reduce $phImg 8] PSec "Required time" [ClockLookup] ui_addphoto [::poPhotoUtil::Compose 2 $phImg $phReduce8] $msg set msg "Reducing color depth to 6-bit" P $msg ClockStart set phReduce6 [::poPhotoUtil::Reduce $phImg 6] PSec "Required time" [ClockLookup] ui_addphoto [::poPhotoUtil::Compose 2 $phImg $phReduce6] $msg set msg "Reducing color depth to 4-bit" P $msg ClockStart set phReduce4 [::poPhotoUtil::Reduce $phImg 4] PSec "Required time" [ClockLookup] ui_addphoto [::poPhotoUtil::Compose 2 $phImg $phReduce4] $msg set msg "Difference image (original vs. 4-bit)" P $msg ClockStart set phDiff [::poPhotoUtil::Difference $phImg $phReduce4] PSec "Required time" [ClockLookup] ui_addphoto [::poPhotoUtil::Compose 3 $phImg $phReduce4 $phDiff] $msg } proc PrintImgStats { statDict } { set minStr [format "(%d, %d, %d)" \ [dict get $statDict min red ] \ [dict get $statDict min green] \ [dict get $statDict min blue ]] set maxStr [format "(%d, %d, %d)" \ [dict get $statDict max red ] \ [dict get $statDict max green] \ [dict get $statDict max blue ]] set medStr [format "(%.3f, %.3f, %.3f)" \ [dict get $statDict mean red ] \ [dict get $statDict mean green] \ [dict get $statDict mean blue ]] set stdStr [format "(%.3f, %.3f, %.3f)" \ [dict get $statDict std red ] \ [dict get $statDict std green] \ [dict get $statDict std blue ]] P "Num pixels : [dict get $statDict num]" P "Minimum values: $minStr" P "Maximum values: $maxStr" P "Mean values: $medStr" P "StdDev values: $stdStr" } proc PrintImgHisto { histoDict } { for { set i 0 } { $i < 256 } { incr i } { P [format "%3d: %3d %3d %3d" $i \ [lindex [dict get $histoDict red] $i] \ [lindex [dict get $histoDict green] $i] \ [lindex [dict get $histoDict blue] $i]] } } if { $testStats } { set msg "Calculating image histogram" P $msg ClockStart set histoDict [::poPhotoUtil::Histogram $phImg] PSec "Required time" [ClockLookup] PrintImgHisto $histoDict set histoHeight 150 set msg "Putting logarithmic histogram into image" P $msg ClockStart set logDict [::poPhotoUtil::ScaleHistogram $histoDict $histoHeight true] set phHistoLogRed [::poPhotoUtil::DrawHistogram $logDict $histoHeight "red" ] set phHistoLogGreen [::poPhotoUtil::DrawHistogram $logDict $histoHeight "green"] set phHistoLogBlue [::poPhotoUtil::DrawHistogram $logDict $histoHeight "blue" ] PSec "Required time" [ClockLookup] set phHistoLogImg [::poPhotoUtil::Compose 4 $phImg \ $phHistoLogRed $phHistoLogGreen $phHistoLogBlue] ui_addphoto $phHistoLogImg $msg set msg "Putting linear histogram into image" P $msg ClockStart set linDict [::poPhotoUtil::ScaleHistogram $histoDict $histoHeight false] set phHistoLinRed [::poPhotoUtil::DrawHistogram $linDict $histoHeight "red" ] set phHistoLinGreen [::poPhotoUtil::DrawHistogram $linDict $histoHeight "green"] set phHistoLinBlue [::poPhotoUtil::DrawHistogram $linDict $histoHeight "blue" ] PSec "Required time" [ClockLookup] set phHistoLinImg [::poPhotoUtil::Compose 4 $phImg \ $phHistoLinRed $phHistoLinGreen $phHistoLinBlue] ui_addphoto $phHistoLinImg $msg set msg "Calculating image characteristics (whole image)" P $msg ClockStart set statDict [::poPhotoUtil::GetImgStats $phImg true] PrintImgStats $statDict PSec "Required time" [ClockLookup] set msg "Calculating image characteristics (part of image)" P $msg ClockStart set statDict [::poPhotoUtil::GetImgStats $phImg true \ 50 70 50 80] PrintImgStats $statDict PSec "Required time" [ClockLookup] } PS P "End of test" ui_show