├── .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 | [](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 | 
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 |
--------------------------------------------------------------------------------