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 | expandRight(image, r) |
43 | if tooLarge(r) then return nil end |
44 | expandBottom(image, r) |
45 | if tooLarge(r) then return nil end |
46 | if compareTables(last, r) then |
47 | if r.width >= minSize.width and r.height >= minSize.width then |
48 | rectangles[r] = true |
49 | return r |
50 | else |
51 | return nil |
52 | end |
53 | end |
54 | end |
55 | end |
56 | |
57 | function tooLarge(r) |
58 | return maxSize ~= nil and (r.width > maxSize.width or r.height > maxSize.height) |
59 | end |
60 | |
61 | function expandLeft(image, r) |
62 | local newX = math.max(r.x - wandSize, 0) |
63 | if newX == r.x then return end |
64 | newX = searchFromLeft(image, newRectangle(newX, r.y, r.x-newX, r.height)) |
65 | r.width = r.width+r.x-newX |
66 | r.x = newX |
67 | end |
68 | |
69 | function searchFromLeft(image, r) |
70 | for x = r.x, r.x+r.width-1 do |
71 | if regionNotEmpty(image, newRectangle(x, r.y, 1, r.height)) then |
72 | return x |
73 | end |
74 | end |
75 | return r.x+r.width |
76 | end |
77 | |
78 | function expandRight(image, r) |
79 | local newX = math.min(r.x + r.width + wandSize, image.width) |
80 | if newX == r.x+r.width then return end |
81 | newX = searchFromRight(image, newRectangle(r.x+r.width, r.y, newX-(r.x+r.width), r.height)) |
82 | r.width = newX-r.x |
83 | end |
84 | |
85 | function searchFromRight(image, r) |
86 | for x = r.x+r.width-1, r.x, -1 do |
87 | if regionNotEmpty(image, newRectangle(x, r.y, 1, r.height)) then |
88 | return x+1 |
89 | end |
90 | end |
91 | return r.x |
92 | end |
93 | |
94 | function expandTop(image, r) |
95 | local newY = math.max(r.y - wandSize, 0) |
96 | if newY == r.y then return end |
97 | newY = searchFromTop(image, newRectangle(r.x, newY, r.width, r.y-newY)) |
98 | r.height = r.height+r.y-newY |
99 | r.y = newY |
100 | end |
101 | |
102 | function searchFromTop(image, r) |
103 | for y = r.y, r.y+r.height-1 do |
104 | if regionNotEmpty(image, newRectangle(r.x, y, r.width, 1)) then |
105 | return y |
106 | end |
107 | end |
108 | return r.y+r.height |
109 | end |
110 | |
111 | function expandBottom(image, r) |
112 | local newY = math.min(r.y + r.height + wandSize, image.height) |
113 | if newY == r.y+r.height then return end |
114 | newY = searchFromBottom(image, newRectangle(r.x, r.y + r.height, r.width, newY-(r.y+r.height))) |
115 | r.height = newY-r.y |
116 | end |
117 | |
118 | function searchFromBottom(image, r) |
119 | for y = r.y+r.height-1, r.y, -1 do |
120 | if regionNotEmpty(image, newRectangle(r.x, y, r.width, 1)) then |
121 | return y+1 |
122 | end |
123 | end |
124 | return r.y |
125 | end |
126 | |
127 | -- we're looking for dark pixels this time |
128 | function regionNotEmpty(image, r) |
129 | --return image.clip(rectangle).anyPixelBrighterThan(threshold) |
130 | for y=r.y, r.y+r.height-1 do |
131 | for x=r.x, r.x+r.width-1 do |
132 | if bright(rgb(image.getInt(x, y))) < threshold then |
133 | return true |
134 | end |
135 | end |
136 | end |
137 | return false |
138 | end |
139 | |
140 | |
141 | function recttostring(r) |
142 | return r.x..", "..r.y..", "..r.x+r.width..", "..r.y+r.height |
143 | end |
144 | |
145 | function magicWandAll(image) |
146 | local allrects = {} |
147 | |
148 | for y = 0, image.height-1, 2 do |
149 | for x = 0, image.width-1, 2 do |
150 | local r = magicWand(image, x, y) |
151 | |
152 | if r then |
153 | allrects[recttostring(r)] = true |
154 | end |
155 | end |
156 | end |
157 | |
158 | return allrects |
159 | end |
Began life as a copy of #369
test run test run with input download show line numbers
Travelled to 12 computer(s): aoiabmzegqzx, bhatertpkbcr, cbybwowwnfue, gwrvuhgaqvyk, ishqpsrjomds, lpdgvwnxivlt, mqqgnosmbjvj, pyentgdyhuwx, pzhvpgtvlbxg, tslmcundralx, tvejysmllsmz, vouqrxazstgt
No comments. add comment
Snippet ID: | #398 |
Snippet name: | Magic Wand Algorithm (optimizing) |
Eternal ID of this version: | #398/1 |
Text MD5: | 118d7f99c02fdc09a4f53a82dda704cb |
Author: | stefan |
Category: | image recognition |
Type: | Lua code |
Public (visible to everyone): | Yes |
Archived (hidden from active list): | No |
Created/modified: | 2015-02-05 00:16:59 |
Source code size: | 3870 bytes / 159 lines |
Pitched / IR pitched: | No / Yes |
Views / Downloads: | 749 / 178 |
Referenced in: | [show references] |