├── .gitignore ├── .project ├── .smalltalk.ston ├── .travis.yml ├── README.md ├── examples ├── binary.png ├── dilation.png ├── erosion.png ├── icon.png ├── pharo.png ├── pharo_sobel.png ├── pwq7S.jpg ├── pwq7S_crop.jpg ├── pwq7S_darker.jpg ├── pwq7S_flipHorizontally.jpg ├── pwq7S_flipVertically.jpg ├── pwq7S_gaussian.png ├── pwq7S_gray.jpg ├── pwq7S_lighter.jpg ├── pwq7S_negated.png ├── pwq7S_rotated45.png ├── pwq7S_rotatedLeft.jpg ├── pwq7S_scaled.jpg ├── pwq7S_sub.png ├── pwq7S_sum.png └── show.jpg └── src ├── .properties ├── BaselineOfImageForm ├── BaselineOfImageForm.class.st └── package.st ├── Image-Form-core ├── ImageForm.class.st ├── ImageFormGrayScale.class.st └── package.st └── Image-Form-test ├── ImageFormGrayScaleTest.class.st ├── ImageFormTest.class.st └── package.st /.gitignore: -------------------------------------------------------------------------------- 1 | # changes file 2 | *.changes 3 | *.chg 4 | 5 | # system image 6 | *.image 7 | *.img7 8 | *.img 9 | 10 | # Pharo Smalltalk Debug log file 11 | PharoDebug.log 12 | 13 | # Squeak Smalltalk Debug log file 14 | SqueakDebug.log 15 | 16 | # Dolphin Smalltalk source file 17 | *.sml 18 | 19 | # Dolphin Smalltalk error file 20 | *.errors 21 | 22 | # Monticello package cache 23 | /package-cache 24 | 25 | # playground cache 26 | /play-cache 27 | /play-stash 28 | 29 | # Metacello-github cache 30 | /github-cache 31 | github-*.zip 32 | -------------------------------------------------------------------------------- /.project: -------------------------------------------------------------------------------- 1 | { 2 | 'srcDirectory' : 'src' 3 | } -------------------------------------------------------------------------------- /.smalltalk.ston: -------------------------------------------------------------------------------- 1 | SmalltalkCISpec { 2 | #loading : [ 3 | SCIMetacelloLoadSpec { 4 | #baseline : 'ImageForm', 5 | #directory: 'src', 6 | #platforms : [#pharo] 7 | } 8 | ] 9 | } 10 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: smalltalk 2 | sudo: false 3 | 4 | # Select operating system(s) 5 | notifications: 6 | email: 7 | on_succes: never 8 | on_failures: always 9 | 10 | os: 11 | - linux 12 | - osx 13 | 14 | # Select compatible Smalltalk image(s) 15 | smalltalk: 16 | - Pharo64-8.0 17 | # ... 18 | - Pharo32-8.0 19 | # ... 20 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![Build Status](https://travis-ci.org/pablo1n7/ImageForm.svg?branch=master)](https://travis-ci.org/pablo1n7/ImageForm) 2 | 3 | # ImageForm 4 | 5 | Small Lib for basic Image processing. Programmed in Pharo for Pharo :) 6 | 7 | # Install 8 | 9 | ```smalltalk 10 | Metacello new 11 | baseline: #ImageForm; 12 | repository: 'github://pablo1n7/ImageForm/src'; 13 | load. 14 | ``` 15 | 16 | # Basic Operations 17 | 18 | ## Open an Image. 19 | 20 | ```smalltalk 21 | anImage := ImageForm open: '/Users/sdas/Documents/pwq7S.jpg'. 22 | ``` 23 |

24 | 25 |

26 | 27 | ## Show Image. 28 | ```smalltalk 29 | anImage show: 'Eileen Collins'. 30 | ``` 31 | ![](https://raw.githubusercontent.com/pablo1n7/ImageForm/master/examples/show.jpg) 32 | 33 | ## Save Image. 34 | ```smalltalk 35 | aScaledImage save:'/Users/pablo/Documents/Pharo/pwq7S_scaled.jpg'. 36 | aScaledImage save:'/Users/pablo/Documents/Pharo/pwq7S_scaled.png'. 37 | ``` 38 | 39 | # Color Operations 40 | 41 | ## Transform to Gray Scale. 42 | ```smalltalk 43 | aGrayImage := anImage asGrayScale 44 | ``` 45 | or 46 | 47 | ```smalltalk 48 | aGrayImage := ImageFormGrayScale open: '/Users/sdas/Documents/pwq7S.jpg'. 49 | ``` 50 |

51 | 52 |

53 | 54 | ## Lighter Image. 55 | ```smalltalk 56 | aLighterImage := anImageForm lighter:0.25. 57 | ``` 58 |

59 | 60 |

61 | 62 | ## Darker Image. 63 | ```smalltalk 64 | aDarkerImage := anImageForm darker:0.25. 65 | ``` 66 |

67 | 68 |

69 | 70 | ## Negated Image. 71 | ```smalltalk 72 | aNegatedImage := anImage negated . 73 | ``` 74 |

75 | 76 |

77 | 78 | # Image Transformations. 79 | 80 | ## Flip Image. 81 | ```smalltalk 82 | aFlippedImage := anImage flipHorizontally. 83 | ``` 84 | 85 |

86 | 87 |

88 | 89 | ```smalltalk 90 | aFlippedImage := anImage flipVertically. 91 | ``` 92 |

93 | 94 |

95 | 96 | ## Rotate Image. 97 | ```smalltalk 98 | aRotatedImage := anImage rotateBy: 45. 99 | ``` 100 |

101 | 102 |

103 | 104 | ```smalltalk 105 | aRotatedImage := anImage rotateBy: #left centerAt: 0@0. 106 | ``` 107 |

108 | 109 |

110 | 111 | ## Scale Image. 112 | ```smalltalk 113 | aScaledImage := anImageA scaled: 100 height: 100. 114 | ``` 115 |

116 | 117 |

118 | 119 | ## Crop Image. 120 | ```smalltalk 121 | aCroppedImage := anImageForm crop: 0@0 h: 300 w: 500. 122 | ``` 123 |

124 | 125 |

126 | 127 | 128 | # Basic Arithmetics. 129 | ```smalltalk 130 | anImageA := ImageForm open: '/Users/sdas/Documents/pwq7S.jpg'. 131 | anImageB := ImageForm open: '/Users/sdas/Documents/pharo.png'. 132 | 133 | aSubResult := anImageB - anImageA. 134 | ``` 135 |

136 | 137 |

138 | 139 | ```smalltalk 140 | aSumResult := anImageB + anImageA. 141 | ``` 142 |

143 | 144 |

145 | 146 | # Advanced Operations 147 | ## Operations with kernels (3x3 and 5x5) 148 | 149 | ```smalltalk 150 | anImage := ImageForm open: '/Users/pablo/Documents/pharo/pharo.png'. 151 | aGaussianKernel := {{ 1/256. 4/256. 6/256. 4/256. 1/256. }. 152 | { 4/256. 16/256. 24/256. 16/256. 4/256.}. 153 | { 6/256. 24/256. 36/256. 24/256. 6/256. }. 154 | { 4/256. 16/256. 24/256. 16/256. 4/256. }. 155 | { 1/256. 4/256. 6/256. 4/256. 1/256. }.}. 156 | aResult := anImage applyKernel: aGaussianKernel flattened . 157 | ``` 158 |

159 | 160 |

161 | 162 | 163 | ```smalltalk 164 | anImage := ImageForm open: '/Users/pablo/Documents/pharo/pharo.png'. 165 | aGrayImage := anImage asGrayScale. 166 | aSobelKernel := {{-0.1. -0.1. -0.1}. 167 | { -0.1. 0.80. -0.1}. 168 | {-0.1. -0.1. -0.1}}. 169 | aResult := aGrayImage applyKernel: aSobelKernel flattened . 170 | ``` 171 |

172 | 173 |

174 | 175 | # Gray Scale Operations 176 | 177 | ```smalltalk 178 | anImage := ImageForm open: '/Users/pablo/Documents/pharo/icon.png'. 179 | aGrayImage := anImage asGrayScale. 180 | ``` 181 | 182 | ## Transform to Binary Image. 183 | 184 | ```smalltalk 185 | aBinaryImage := aGrayImage asBinaryImage: 0.1. 186 | ``` 187 |

188 | 189 |

190 | 191 | 192 | ## Erosion. 193 | 194 | ```smalltalk 195 | anErosionImage := aBinaryImage erosion: ImageFormGrayScale squareKernel3x3 iterations: 6. 196 | ``` 197 | 198 |

199 | 200 |

201 | 202 | ## Dilation. 203 | 204 | ```smalltalk 205 | anDilationImage := aBinaryImage dilation: ImageFormGrayScale squareKernel3x3 iterations: 6. 206 | ``` 207 | 208 |

209 | 210 |

211 | 212 | 213 | 214 | -------------------------------------------------------------------------------- /examples/binary.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pablo1n7/ImageForm/3a6ef066a6de156a5d81b8ecd721f1fd2645f08b/examples/binary.png -------------------------------------------------------------------------------- /examples/dilation.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pablo1n7/ImageForm/3a6ef066a6de156a5d81b8ecd721f1fd2645f08b/examples/dilation.png -------------------------------------------------------------------------------- /examples/erosion.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pablo1n7/ImageForm/3a6ef066a6de156a5d81b8ecd721f1fd2645f08b/examples/erosion.png -------------------------------------------------------------------------------- /examples/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pablo1n7/ImageForm/3a6ef066a6de156a5d81b8ecd721f1fd2645f08b/examples/icon.png -------------------------------------------------------------------------------- /examples/pharo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pablo1n7/ImageForm/3a6ef066a6de156a5d81b8ecd721f1fd2645f08b/examples/pharo.png -------------------------------------------------------------------------------- /examples/pharo_sobel.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pablo1n7/ImageForm/3a6ef066a6de156a5d81b8ecd721f1fd2645f08b/examples/pharo_sobel.png -------------------------------------------------------------------------------- /examples/pwq7S.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pablo1n7/ImageForm/3a6ef066a6de156a5d81b8ecd721f1fd2645f08b/examples/pwq7S.jpg -------------------------------------------------------------------------------- /examples/pwq7S_crop.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pablo1n7/ImageForm/3a6ef066a6de156a5d81b8ecd721f1fd2645f08b/examples/pwq7S_crop.jpg -------------------------------------------------------------------------------- /examples/pwq7S_darker.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pablo1n7/ImageForm/3a6ef066a6de156a5d81b8ecd721f1fd2645f08b/examples/pwq7S_darker.jpg -------------------------------------------------------------------------------- /examples/pwq7S_flipHorizontally.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pablo1n7/ImageForm/3a6ef066a6de156a5d81b8ecd721f1fd2645f08b/examples/pwq7S_flipHorizontally.jpg -------------------------------------------------------------------------------- /examples/pwq7S_flipVertically.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pablo1n7/ImageForm/3a6ef066a6de156a5d81b8ecd721f1fd2645f08b/examples/pwq7S_flipVertically.jpg -------------------------------------------------------------------------------- /examples/pwq7S_gaussian.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pablo1n7/ImageForm/3a6ef066a6de156a5d81b8ecd721f1fd2645f08b/examples/pwq7S_gaussian.png -------------------------------------------------------------------------------- /examples/pwq7S_gray.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pablo1n7/ImageForm/3a6ef066a6de156a5d81b8ecd721f1fd2645f08b/examples/pwq7S_gray.jpg -------------------------------------------------------------------------------- /examples/pwq7S_lighter.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pablo1n7/ImageForm/3a6ef066a6de156a5d81b8ecd721f1fd2645f08b/examples/pwq7S_lighter.jpg -------------------------------------------------------------------------------- /examples/pwq7S_negated.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pablo1n7/ImageForm/3a6ef066a6de156a5d81b8ecd721f1fd2645f08b/examples/pwq7S_negated.png -------------------------------------------------------------------------------- /examples/pwq7S_rotated45.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pablo1n7/ImageForm/3a6ef066a6de156a5d81b8ecd721f1fd2645f08b/examples/pwq7S_rotated45.png -------------------------------------------------------------------------------- /examples/pwq7S_rotatedLeft.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pablo1n7/ImageForm/3a6ef066a6de156a5d81b8ecd721f1fd2645f08b/examples/pwq7S_rotatedLeft.jpg -------------------------------------------------------------------------------- /examples/pwq7S_scaled.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pablo1n7/ImageForm/3a6ef066a6de156a5d81b8ecd721f1fd2645f08b/examples/pwq7S_scaled.jpg -------------------------------------------------------------------------------- /examples/pwq7S_sub.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pablo1n7/ImageForm/3a6ef066a6de156a5d81b8ecd721f1fd2645f08b/examples/pwq7S_sub.png -------------------------------------------------------------------------------- /examples/pwq7S_sum.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pablo1n7/ImageForm/3a6ef066a6de156a5d81b8ecd721f1fd2645f08b/examples/pwq7S_sum.png -------------------------------------------------------------------------------- /examples/show.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pablo1n7/ImageForm/3a6ef066a6de156a5d81b8ecd721f1fd2645f08b/examples/show.jpg -------------------------------------------------------------------------------- /src/.properties: -------------------------------------------------------------------------------- 1 | { 2 | #format : #tonel 3 | } -------------------------------------------------------------------------------- /src/BaselineOfImageForm/BaselineOfImageForm.class.st: -------------------------------------------------------------------------------- 1 | Class { 2 | #name : #BaselineOfImageForm, 3 | #superclass : #BaselineOf, 4 | #category : #BaselineOfImageForm 5 | } 6 | 7 | { #category : #baselines } 8 | BaselineOfImageForm >> baseline: spec [ 9 | 10 | spec 11 | for:#common 12 | do:[ 13 | "Packages" 14 | spec 15 | package: 'Image-Form-core'; 16 | package: 'Image-Form-test'. 17 | 18 | ] 19 | ] 20 | -------------------------------------------------------------------------------- /src/BaselineOfImageForm/package.st: -------------------------------------------------------------------------------- 1 | Package { #name : #BaselineOfImageForm } 2 | -------------------------------------------------------------------------------- /src/Image-Form-core/ImageForm.class.st: -------------------------------------------------------------------------------- 1 | " 2 | Represent an image in Pharo. 3 | 4 | This lib allows basic manipulate and show images. 5 | 6 | 7 | Example for open a image: 8 | 9 | anImageA := ImageForm open: '/Users/pablo/Documents/Pharo/pwq7S.jpg'. 10 | 11 | Example to transform a GrayScale image. 12 | 13 | aGrayScaleImageA := anImageA asGrayScale. 14 | 15 | 16 | Example for sum two images: 17 | 18 | anImageB := ImageForm open: '/Users/pablo/Documents/Pharo/pharo.png'. 19 | aResultSum := anImageB - anImageA. 20 | 21 | Example to show a image: 22 | anImageA show: 'Image A'. 23 | 24 | Example to scaled: 25 | 26 | aSacaledImage := anImageA scaledFactor:500 height:100. 27 | 28 | Example to flip: 29 | 30 | aFlippedImage := anImageA flipHorizontally. 31 | 32 | aFlippedImage := anImageA flipVertically. 33 | 34 | 35 | " 36 | Class { 37 | #name : #ImageForm, 38 | #superclass : #Form, 39 | #category : #'Image-Form-core' 40 | } 41 | 42 | { #category : #'instance creation' } 43 | ImageForm class >> imageTest [ 44 | "Return a Image test." 45 | | anImage aForm | 46 | 47 | aForm := Form extent: 100@100 depth:32. 48 | anImage := self newFrom: aForm. 49 | anImage fillColor: Color red. 50 | 51 | 0 to: 50 do:[:x| 0 to: 50 52 | do:[:y| |aPoint| 53 | aPoint := Point x: x y: y. 54 | anImage colorAt: aPoint put: Color blue ]]. 55 | 56 | 50 to: 100 do:[:x| 50 to: 100 57 | do:[:y| |aPoint| 58 | aPoint := Point x: x y: y. 59 | anImage colorAt: aPoint put: Color yellow]]. 60 | 61 | 0 to: 50 do:[:x| 50 to: 100 62 | do:[:y| |aPoint| 63 | aPoint := Point x: x y: y. 64 | anImage colorAt: aPoint put: Color green]]. 65 | ^ anImage 66 | ] 67 | 68 | { #category : #'instance creation' } 69 | ImageForm class >> newFrom: aForm [ 70 | "New ImageForm from an object Form." 71 | 72 | | anImage | 73 | anImage := self new. 74 | anImage copyFrom: aForm. 75 | ^ anImage 76 | ] 77 | 78 | { #category : #'instance creation' } 79 | ImageForm class >> newImageColor: aColor width: aWidth height: aHeight [ 80 | "Return a new ImageForm filled with aColor. The size is aWidth x aHeight." 81 | 82 | | anImage aForm | 83 | anImage := self new. 84 | aForm := Form extent: aWidth @ aHeight depth:32. 85 | aForm fillColor: aColor. 86 | anImage copyFrom: aForm. 87 | ^ anImage 88 | ] 89 | 90 | { #category : #'instance creation' } 91 | ImageForm class >> open: aFileName [ 92 | "Open an Image." 93 | 94 | | anImage aForm | 95 | aFileName asFileReference exists ifTrue: [ 96 | aForm := ImageReadWriter formFromFileNamed: aFileName. 97 | anImage := self newFrom: aForm. 98 | ^ anImage 99 | ]. 100 | self error: 'File does not exist'. 101 | ] 102 | 103 | { #category : #operators } 104 | ImageForm >> + anImage [ 105 | "Sum pixel to pixel two images, and return new ImageForm. The channels that be bigger than 1 is setter in 1. It affects the alpha channel." 106 | 107 | |anImageResult| 108 | 109 | anImageResult := self deepCopy. 110 | 0 to: self width -1 do: [:x| 111 | 0 112 | to: self height -1 113 | do: [ :y | 114 | | aPoint aColorA aColorB | 115 | aPoint := Point x: x y: y. 116 | aColorA := anImageResult colorAt: aPoint. 117 | aColorB := anImage colorAt: aPoint. 118 | anImageResult colorAt: aPoint put: aColorA + aColorB ]]. 119 | ^ anImageResult 120 | 121 | ] 122 | 123 | { #category : #operators } 124 | ImageForm >> - anImage [ 125 | "Subtraction pixel to pixel two images, and return new ImageForm. The channels that be lower than 0 is setter in 0.. It affects the alpha channel." 126 | 127 | |anImageResult| 128 | 129 | anImageResult := self deepCopy. 130 | 0 to: self width -1 do:[:x| 0 to: self height -1 131 | do:[:y| 132 | |aPoint aColorA aColorB aColorC| 133 | aPoint := (Point x: x y: y). 134 | aColorA := anImageResult colorAt: aPoint. 135 | aColorB := anImage colorAt: aPoint. 136 | aColorC := aColorA - aColorB. 137 | anImageResult colorAt: aPoint put: aColorC. ]]. 138 | ^ anImageResult 139 | 140 | ] 141 | 142 | { #category : #accesing } 143 | ImageForm >> accumulateColor: aChannel [ 144 | "Accumulate values for one channel in 0 to 255, returns in percentage" 145 | 146 | | anAcumulate aBlues| 147 | anAcumulate := OrderedCollection new. 148 | aBlues := (aChannel * 255) rounded. 149 | anAcumulate := (0 to: 255) collect:[:each|(aBlues select:[:number|number=each]) size]. 150 | ^anAcumulate / (self height * self width) 151 | ] 152 | 153 | { #category : #'image manipulation' } 154 | ImageForm >> applyKernel: aKernel [ 155 | "Apply convolution to image. AKernel is a flattened list. Return a new ImageForm. 156 | 157 | anKernel := {{-0.1. -0.1. -0.1}. { -0.1. 0.80. -0.1}. {-0.1. -0.1. -0.1}}. 158 | anImageResult := anImage applyKernel: anKernel flattened . 159 | " 160 | 161 | |anImage aDim| 162 | aDim := aKernel size sqrt. 163 | (aDim = 3 or: aDim = 5 ) ifFalse:[self error: 'only supported 3x3 or 5x5 kernel size']. 164 | anImage := self deepCopy. 165 | 0 to: self width do:[:x| 166 | 0 to: self height do:[:y| 167 | |aPoint aColorA| 168 | aPoint := Point x: x y: y. 169 | aColorA := self getNeighborhood: 170 | aPoint kernel: aKernel. 171 | anImage colorAt: aPoint put: aColorA]]. 172 | 173 | ^anImage. 174 | ] 175 | 176 | { #category : #converting } 177 | ImageForm >> applyMask: aMask [ 178 | "Apply a Binary Mask in one image. The Result is a new Image, with color transparent in the position with black color in the mask." 179 | 180 | | anImageResult | 181 | "({Color black. Color white. } = aMask colorsUsed) 182 | ifFalse:[self error: 'the mask only has Black and White color.']." 183 | anImageResult := self deepCopy. 184 | 0 to: self width do: [:x| 185 | 0 186 | to: self height 187 | do: [ :y | 188 | | aPoint aColorA aColorB | 189 | aPoint := Point x: x y: y. 190 | aColorA := anImageResult colorAt: aPoint. 191 | aColorB := aMask colorAt: aPoint. 192 | aColorB = Color black 193 | ifTrue: [ aColorA := Color transparent ]. 194 | 195 | anImageResult colorAt: aPoint put: aColorA ]]. 196 | ^ anImageResult 197 | ] 198 | 199 | { #category : #converting } 200 | ImageForm >> asGrayScale [ 201 | "Convert Colors to Gray Colors. Return a new ImageForm. the alpha channel is set in 1." 202 | 203 | | anImageForm| 204 | 205 | anImageForm := self collectColors:[:each | 206 | |aIntensity| 207 | aIntensity := (0.2125 * each red) + (0.7154 * each green) + (0.0721 * each blue). 208 | Color r:aIntensity g: aIntensity b: aIntensity alpha: 1.0. ]. 209 | ^ ImageFormGrayScale newFrom: anImageForm. 210 | 211 | ] 212 | 213 | { #category : #converting } 214 | ImageForm >> asGrayScaleWithAlpha [ 215 | "Convert Colors to Gray Colors. Return a new ImageFormGrayScale. Keep the alpha channel" 216 | 217 | | anImageForm| 218 | 219 | anImageForm := self collectColors:[:each | 220 | |aIntensity| 221 | aIntensity := (0.2125 * each red) + (0.7154 * each green) + (0.0721 * each blue). 222 | Color r:aIntensity g: aIntensity b: aIntensity alpha: each alpha. ]. 223 | 224 | ^ImageFormGrayScale newFrom: anImageForm. 225 | 226 | ] 227 | 228 | { #category : #accesing } 229 | ImageForm >> blues [ 230 | "returns all values for channel blue in RGB." 231 | ^ self colors collect: [ :each | each blue.]. 232 | ] 233 | 234 | { #category : #'image manipulation' } 235 | ImageForm >> colors [ 236 | "Create a new copy of the receiver with all the colors transformed by aBlock" 237 | |aColors| 238 | aColors := OrderedCollection new. 239 | 0 to: self width -1 do: [:x| 240 | 0 241 | to: self height -1 242 | do: [ :y | 243 | | aPoint aColor| 244 | aPoint := Point x: x y: y. 245 | aColor:= self colorAt: aPoint. 246 | aColors add: aColor.]]. 247 | ^ aColors 248 | ] 249 | 250 | { #category : #'image manipulation' } 251 | ImageForm >> crop: aPoint h: aH w: aW [ 252 | "Crop an image begin for aPoint. degrees the image. Return a new ImageForm. 253 | aCropImage := anImage crop: 0@0 h:200 w: 400. " 254 | 255 | | aForm aFormImage| 256 | 257 | aForm := Form extent: aW@aH depth:32. 258 | aFormImage := self deepCopy. 259 | aPoint x to: (aPoint x + aW) do:[:x| aPoint y to: (aPoint y + aH) 260 | do:[:y| 261 | |aCoord aNewPoint| 262 | aCoord := (Point x: x y: y). 263 | aNewPoint := (aCoord x - aPoint x)@(aCoord y - aPoint y). 264 | aForm colorAt: aNewPoint put: (aFormImage colorAt: aCoord). ]]. 265 | ^ self class newFrom: aForm. 266 | ] 267 | 268 | { #category : #'image manipulation' } 269 | ImageForm >> darker: aFactor [ 270 | "Darker an image. aFactor is a float from 0 to 1. Return a new ImageForm." 271 | 272 | | aForm aFormImage | 273 | aForm:= super darker:aFactor. 274 | aFormImage := self class newFrom: aForm. 275 | 276 | ^ aFormImage 277 | ] 278 | 279 | { #category : #'image manipulation' } 280 | ImageForm >> flipHorizontally [ 281 | "Flip Horizontally. Return a new ImageForm." 282 | 283 | | aForm aFormImage| 284 | 285 | aForm:= super flipBy: #horizontal centerAt: (0@0). 286 | aFormImage := self class newFrom: aForm. 287 | 288 | ^ aFormImage 289 | ] 290 | 291 | { #category : #'image manipulation' } 292 | ImageForm >> flipVertically [ 293 | "Flip Vertically. Return a new ImageForm." 294 | 295 | | aForm aFormImage| 296 | 297 | aForm:= super flipBy: #vertical centerAt: (0@0). 298 | aFormImage := self class newFrom: aForm. 299 | 300 | ^ aFormImage 301 | ] 302 | 303 | { #category : #accessing } 304 | ImageForm >> getNeighborhood: aPoint kernel: aKernel [ 305 | "Apply convolution to a point. AKernel is a flattened list. Return a new Color. 306 | 307 | aPixelColor := anImage getNeighborhood: 0@0 kernel:{0. 0. 0. 0. 1. 0. 0. 0. 0.}. 308 | 309 | " 310 | 311 | 312 | |aPoints aRed aGreen aBlue aKernelR aDim| 313 | aDim := aKernel size sqrt. 314 | aPoints := self getPointNeighborhood: aPoint ksize: aDim@aDim. 315 | aRed := 0. 316 | aGreen := 0. 317 | aBlue := 0. 318 | aKernelR := aKernel. 319 | aPoints collectWithIndex: [:aKPoint :inx| 320 | |aKColor aKFactor| 321 | aKColor := (self colorAt: aKPoint). 322 | aKFactor := (aKernelR at:inx). 323 | aRed := aRed + (aKColor red * aKFactor). 324 | aGreen := aGreen + (aKColor green * aKFactor). 325 | aBlue := aBlue + (aKColor blue * aKFactor). 326 | ]. 327 | ^ Color r: aRed g: aGreen b: aBlue. 328 | ] 329 | 330 | { #category : #accessing } 331 | ImageForm >> getPointNeighborhood: aPoint ksize: aKsize [ 332 | "Get a Neighbordhood of a point, aKsize is a shape like 3@3. Return a list of points. If the points are out of the image, it repeats the image border. 333 | 334 | anImage getPointNeighborhood: 0@0 ksize: 3@3. 335 | 336 | " 337 | 338 | |aPoints aXvalues aYvalues factor| 339 | 340 | factor := (aKsize x -1 ) / 2. 341 | aPoints := OrderedCollection new. 342 | aXvalues := (aPoint x - factor) to: (aPoint x + factor). 343 | aYvalues := (aPoint y - factor) to: (aPoint y + factor). 344 | 345 | aXvalues do: [ :x | 346 | aYvalues do: [ :y | 347 | |xK yK| 348 | xK := x. 349 | yK := y. 350 | x < 0 ifTrue: [xK := 0]. "this need refactoring" 351 | y < 0 ifTrue: [yK := 0]. 352 | x > (self width-1) ifTrue: [xK := self width-1]. 353 | y > (self height-1) ifTrue: [yK := self height-1]. 354 | 355 | aPoints add: xK@yK. 356 | ]. 357 | ]. 358 | ^aPoints 359 | ] 360 | 361 | { #category : #accesing } 362 | ImageForm >> greens [ 363 | "returns all values for channel green in RGB." 364 | ^ self colors collect: [ :each | each green.]. 365 | ] 366 | 367 | { #category : #'image manipulation' } 368 | ImageForm >> lighter: aFactor [ 369 | "Lighter an image. aFactor is a float from 0 to 1. Return a new ImageForm." 370 | 371 | | aForm aFormImage| 372 | 373 | aForm:= super lighter:aFactor. 374 | aFormImage := self class newFrom: aForm. 375 | 376 | ^ aFormImage 377 | ] 378 | 379 | { #category : #converting } 380 | ImageForm >> negated [ 381 | "Convert an image to negated image form. Return a new ImageForm. Keep the alpha channel" 382 | 383 | | anImageForm| 384 | 385 | anImageForm := self collectColors:[:each | each negated]. 386 | 387 | ^self class newFrom: anImageForm. 388 | 389 | ] 390 | 391 | { #category : #accesing } 392 | ImageForm >> reds [ 393 | "returns all values for channel red in RGB." 394 | ^ self colors collect: [ :each | each red.]. 395 | ] 396 | 397 | { #category : #'image manipulation' } 398 | ImageForm >> rotateBy: aDeg [ 399 | "rotate aDeg degrees the image. Return a new ImageForm." 400 | 401 | | aForm aFormImage | 402 | aFormImage := self deepCopy. 403 | aForm:= super rotateBy: aDeg. 404 | aFormImage := self class newFrom: aForm. 405 | ^ aFormImage 406 | ] 407 | 408 | { #category : #'image manipulation' } 409 | ImageForm >> rotateBy: direction centerAt: aPoint [ 410 | "Rotate in a direction with the center in point. Return a new ImageForm. Possible values for 411 | direction = #left, #right or #pi" 412 | 413 | | aForm aFormImage | 414 | aFormImage := self deepCopy. 415 | aForm:= super rotateBy: direction centerAt: aPoint. 416 | aFormImage := self class newFrom: aForm. 417 | 418 | ^ aFormImage 419 | ] 420 | 421 | { #category : #'file in/out' } 422 | ImageForm >> save: aName [ 423 | "Save in disk a image. Supported extensions: .png; .jpg; .bmp; and .jpeg" 424 | 425 | ('*.png' match: aName) 426 | ifTrue: [ self writePNGFileNamed: aName. ^self ]. 427 | 428 | (('*.jpg' match: aName) | ('*.jpeg' match: aName)) 429 | ifTrue: [ self writeJPEGfileNamed: aName. ^self ]. 430 | 431 | ('*.bmp' match: aName) 432 | ifTrue: [ self writeBMPfileNamed: aName. ^self ]. 433 | 434 | self error:'Only support extension: .png; .jpg; .bmp; and .jpeg'. 435 | 436 | 437 | ] 438 | 439 | { #category : #'image manipulation' } 440 | ImageForm >> scaled: aWidth height: aHeight [ 441 | "Scaled one image. This method try to Keep relation of aspect. Return a new ImageForm." 442 | 443 | ^ self class newFrom: (super scaledToSize: (Point x: aWidth y: aHeight)) 444 | ] 445 | 446 | { #category : #'image manipulation' } 447 | ImageForm >> scaledIntoFormOfSize: aSize [ 448 | "Scaled one image in aSize. Return a new ImageForm width shape aSizeXaSize." 449 | 450 | ^ self class newFrom: (super scaledIntoFormOfSize: aSize) 451 | ] 452 | 453 | { #category : #accessing } 454 | ImageForm >> shape [ 455 | "return a string with format: widthXheight" 456 | 457 | ^ self width printString, 'X', self height printString 458 | ] 459 | 460 | { #category : #showing } 461 | ImageForm >> show [ 462 | "show one image in the world" 463 | 464 | self show:'Image' 465 | ] 466 | 467 | { #category : #showing } 468 | ImageForm >> show: anTitle [ 469 | "show one image in the world with a title. Always sacaled the image in 500x500" 470 | 471 | | im | 472 | im := (ImageMorph withForm: (self scaledIntoFormOfSize:500) ). 473 | im withSnapshotBorder. 474 | im borderWidth: 5. 475 | im color: (Color gray). 476 | im openInWindowLabeled: (anTitle, '| ', self shape). 477 | ] 478 | -------------------------------------------------------------------------------- /src/Image-Form-core/ImageFormGrayScale.class.st: -------------------------------------------------------------------------------- 1 | " 2 | Represent a Gray Scale image in Pharo. 3 | 4 | This lib allows basic manipulate and show images. 5 | 6 | 7 | Example for open a image: 8 | 9 | anImageA := ImageFormGrayScale open: '/Users/pablo/Documents/Pharo/pwq7S.jpg'. 10 | 11 | Example for sum two images: 12 | 13 | anImageB := ImageForm open: '/Users/pablo/Documents/Pharo/pharo.png'. 14 | aResultSum := anImageB - anImageA. 15 | 16 | Example to show a image: 17 | anImageA show: 'Image A'. 18 | 19 | Example to scaled: 20 | 21 | aSacaledImage := anImageA scaledFactor:500 height:100. 22 | 23 | Example to flip: 24 | 25 | aFlippedImage := anImageA flipHorizontally. 26 | 27 | aFlippedImage := anImageA flipVertically. 28 | 29 | 30 | " 31 | Class { 32 | #name : #ImageFormGrayScale, 33 | #superclass : #ImageForm, 34 | #category : #'Image-Form-core' 35 | } 36 | 37 | { #category : #constants } 38 | ImageFormGrayScale class >> discKernel3x3 [ 39 | "return a Disc Kernel" 40 | ^ #(#(1 0 1) #(1 1 1) #(1 0 1)) 41 | ] 42 | 43 | { #category : #constants } 44 | ImageFormGrayScale class >> horizontalKernel3x3 [ 45 | "return a Horizontal Kernel" 46 | ^ #(#(1 0 1) #(1 0 1) #(1 0 1)) 47 | ] 48 | 49 | { #category : #'instance creation' } 50 | ImageFormGrayScale class >> open: aFileName [ 51 | "Open an Image in Gray Scale." 52 | ^ (superclass open: aFileName) asGrayScale 53 | ] 54 | 55 | { #category : #constants } 56 | ImageFormGrayScale class >> squareKernel3x3 [ 57 | "return a Square Kernel" 58 | ^ #(#(1 1 1) #(1 1 1) #(1 1 1)) 59 | ] 60 | 61 | { #category : #constants } 62 | ImageFormGrayScale class >> verticalKernel3x3 [ 63 | "return a Vertical Kernel" 64 | ^ #(#(1 1 1) #(0 0 0) #(1 1 1)) 65 | ] 66 | 67 | { #category : #converting } 68 | ImageFormGrayScale >> applyKernel: aKernel with: anOperation [ 69 | "Apply convolution to image. AKernel is a flattened list. Return a new ImageFormGrayScale. 70 | 71 | anKernel := {{-0.1. -0.1. -0.1}. { -0.1. 0.80. -0.1}. {-0.1. -0.1. -0.1}}. 72 | anImageResult := anImage applyKernel: anKernel flattened . 73 | " 74 | 75 | |anImage aDim| 76 | aDim := aKernel size sqrt. 77 | (aDim = 3 or: aDim = 5 ) ifFalse:[self error: 'only supported 3x3 or 5x5 kernel size']. 78 | anImage := self deepCopy. 79 | 0 to: self width do:[:x| 80 | 0 to: self height do:[:y| 81 | |aPoint aColorA| 82 | aPoint := Point x: x y: y. 83 | aColorA := self 84 | getNeighborhood:aPoint 85 | kernel: aKernel 86 | with: anOperation . 87 | anImage colorAt: aPoint put: aColorA]]. 88 | 89 | ^anImage. 90 | ] 91 | 92 | { #category : #converting } 93 | ImageFormGrayScale >> asBinaryImage: aThreshold [ 94 | "Covert an image in Binary (Black and White). Return a new ImageFormGray. Keep the alpha channel" 95 | 96 | | anImageForm | 97 | anImageForm := self collectColors:[:each | 98 | |anIntensity| 99 | anIntensity := 0. 100 | each red > aThreshold ifTrue:[ anIntensity := 1]. 101 | Color r: anIntensity g: anIntensity b: anIntensity alpha: each alpha. ]. 102 | 103 | ^self class newFrom: anImageForm. 104 | 105 | ] 106 | 107 | { #category : #converting } 108 | ImageFormGrayScale >> asGrayScale [ 109 | "Convert Colors to Gray Colors. Return a new ImageForm. the alpha channel is set in 1." 110 | self error: 'Is in GrayScale' 111 | ] 112 | 113 | { #category : #converting } 114 | ImageFormGrayScale >> asGrayScaleWithAlpha [ 115 | "Convert Colors to Gray Colors. Return a new ImageForm. the alpha channel is set in 1." 116 | self error: 'Is in GrayScale' 117 | ] 118 | 119 | { #category : #converting } 120 | ImageFormGrayScale >> closing: aKernel [ 121 | "Closing is reverse of Opening, Dilation followed by Erosion. AKernel is a flattened list. Return a new ImageFormGrayScale." 122 | 123 | | dilation | 124 | dilation := self dilation: aKernel flattened. 125 | ^ dilation erosion: aKernel flattened. 126 | ] 127 | 128 | { #category : #converting } 129 | ImageFormGrayScale >> dilation: aKernel [ 130 | "Increases the white region in the image or size of foreground object increases. AKernel is a flattened list. Return a new ImageFormGrayScale." 131 | ^ self applyKernel: aKernel flattened with: #max. 132 | 133 | ] 134 | 135 | { #category : #converting } 136 | ImageFormGrayScale >> dilation: aKernel iterations: aNum [ 137 | "Increases the white region in the image or size of foreground object increases. AKernel is a flattened list. Return a new ImageFormGrayScale." 138 | 139 | | anImage | 140 | anImage := self dilation: aKernel. 141 | 1 to: aNum -1 do:[ :each| anImage := anImage dilation: aKernel. ]. 142 | ^anImage. 143 | 144 | 145 | ] 146 | 147 | { #category : #converting } 148 | ImageFormGrayScale >> erosion: aKernel [ 149 | "Erodes away the boundaries of foreground object. AKernel is a flattened list. Return a new ImageFormGrayScale." 150 | ^ self applyKernel: aKernel flattened with: #min. 151 | 152 | ] 153 | 154 | { #category : #converting } 155 | ImageFormGrayScale >> erosion: aKernel iterations: aNum [ 156 | "Erodes away the boundaries of foreground object. AKernel is a flattened list. Return a new ImageFormGrayScale." 157 | 158 | | anImage | 159 | anImage := self erosion: aKernel. 160 | 1 to: aNum -1 do:[ :each| anImage := anImage erosion: aKernel. ]. 161 | ^anImage. 162 | 163 | ] 164 | 165 | { #category : #converting } 166 | ImageFormGrayScale >> getNeighborhood: aPoint kernel: aKernel with: aOperation [ 167 | "Apply convolution to a point with an operation. AKernel is a flattened list. Return a new Color. 168 | 169 | aPixelColor := anImage getNeighborhood: 0@0 kernel:{0. 0. 0. 0. 1. 0. 0. 0. 0.} with: #min. 170 | 171 | " 172 | | aPoints anIntensity aKernelR aDim anIntensities | 173 | aDim := aKernel size sqrt. 174 | aPoints := self getPointNeighborhood: aPoint ksize: aDim@aDim. 175 | anIntensities := OrderedCollection new. 176 | aKernelR := aKernel. 177 | aPoints collectWithIndex: [:aKPoint :inx| 178 | |aKColor aKFactor| 179 | aKColor := (self colorAt: aKPoint). 180 | aKFactor := (aKernelR at:inx). 181 | aKFactor == 1 182 | ifTrue:[anIntensities add: (aKColor red)]. 183 | ]. 184 | anIntensity := anIntensities perform: aOperation. 185 | ^ Color 186 | r: anIntensity 187 | g: anIntensity 188 | b: anIntensity 189 | ] 190 | 191 | { #category : #converting } 192 | ImageFormGrayScale >> opening: aKernel [ 193 | "Opening is just another name of erosion followed by dilation." 194 | 195 | |erosion| 196 | erosion := self erosion: aKernel flattened. 197 | ^ erosion dilation: aKernel. 198 | ] 199 | -------------------------------------------------------------------------------- /src/Image-Form-core/package.st: -------------------------------------------------------------------------------- 1 | Package { #name : #'Image-Form-core' } 2 | -------------------------------------------------------------------------------- /src/Image-Form-test/ImageFormGrayScaleTest.class.st: -------------------------------------------------------------------------------- 1 | " 2 | An ImageFormGrayScaleTest is a test class for testing the behavior of ImageFormGrayScale 3 | " 4 | Class { 5 | #name : #ImageFormGrayScaleTest, 6 | #superclass : #ImageFormTest, 7 | #category : #'Image-Form-test' 8 | } 9 | 10 | { #category : #test } 11 | ImageFormGrayScaleTest >> imageFormGrayScaleClass [ 12 | ^ ImageFormGrayScale 13 | ] 14 | 15 | { #category : #test } 16 | ImageFormGrayScaleTest >> testApplyKernelWith [ 17 | "Apply convolution to a image using a Identity Kernel. The image should not be changed" 18 | 19 | | anImage anImageKernel | 20 | anImage := self imageFormGrayScaleClass 21 | newFrom: (self formClass extent: 50 @ 50 depth: 32). 22 | anImage fillColor: Color black. 23 | anImage colorAt: 25@25 put: Color white. 24 | anImageKernel := anImage applyKernel: ImageFormGrayScale squareKernel3x3 flattened with: #min. 25 | self 26 | assert: (anImageKernel colorAt: 25@25) 27 | equals: Color black. 28 | anImageKernel := anImage applyKernel: ImageFormGrayScale squareKernel3x3 flattened with: #max. 29 | self 30 | assert: (anImageKernel colorAt: 25@25) 31 | equals: Color white. 32 | ] 33 | 34 | { #category : #test } 35 | ImageFormGrayScaleTest >> testAsBinaryImage [ 36 | "Covert an image in Binary (Black and White). The color in position 0,0 should be changed" 37 | 38 | | aForm aGrayImage aImage | 39 | aForm := self formClass extent: 10 @ 10 depth: 32. 40 | aImage := self imageFormGrayScaleClass newFrom: aForm. 41 | aImage fillColor: (Color gray). 42 | aGrayImage := aImage asBinaryImage: 0.3. 43 | self 44 | assert: (aGrayImage colorAt: 0 @ 0) 45 | equals: Color white 46 | ] 47 | 48 | { #category : #test } 49 | ImageFormGrayScaleTest >> testAsGrayScale [ 50 | 51 | | aForm aGrayImage | 52 | aForm := self formClass extent: 10 @ 10 depth: 32. 53 | aGrayImage := self imageFormGrayScaleClass newFrom: aForm. 54 | aGrayImage fillColor: (Color gray). 55 | self should:[ aGrayImage asGrayScale ] raise: Error 56 | ] 57 | 58 | { #category : #test } 59 | ImageFormGrayScaleTest >> testAsGrayScaleWithAlpha [ 60 | 61 | | aForm aGrayImage | 62 | aForm := self formClass extent: 10 @ 10 depth: 32. 63 | aGrayImage := self imageFormGrayScaleClass newFrom: aForm. 64 | aGrayImage fillColor: (Color gray). 65 | self should:[ aGrayImage asGrayScaleWithAlpha ] raise: Error 66 | ] 67 | 68 | { #category : #test } 69 | ImageFormGrayScaleTest >> testClosing [ 70 | 71 | | aForm aGrayImage aClosing | 72 | aForm := self formClass extent: 12 @ 12 depth: 32. 73 | aGrayImage := self imageFormGrayScaleClass newFrom: aForm. 74 | aGrayImage fillColor: (Color black). 75 | aGrayImage colorAt: 6@6 put: Color white. 76 | aClosing:= aGrayImage closing: ImageFormGrayScale squareKernel3x3. 77 | self 78 | assert: (aClosing colorAt: 6@6) 79 | equals: Color white 80 | ] 81 | 82 | { #category : #test } 83 | ImageFormGrayScaleTest >> testDilation [ 84 | 85 | | aForm aGrayImage aDilation | 86 | aForm := self formClass extent: 12 @ 12 depth: 32. 87 | aGrayImage := self imageFormGrayScaleClass newFrom: aForm. 88 | aGrayImage fillColor: (Color white). 89 | aGrayImage colorAt: 6@6 put: Color black. 90 | aDilation := aGrayImage dilation: ImageFormGrayScale squareKernel3x3. 91 | self 92 | assert: (aDilation colorAt: 6@6) 93 | equals: Color white 94 | ] 95 | 96 | { #category : #test } 97 | ImageFormGrayScaleTest >> testErosion [ 98 | 99 | | aForm aGrayImage aErosion | 100 | aForm := self formClass extent: 12 @ 12 depth: 32. 101 | aGrayImage := self imageFormGrayScaleClass newFrom: aForm. 102 | aGrayImage fillColor: (Color black). 103 | aGrayImage colorAt: 6@6 put: Color white. 104 | aErosion := aGrayImage dilation: ImageFormGrayScale squareKernel3x3. 105 | self 106 | assert: (aErosion colorAt: 5@5) 107 | equals: Color white 108 | ] 109 | 110 | { #category : #test } 111 | ImageFormGrayScaleTest >> testGetNeighborhoodKernelWith [ 112 | 113 | | aForm aGrayImage aColor | 114 | aForm := self formClass extent: 12 @ 12 depth: 32. 115 | aGrayImage := self imageFormGrayScaleClass newFrom: aForm. 116 | aGrayImage fillColor: (Color white). 117 | aGrayImage colorAt: 6@6 put: Color black. 118 | 119 | aColor := aGrayImage getNeighborhood: 6@6 kernel: {0. 0. 0. 1. 1. 1. 0. 0. 0.} with: #min. 120 | self 121 | assert: aColor 122 | equals: Color black. 123 | 124 | aColor := aGrayImage getNeighborhood: 6@6 kernel: {0. 0. 0. 1. 1. 1. 0. 0. 0.} with: #max. 125 | self 126 | assert: aColor 127 | equals: Color white. 128 | ] 129 | 130 | { #category : #test } 131 | ImageFormGrayScaleTest >> testOpen [ 132 | "Save an image in the imageDirectory and Open it. The image should be the same. Before exit remove the image." 133 | 134 | | aImage aPath aImageSave | 135 | aPath := FileLocator imageDirectory fullPath pathString 136 | , '/test_image.png'. 137 | aImage := self imageFormClass imageTest. 138 | aImage save: aPath. 139 | aImageSave := self imageFormGrayScaleClass open: aPath. 140 | self 141 | assert: (aImageSave colorAt: 5@5) 142 | equals: (Color r: 0.07 g: 0.07 b: 0.07) . 143 | ] 144 | 145 | { #category : #test } 146 | ImageFormGrayScaleTest >> testOpening [ 147 | 148 | | aForm aGrayImage aOpening | 149 | aForm := self formClass extent: 12 @ 12 depth: 32. 150 | aGrayImage := self imageFormGrayScaleClass newFrom: aForm. 151 | aGrayImage fillColor: (Color black). 152 | aGrayImage colorAt: 6@6 put: Color white. 153 | aOpening := aGrayImage opening: ImageFormGrayScale squareKernel3x3. 154 | self 155 | assert: (aOpening colorAt: 5@5) 156 | equals: Color black 157 | ] 158 | -------------------------------------------------------------------------------- /src/Image-Form-test/ImageFormTest.class.st: -------------------------------------------------------------------------------- 1 | " 2 | An ImageFormTest is a test class for testing the behavior of ImageForm 3 | " 4 | Class { 5 | #name : #ImageFormTest, 6 | #superclass : #TestCase, 7 | #category : #'Image-Form-test' 8 | } 9 | 10 | { #category : #test } 11 | ImageFormTest >> formClass [ 12 | ^ Form 13 | ] 14 | 15 | { #category : #test } 16 | ImageFormTest >> imageFormClass [ 17 | ^ ImageForm 18 | ] 19 | 20 | { #category : #test } 21 | ImageFormTest >> testAccumulateColor [ 22 | "" 23 | 24 | | anImage acumulate | 25 | anImage := self imageFormClass newImageColor: Color blue width: 10 height: 10. 26 | acumulate := anImage accumulateColor: anImage blues. 27 | self 28 | assert: (acumulate at:256) 29 | equals: 1. 30 | self 31 | assert: (acumulate at:1) 32 | equals: 0. 33 | ] 34 | 35 | { #category : #test } 36 | ImageFormTest >> testApplyKernel [ 37 | "Apply convolution to a image using a Identity Kernel. The image should not be changed" 38 | 39 | | anImage anImageKernel anIdentityKernel | 40 | anImage := self imageFormClass 41 | newFrom: (self formClass extent: 50 @ 50 depth: 32). 42 | anImage fillColor: Color red. 43 | anIdentityKernel := {{0 . 0 . 0}. 44 | {0 . 1 . 0}. 45 | {0 . 0 . 0}}. 46 | anImageKernel := anImage applyKernel: anIdentityKernel flattened. 47 | self assertCollection: anImage bits equals: anImageKernel bits 48 | ] 49 | 50 | { #category : #test } 51 | ImageFormTest >> testApplyMask [ 52 | "Apply mask an test image. The result should be transparent for black mask and blue for white mask" 53 | 54 | | anImage anMask aResult | 55 | anImage := self imageFormClass imageTest. 56 | anMask := self imageFormClass 57 | newFrom: (self formClass extent: anImage height @ anImage width depth: 32). 58 | anMask fillColor: Color black. 59 | anMask colorAt: 10@10 put: Color white. 60 | aResult := anImage applyMask: anMask. 61 | self 62 | assert: (aResult colorAt: 0 @ 0) 63 | equals: Color transparent. 64 | 65 | anMask fillColor: Color white. 66 | anMask colorAt: 10@10 put: Color black. 67 | aResult := anImage applyMask: anMask. 68 | self 69 | assert: (aResult colorAt: 0 @ 0) 70 | equals: Color blue 71 | ] 72 | 73 | { #category : #test } 74 | ImageFormTest >> testAsGrayScale [ 75 | "Covert a image in GrayScale. The color in position 0,0 should be changed" 76 | 77 | | aForm aGrayImage aImage | 78 | aForm := self formClass extent: 10 @ 10 depth: 32. 79 | aImage := self imageFormClass newFrom: aForm. 80 | aImage fillColor: Color red. 81 | aGrayImage := aImage asGrayScale. 82 | self 83 | assert: (aGrayImage colorAt: 0 @ 0) 84 | equals: 85 | (Color 86 | r: 0.211 87 | g: 0.211 88 | b: 0.211 89 | alpha: 1.0). 90 | self assert: (aImage colorAt: 0 @ 0) equals: Color red 91 | ] 92 | 93 | { #category : #test } 94 | ImageFormTest >> testAsGrayScaleWithAlpha [ 95 | "Covert a image in GrayScale. The color in position 0,0 should be changed" 96 | 97 | | aForm aGrayImage aImage | 98 | aForm := self formClass extent: 10 @ 10 depth: 32. 99 | aImage := self imageFormClass newFrom: aForm. 100 | aImage 101 | fillColor: 102 | (Color 103 | r: 1 104 | g: 0 105 | b: 0 106 | alpha: 0.2). 107 | aGrayImage := aImage asGrayScaleWithAlpha. 108 | self 109 | assert: (aGrayImage colorAt: 0 @ 0) 110 | equals: 111 | (Color 112 | r: 0.211 113 | g: 0.211 114 | b: 0.211 115 | alpha: 0.2). 116 | self 117 | assert: (aImage colorAt: 0 @ 0) 118 | equals: 119 | (Color 120 | r: 1 121 | g: 0 122 | b: 0 123 | alpha: 0.2) 124 | ] 125 | 126 | { #category : #test } 127 | ImageFormTest >> testBlues [ 128 | 129 | | anImage acumulate | 130 | anImage := self imageFormClass newImageColor: Color blue width: 10 height: 10. 131 | acumulate := anImage blues. 132 | self assert: acumulate median > 0.98 133 | ] 134 | 135 | { #category : #test } 136 | ImageFormTest >> testColors [ 137 | 138 | | anImage colors | 139 | anImage := self imageFormClass newImageColor: Color red width: 10 height: 10. 140 | colors := anImage colors. 141 | self 142 | assertCollection: (colors asSet) 143 | hasSameElements: { Color red } 144 | ] 145 | 146 | { #category : #test } 147 | ImageFormTest >> testCropHW [ 148 | "crop a imageTest. The color of the image should be all blue" 149 | 150 | | aImage aCroppedImage | 151 | aImage := self imageFormClass imageTest. 152 | aCroppedImage := aImage crop: 0 @ 0 h: 50 w: 50. 153 | self assert: aCroppedImage shape equals: '50X50'. 154 | self assert: aImage shape equals: '100X100'. 155 | self assert: aCroppedImage colorsUsed equals: {Color blue} 156 | ] 157 | 158 | { #category : #test } 159 | ImageFormTest >> testDarker [ 160 | "darker a white image in 1. The result should be a black image" 161 | 162 | | anImage anImageDarkered | 163 | anImage := self imageFormClass 164 | newFrom: (self formClass extent: 50 @ 50 depth: 32). 165 | anImage fillColor: Color white. 166 | anImageDarkered := anImage darker: 1. 167 | self 168 | assert: (anImageDarkered colorAt: 0 @ 0) 169 | equals: 170 | (Color 171 | r: 0.004 172 | g: 0.004 173 | b: 0.004 174 | alpha: 1.0) 175 | ] 176 | 177 | { #category : #test } 178 | ImageFormTest >> testFlipHorizontally [ 179 | "Flip horizontally the imageTest. The color in position 0,0 should be changed" 180 | 181 | | aImage aImageFlipped | 182 | aImage := self imageFormClass imageTest. 183 | aImageFlipped := aImage flipHorizontally. 184 | self assert: (aImageFlipped colorAt: 0 @ 0) equals: Color red. 185 | self assert: (aImage colorAt: 0 @ 0) equals: Color blue 186 | ] 187 | 188 | { #category : #test } 189 | ImageFormTest >> testFlipVertically [ 190 | "Flip vertically the imageTest. The color in position 0,0 should be changed" 191 | 192 | | aImage aImageFlipped | 193 | aImage := self imageFormClass imageTest. 194 | aImageFlipped := aImage flipVertically. 195 | self assert: (aImageFlipped colorAt: 0 @ 0) equals: Color green. 196 | self assert: (aImage colorAt: 0 @ 0) equals: Color blue 197 | ] 198 | 199 | { #category : #test } 200 | ImageFormTest >> testGetNeighborhoodKernel [ 201 | "Apply convolution to a point using a Identity Kernel. The result color should not be chaged " 202 | 203 | | aForm aImage aPixelColor | 204 | aForm := self formClass extent: 100 @ 100 depth: 32. 205 | aImage := self imageFormClass newFrom: aForm. 206 | aImage fillColor: Color red. 207 | aPixelColor := aImage 208 | getNeighborhood: 0 @ 0 209 | kernel: {0 . 0 . 0 . 0 . 1 . 0 . 0 . 0 . 0}. 210 | self assert: aPixelColor equals: Color red 211 | ] 212 | 213 | { #category : #test } 214 | ImageFormTest >> testGetPointNeighborhoodKsize [ 215 | "Get a Neighbordhood of a point, with an image (3x3). should be return an expected list of points." 216 | 217 | | aForm aImage neighborhood | 218 | aForm := self formClass extent: 3 @ 3 depth: 32. 219 | aImage := self imageFormClass newFrom: aForm. 220 | aImage fillColor: Color red. 221 | neighborhood := aImage getPointNeighborhood: 0 @ 0 ksize: 3 @ 3. 222 | self 223 | assertCollection: neighborhood 224 | hasSameElements: 225 | {(0 @ 0). 226 | (0 @ 0). 227 | (0 @ 1). 228 | (0 @ 0). 229 | (0 @ 0). 230 | (0 @ 1). 231 | (1 @ 0). 232 | (1 @ 0). 233 | (1 @ 1)} asOrderedCollection. 234 | neighborhood := aImage getPointNeighborhood: 1 @ 1 ksize: 3 @ 3. 235 | self 236 | assertCollection: neighborhood 237 | hasSameElements: 238 | {(0 @ 0). 239 | (0 @ 1). 240 | (0 @ 2). 241 | (1 @ 0). 242 | (1 @ 1). 243 | (1 @ 2). 244 | (2 @ 0). 245 | (2 @ 1). 246 | (2 @ 2)} asOrderedCollection. 247 | neighborhood := aImage getPointNeighborhood: 2 @ 2 ksize: 3 @ 3. 248 | self 249 | assertCollection: neighborhood 250 | hasSameElements: 251 | {(1 @ 1). 252 | (1 @ 2). 253 | (1 @ 2). 254 | (2 @ 1). 255 | (2 @ 2). 256 | (2 @ 2). 257 | (2 @ 1). 258 | (2 @ 2). 259 | (2 @ 2)} asOrderedCollection 260 | ] 261 | 262 | { #category : #test } 263 | ImageFormTest >> testGreens [ 264 | 265 | | anImage acumulate | 266 | anImage := self imageFormClass newImageColor: Color green width: 10 height: 10. 267 | acumulate := anImage greens. 268 | self assert: acumulate median > 0.98 269 | ] 270 | 271 | { #category : #test } 272 | ImageFormTest >> testLighter [ 273 | "ligter a black image in 1. The result should be a white image" 274 | 275 | | anImage anImageLightered | 276 | anImage := self imageFormClass 277 | newFrom: (self formClass extent: 50 @ 50 depth: 32). 278 | anImage fillColor: Color black. 279 | anImageLightered := anImage lighter: 1. 280 | self 281 | assert: (anImageLightered colorAt: 0 @ 0) 282 | equals: 283 | (Color 284 | r: 1.0 285 | g: 0.995 286 | b: 0.995 287 | alpha: 1.0) 288 | ] 289 | 290 | { #category : #test } 291 | ImageFormTest >> testNegated [ 292 | 293 | | anImage aNegatedImage| 294 | anImage := self imageFormClass newFrom: (self formClass extent: 50 @ 50 depth: 32). 295 | anImage fillColor: Color black. 296 | aNegatedImage := anImage negated. 297 | self 298 | assert: (aNegatedImage colorAt: 0 @ 0) 299 | equals: Color white. 300 | 301 | anImage fillColor: Color red. 302 | aNegatedImage := anImage negated. 303 | self 304 | assert: (aNegatedImage colorAt: 0 @ 0) 305 | equals: Color cyan. 306 | ] 307 | 308 | { #category : #test } 309 | ImageFormTest >> testNewFrom [ 310 | "Create a new image from an object Form." 311 | 312 | | anImage anForm | 313 | anForm:= self formClass extent: 10@10 depth: 32. 314 | anImage := self imageFormClass newFrom: anForm. 315 | self assert: anForm bits equals: anImage bits. 316 | 317 | ] 318 | 319 | { #category : #test } 320 | ImageFormTest >> testNewImageColorWidthHeight [ 321 | "Create a new image with filled color." 322 | 323 | | anImage anForm | 324 | anForm:= self formClass extent: 10@10 depth: 32. 325 | anForm fillColor: Color blue. 326 | anImage := self imageFormClass newImageColor: Color blue width: 10 height: 10. 327 | self assert: anForm bits equals: anImage bits. 328 | self assert: (anImage colorsUsed) equals:{Color blue}. 329 | 330 | ] 331 | 332 | { #category : #test } 333 | ImageFormTest >> testOpen [ 334 | "Save an image in the imageDirectory and Open it. The image should be the same. Before exit remove the image." 335 | 336 | | aImage aPath aImageSave | 337 | aPath := FileLocator imageDirectory fullPath pathString 338 | , '/test_image.png'. 339 | aImage := self imageFormClass imageTest. 340 | aImage save: aPath. 341 | aImageSave := self imageFormClass open: aPath. 342 | self assert: aImage bits equals: aImageSave bits. 343 | (FileLocator imageDirectory / 'test_image.png') delete. 344 | self 345 | should: [ self imageFormClass open: 'estonoexiste.jpg' ] 346 | raise: Error 347 | ] 348 | 349 | { #category : #test } 350 | ImageFormTest >> testReds [ 351 | 352 | | anImage acumulate | 353 | anImage := self imageFormClass newImageColor: Color red width: 10 height: 10. 354 | acumulate := anImage reds. 355 | self assert: acumulate median > 0.98 356 | ] 357 | 358 | { #category : #test } 359 | ImageFormTest >> testRotateBy [ 360 | "Using the ImageForm imageTest. Rotate in three degress. The color in position 0,0 should be changed" 361 | 362 | | aImage aImageRotated | 363 | aImage := self imageFormClass imageTest. 364 | aImageRotated := aImage rotateBy: 90. 365 | self assert: (aImageRotated colorAt: 100 @ 100) equals: Color red. 366 | self assert: (aImage colorAt: 0 @ 0) equals: Color blue. 367 | 368 | aImageRotated := aImage rotateBy: 180. 369 | self assert: (aImageRotated colorAt: 100 @ 100) equals: Color blue. 370 | self assert: (aImage colorAt: 0 @ 0) equals: Color blue. 371 | 372 | aImageRotated := aImage rotateBy: 360. 373 | self assert: (aImageRotated colorAt: 100 @ 100) equals: Color yellow. 374 | self assert: (aImage colorAt: 0 @ 0) equals: Color blue 375 | ] 376 | 377 | { #category : #test } 378 | ImageFormTest >> testRotateBycenterAt [ 379 | "Using the ImageForm imageTest. Rotate in three directions. The color in position 0,0 should be changed" 380 | 381 | | aImage aImageRotated | 382 | aImage := self imageFormClass imageTest. 383 | aImageRotated := aImage rotateBy: #left centerAt: 0 @ 0. 384 | self assert: (aImageRotated colorAt: 0 @ 0) equals: Color red. 385 | self assert: (aImage colorAt: 0 @ 0) equals: Color blue. 386 | aImageRotated := aImage rotateBy: #right centerAt: 0 @ 0. 387 | self assert: (aImageRotated colorAt: 0 @ 0) equals: Color green. 388 | self assert: (aImage colorAt: 0 @ 0) equals: Color blue. 389 | aImageRotated := aImage rotateBy: #pi centerAt: 0 @ 0. 390 | self assert: (aImageRotated colorAt: 0 @ 0) equals: Color yellow. 391 | self assert: (aImage colorAt: 0 @ 0) equals: Color blue 392 | ] 393 | 394 | { #category : #test } 395 | ImageFormTest >> testSave [ 396 | "Save an image in the imageDirectory. The image should exist. Before exit remove the image." 397 | 398 | | aImage aPath | 399 | aPath := FileLocator imageDirectory fullPath pathString , '/test.png'. 400 | aImage := self imageFormClass imageTest. 401 | aImage save: aPath. 402 | self assert: (FileLocator imageDirectory / 'test.png') exists. 403 | (FileLocator imageDirectory / 'test.png') delete 404 | ] 405 | 406 | { #category : #test } 407 | ImageFormTest >> testScaledHeight [ 408 | "Scaled an image in height 50 and width 50. The result should be a image with shape 50x25" 409 | 410 | | aImage aImageScaled | 411 | aImage := self imageFormClass newFrom: (Form extent: 100 @ 50). 412 | aImageScaled := aImage scaled: 50 height: 50. 413 | self assert: aImageScaled shape equals: '50X25' 414 | ] 415 | 416 | { #category : #test } 417 | ImageFormTest >> testScaledIntoFormOfSize [ 418 | "Scaled an image in height 20 and width 20. The result should be a image with shape 20x20 " 419 | 420 | | aImage aImageScaled | 421 | aImage := self imageFormClass newFrom: (Form extent: 100 @ 300). 422 | aImageScaled := aImage scaledIntoFormOfSize: 20. 423 | self assert: aImageScaled shape equals: '20X20' 424 | ] 425 | 426 | { #category : #test } 427 | ImageFormTest >> testShape [ 428 | "Create an Image with height 100 and width 100. The result should be a string with value 100x100" 429 | 430 | | aImage | 431 | aImage := self imageFormClass newFrom: (self formClass extent: 100@100). 432 | self assert: aImage shape equals: '100X100'. 433 | ] 434 | 435 | { #category : #test } 436 | ImageFormTest >> testSub [ 437 | "For test subtraction operation. Create two ImagesForm (Magenta and Red). The result should be in an image Blue" 438 | 439 | |aImageA aImageB aImageC| 440 | 441 | aImageA := self imageFormClass newFrom: (Form extent: 100@100 depth:32). 442 | aImageB := self imageFormClass newFrom: (Form extent: 100@100 depth:32). 443 | aImageC := self imageFormClass newFrom: (Form extent: 100@100 depth:32). 444 | aImageA fillColor: Color magenta. 445 | aImageB fillColor: Color red. 446 | aImageC fillColor: Color blue. 447 | self assert:(aImageA - aImageB) bits equals: aImageC bits. 448 | self assert: (aImageA colorAt: 0@0) equals: Color magenta. 449 | self assert: (aImageB colorAt: 0@0) equals: Color red. 450 | ] 451 | 452 | { #category : #test } 453 | ImageFormTest >> testSum [ 454 | "For test sum operation. Create two ImagesForm (Red and Blue). The sum should result in an image Magenta" 455 | 456 | | aImageA aImageB aImageC | 457 | aImageA := self imageFormClass 458 | newFrom: (self formClass extent: 100 @ 100 depth: 32). 459 | aImageB := self imageFormClass 460 | newFrom: (self formClass extent: 100 @ 100 depth: 32). 461 | aImageC := self imageFormClass 462 | newFrom: (self formClass extent: 100 @ 100 depth: 32). 463 | aImageA fillColor: Color red. 464 | aImageB fillColor: Color blue. 465 | aImageC fillColor: (Color r: 1.0 g: 0 b: 1.0). 466 | self assert: (aImageA + aImageB) bits equals: aImageC bits. 467 | self assert: (aImageA colorAt: 0 @ 0) equals: Color red. 468 | self assert: (aImageB colorAt: 0 @ 0) equals: Color blue 469 | ] 470 | -------------------------------------------------------------------------------- /src/Image-Form-test/package.st: -------------------------------------------------------------------------------- 1 | Package { #name : #'Image-Form-test' } 2 | --------------------------------------------------------------------------------