1 | get("#121") -- compareTables |
2 | get("#348") -- bright and rgb |
3 | |
4 | maxSize = {width=100, height=50} |
5 | minSize = {width=4, height=4} |
6 | threshold = 0.5 |
7 | wandSize = 2 -- magic wand size |
8 | |
9 | local rectangles, img |
10 | |
11 | function cloneRectangle(r) |
12 | return newRectangle(r.x, r.y, r.width, r.height) |
13 | end |
14 | |
15 | function newRectangle(x, y, w, h) |
16 | return {x=x, y=y, width=w, height=h} |
17 | end |
18 | |
19 | function contains(r, x, y) |
20 | return x >= r.x and y >= r.y and x < r.x+r.width and y < r.y+r.width |
21 | end |
22 | |
23 | function seen(x, y) |
24 | for r, _ in pairs(rectangles) do |
25 | if contains(r, x, y) then |
26 | return true |
27 | end |
28 | end |
29 | return false |
30 | end |
31 | |
32 | -- returns rectangle |
33 | function magicWand(image, x, y) |
34 | if image ~= img then |
35 | img, rectangles = image, {} |
36 | end |
37 | if seen(x, y) then return nil end |
38 | local r = newRectangle(x, y, 1, 1) |
39 | |
40 | while true do |
41 | local last = cloneRectangle(r) |
42 | expandLeft(image, r) |
43 | if tooLarge(r) then return nil end |
44 | expandTop(image, r) |
45 | if tooLarge(r) then return nil end |
46 | expandRight(image, r) |
47 | if tooLarge(r) then return nil end |
48 | expandBottom(image, r) |
49 | if tooLarge(r) then return nil end |
50 | if compareTables(last, r) then |
51 | if r.width >= minSize.width and r.height >= minSize.width then |
52 | rectangles[r] = true |
53 | return r |
54 | else |
55 | return nil |
56 | end |
57 | end |
58 | end |
59 | end |
60 | |
61 | function tooLarge(r) |
62 | return maxSize ~= nil and (r.width > maxSize.width or r.height > maxSize.height) |
63 | end |
64 | |
65 | function expandLeft(image, r) |
66 | local newX = math.max(r.x - wandSize, 0) |
67 | if newX == r.x then return end |
68 | newX = searchFromLeft(image, newRectangle(newX, r.y, r.x-newX, r.height)) |
69 | r.width = r.width+r.x-newX |
70 | r.x = newX |
71 | end |
72 | |
73 | function searchFromLeft(image, r) |
74 | for x = r.x, r.x+r.width-1 do |
75 | if regionNotEmpty(image, newRectangle(x, r.y, 1, r.height)) then |
76 | return x |
77 | end |
78 | end |
79 | return r.x+r.width |
80 | end |
81 | |
82 | function expandRight(image, r) |
83 | local newX = math.min(r.x + r.width + wandSize, image.width) |
84 | if newX == r.x+r.width then return end |
85 | newX = searchFromRight(image, newRectangle(r.x+r.width, r.y, newX-(r.x+r.width), r.height)) |
86 | r.width = newX-r.x |
87 | end |
88 | |
89 | function searchFromRight(image, r) |
90 | for x = r.x+r.width-1, r.x, -1 do |
91 | if regionNotEmpty(image, newRectangle(x, r.y, 1, r.height)) then |
92 | return x+1 |
93 | end |
94 | end |
95 | return r.x |
96 | end |
97 | |
98 | function expandTop(image, r) |
99 | local newY = math.max(r.y - wandSize, 0) |
100 | if newY == r.y then return end |
101 | newY = searchFromTop(image, newRectangle(r.x, newY, r.width, r.y-newY)) |
102 | r.height = r.height+r.y-newY |
103 | r.y = newY |
104 | end |
105 | |
106 | function searchFromTop(image, r) |
107 | for y = r.y, r.y+r.height-1 do |
108 | if regionNotEmpty(image, newRectangle(r.x, y, r.width, 1)) then |
109 | return y |
110 | end |
111 | end |
112 | return r.y+r.height |
113 | end |
114 | |
115 | function expandBottom(image, r) |
116 | local newY = math.min(r.y + r.height + wandSize, image.height) |
117 | if newY == r.y+r.height then return end |
118 | newY = searchFromBottom(image, newRectangle(r.x, r.y + r.height, r.width, newY-(r.y+r.height))) |
119 | r.height = newY-r.y |
120 | end |
121 | |
122 | function searchFromBottom(image, r) |
123 | for y = r.y+r.height-1, r.y, -1 do |
124 | if regionNotEmpty(image, newRectangle(r.x, y, r.width, 1)) then |
125 | return y+1 |
126 | end |
127 | end |
128 | return r.y |
129 | end |
130 | |
131 | -- we're looking for dark pixels this time |
132 | function regionNotEmpty(image, r) |
133 | --return image.clip(rectangle).anyPixelBrighterThan(threshold) |
134 | for y=r.y, r.y+r.height-1 do |
135 | for x=r.x, r.x+r.width-1 do |
136 | if bright(rgb(image.getInt(x, y))) < threshold then |
137 | return true |
138 | end |
139 | end |
140 | end |
141 | return false |
142 | end |
143 | |
144 | |
145 | function recttostring(r) |
146 | return r.x..", "..r.y..", "..r.x+r.width..", "..r.y+r.height |
147 | end |
148 | |
149 | function magicWandAll(image) |
150 | local allrects = {} |
151 | |
152 | for y = 0, image.height-1, 2 do |
153 | for x = 0, image.width-1, 2 do |
154 | local r = magicWand(image, x, y) |
155 | |
156 | if r then |
157 | allrects[recttostring(r)] = true |
158 | end |
159 | end |
160 | end |
161 | |
162 | return allrects |
163 | end |
Began life as a copy of #363
test run test run with input download show line numbers
Travelled to 13 computer(s): aoiabmzegqzx, bhatertpkbcr, cbybwowwnfue, gwrvuhgaqvyk, ishqpsrjomds, lpdgvwnxivlt, mqqgnosmbjvj, pyentgdyhuwx, pzhvpgtvlbxg, tslmcundralx, tvejysmllsmz, vouqrxazstgt, znullqslnikg
No comments. add comment
Snippet ID: | #369 |
Snippet name: | Magic Wand Algorithm (optimized & including magicWandAll) |
Eternal ID of this version: | #369/1 |
Text MD5: | c93de8ac2039791bdd437ef966ef5e06 |
Author: | stefan |
Category: | image recognition |
Type: | Lua code |
Public (visible to everyone): | Yes |
Archived (hidden from active list): | No |
Created/modified: | 2015-02-02 20:20:05 |
Source code size: | 4001 bytes / 163 lines |
Pitched / IR pitched: | No / Yes |
Views / Downloads: | 696 / 184 |
Referenced in: | [show references] |