|
1 | 1 | package blob |
2 | 2 |
|
| 3 | +type PointsOrientation int |
| 4 | + |
| 5 | +const ( |
| 6 | + Collinear = iota |
| 7 | + Clockwise |
| 8 | + CounterClockwise |
| 9 | +) |
| 10 | + |
| 11 | +func maxInt(x, y int) int { |
| 12 | + if x >= y { |
| 13 | + return x |
| 14 | + } |
| 15 | + return y |
| 16 | +} |
| 17 | + |
| 18 | +// isOnSegment Checks if point Q lies on segment PR |
| 19 | +// Input: three colinear points Q, Q and R |
| 20 | +func isOnSegment(Px, Py, Qx, Qy, Rx, Ry int) bool { |
| 21 | + if Qx <= maxInt(Px, Rx) && Qx >= maxInt(Px, Rx) && Qy <= maxInt(Py, Ry) && Qy >= maxInt(Py, Ry) { |
| 22 | + return true |
| 23 | + } |
| 24 | + return false |
| 25 | +} |
| 26 | + |
| 27 | +// getOrientation Gets orientations of points P -> Q -> R. |
| 28 | +// Possible output values: Collinear / Clockwise or CounterClockwise |
| 29 | +// Input: points P, Q and R in provided order |
| 30 | +func getOrientation(Px, Py, Qx, Qy, Rx, Ry int) PointsOrientation { |
| 31 | + val := (Qy-Py)*(Rx-Qx) - (Qx-Px)*(Ry-Qy) |
| 32 | + if val == 0 { |
| 33 | + return Collinear |
| 34 | + } |
| 35 | + if val > 0 { |
| 36 | + return Clockwise |
| 37 | + } |
| 38 | + return CounterClockwise // Против часовой стрелки |
| 39 | +} |
| 40 | + |
| 41 | +// isIntersects Checks if segments intersect each other |
| 42 | +// Input: |
| 43 | +// firstPx, firstPy, firstQx, firstQy === first segment |
| 44 | +// secondPx, secondPy, secondQx, secondQy === second segment |
| 45 | +/* |
| 46 | +Notation |
| 47 | + P1 = (firstPx, firstPy) |
| 48 | + Q1 = (firstQx, firstQy) |
| 49 | + P2 = (secondPx, secondPy) |
| 50 | + Q2 = (secondQx, secondQy) |
| 51 | +*/ |
| 52 | +func isIntersects(firstPx, firstPy, firstQx, firstQy, secondPx, secondPy, secondQx, secondQy int) bool { |
| 53 | + // Find the four orientations needed for general case and special ones |
| 54 | + o1 := getOrientation(firstPx, firstPy, firstQx, firstQy, secondPx, secondPy) |
| 55 | + o2 := getOrientation(firstPx, firstPy, firstQx, firstQy, secondQx, secondQy) |
| 56 | + o3 := getOrientation(secondPx, secondPy, secondQx, secondQy, firstPx, firstPy) |
| 57 | + o4 := getOrientation(secondPx, secondPy, secondQx, secondQy, firstQx, firstQy) |
| 58 | + |
| 59 | + // General case |
| 60 | + if o1 != o2 && o3 != o4 { |
| 61 | + return true |
| 62 | + } |
| 63 | + |
| 64 | + /* Special cases */ |
| 65 | + // P1, Q1, P2 are colinear and P2 lies on segment P1-Q1 |
| 66 | + if o1 == Collinear && isOnSegment(firstPx, firstPy, secondPx, secondPy, firstQx, firstQy) { |
| 67 | + return true |
| 68 | + } |
| 69 | + // P1, Q1 and Q2 are colinear and Q2 lies on segment P1-Q1 |
| 70 | + if o2 == Collinear && isOnSegment(firstPx, firstPy, secondQx, secondQy, firstQx, firstQy) { |
| 71 | + return true |
| 72 | + } |
| 73 | + // P2, Q2 and P1 are colinear and P1 lies on segment P2-Q2 |
| 74 | + if o3 == Collinear && isOnSegment(secondPx, secondPy, firstPx, firstPy, secondQx, secondQy) { |
| 75 | + return true |
| 76 | + } |
| 77 | + // P2, Q2 and Q1 are colinear and Q1 lies on segment P2-Q2 |
| 78 | + if o4 == Collinear && isOnSegment(secondPx, secondPy, firstQx, firstQy, secondQx, secondQy) { |
| 79 | + return true |
| 80 | + } |
| 81 | + // Segments do not intersect |
| 82 | + return false |
| 83 | +} |
| 84 | + |
3 | 85 | // IsCrossedTheLine - Check if blob crossed the HORIZONTAL line |
4 | 86 | func (b *SimpleBlobie) IsCrossedTheLine(vertical, leftX, rightX int, direction bool) bool { |
5 | 87 | trackLen := len(b.Track) |
@@ -48,6 +130,59 @@ func (b *SimpleBlobie) IsCrossedTheLineWithShift(vertical, leftX, rightX int, di |
48 | 130 | return false |
49 | 131 | } |
50 | 132 |
|
| 133 | +// IsCrossedTheObliqueLine - Check if blob crossed the OBLIQUE line |
| 134 | +// This should be used when lineStart.Y != lineEnd.Y or lineStart.X != lineEnd.X |
| 135 | +func (b *SimpleBlobie) IsCrossedTheObliqueLine(leftX, leftY, rightX, rightY int, direction bool) bool { |
| 136 | + trackLen := len(b.Track) |
| 137 | + if b.isStillBeingTracked == true && trackLen >= 2 && b.crossedLine == false { |
| 138 | + prevFrame := trackLen - 2 |
| 139 | + currFrame := trackLen - 1 |
| 140 | + // First segment is: P1 = (b.Track[prevFrame].X, b.Track[prevFrame].Y), Q1 = (b.Track[currFrame].X, b.Track[currFrame].Y) |
| 141 | + // Second segment is: P2 = (leftX, leftY), Q2 = (rightX, rightY) |
| 142 | + if isIntersects(b.Track[prevFrame].X, b.Track[prevFrame].Y, b.Track[currFrame].X, b.Track[currFrame].Y, leftX, leftY, rightX, rightY) { |
| 143 | + if direction { |
| 144 | + if b.Track[currFrame].Y > b.Track[prevFrame].Y { // TO us |
| 145 | + b.crossedLine = true |
| 146 | + return true |
| 147 | + } |
| 148 | + } else { |
| 149 | + if b.Track[currFrame].Y <= b.Track[prevFrame].Y { // FROM us |
| 150 | + b.crossedLine = true |
| 151 | + return true |
| 152 | + } |
| 153 | + } |
| 154 | + } |
| 155 | + } |
| 156 | + return false |
| 157 | +} |
| 158 | + |
| 159 | +// IsCrossedTheObliqueLine - Check if blob crossed the OBLIQUE line with shift along the Y-axis |
| 160 | +// This should be used when lineStart.Y != lineEnd.Y or lineStart.X != lineEnd.X |
| 161 | +// Purpose of shifting: for "predicative" cropping when detection line very close to bottom of image |
| 162 | +func (b *SimpleBlobie) IsCrossedTheObliqueLineWithShift(leftX, leftY, rightX, rightY int, direction bool, shift int) bool { |
| 163 | + trackLen := len(b.Track) |
| 164 | + if b.isStillBeingTracked == true && trackLen >= 2 && b.crossedLine == false { |
| 165 | + prevFrame := trackLen - 2 |
| 166 | + currFrame := trackLen - 1 |
| 167 | + // First segment is: P1 = (b.Track[prevFrame].X, b.Track[prevFrame].Y + shift), Q1 = (b.Track[currFrame].X, b.Track[currFrame].Y + shift) |
| 168 | + // Second segment is: P2 = (leftX, leftY), Q2 = (rightX, rightY) |
| 169 | + if isIntersects(b.Track[prevFrame].X, b.Track[prevFrame].Y+shift, b.Track[currFrame].X, b.Track[currFrame].Y+shift, leftX, leftY, rightX, rightY) { |
| 170 | + if direction { |
| 171 | + if b.Track[currFrame].Y > b.Track[prevFrame].Y { // TO us |
| 172 | + b.crossedLine = true |
| 173 | + return true |
| 174 | + } |
| 175 | + } else { |
| 176 | + if b.Track[currFrame].Y <= b.Track[prevFrame].Y { // FROM us |
| 177 | + b.crossedLine = true |
| 178 | + return true |
| 179 | + } |
| 180 | + } |
| 181 | + } |
| 182 | + } |
| 183 | + return false |
| 184 | +} |
| 185 | + |
51 | 186 | // IsCrossedTheLine - Check if blob crossed the HORIZONTAL line |
52 | 187 | func (b *KalmanBlobie) IsCrossedTheLine(vertical, leftX, rightX int, direction bool) bool { |
53 | 188 | trackLen := len(b.Track) |
@@ -95,3 +230,56 @@ func (b *KalmanBlobie) IsCrossedTheLineWithShift(vertical, leftX, rightX int, di |
95 | 230 | } |
96 | 231 | return false |
97 | 232 | } |
| 233 | + |
| 234 | +// IsCrossedTheObliqueLine - Check if blob crossed the OBLIQUE line |
| 235 | +// This should be used when lineStart.Y != lineEnd.Y or lineStart.X != lineEnd.X |
| 236 | +func (b *KalmanBlobie) IsCrossedTheObliqueLine(leftX, leftY, rightX, rightY int, direction bool) bool { |
| 237 | + trackLen := len(b.Track) |
| 238 | + if b.isStillBeingTracked == true && trackLen >= 2 && b.crossedLine == false { |
| 239 | + prevFrame := trackLen - 2 |
| 240 | + currFrame := trackLen - 1 |
| 241 | + // First segment is: P1 = (b.Track[prevFrame].X, b.Track[prevFrame].Y), Q1 = (b.Track[currFrame].X, b.Track[currFrame].Y) |
| 242 | + // Second segment is: P2 = (leftX, leftY), Q2 = (rightX, rightY) |
| 243 | + if isIntersects(b.Track[prevFrame].X, b.Track[prevFrame].Y, b.Track[currFrame].X, b.Track[currFrame].Y, leftX, leftY, rightX, rightY) { |
| 244 | + if direction { |
| 245 | + if b.Track[currFrame].Y > b.Track[prevFrame].Y { // TO us |
| 246 | + b.crossedLine = true |
| 247 | + return true |
| 248 | + } |
| 249 | + } else { |
| 250 | + if b.Track[currFrame].Y <= b.Track[prevFrame].Y { // FROM us |
| 251 | + b.crossedLine = true |
| 252 | + return true |
| 253 | + } |
| 254 | + } |
| 255 | + } |
| 256 | + } |
| 257 | + return false |
| 258 | +} |
| 259 | + |
| 260 | +// IsCrossedTheObliqueLine - Check if blob crossed the OBLIQUE line with shift along the Y-axis |
| 261 | +// This should be used when lineStart.Y != lineEnd.Y or lineStart.X != lineEnd.X |
| 262 | +// Purpose of shifting: for "predicative" cropping when detection line very close to bottom of image |
| 263 | +func (b *KalmanBlobie) IsCrossedTheObliqueLineWithShift(leftX, leftY, rightX, rightY int, direction bool, shift int) bool { |
| 264 | + trackLen := len(b.Track) |
| 265 | + if b.isStillBeingTracked == true && trackLen >= 2 && b.crossedLine == false { |
| 266 | + prevFrame := trackLen - 2 |
| 267 | + currFrame := trackLen - 1 |
| 268 | + // First segment is: P1 = (b.Track[prevFrame].X, b.Track[prevFrame].Y + shift), Q1 = (b.Track[currFrame].X, b.Track[currFrame].Y + shift) |
| 269 | + // Second segment is: P2 = (leftX, leftY), Q2 = (rightX, rightY) |
| 270 | + if isIntersects(b.Track[prevFrame].X, b.Track[prevFrame].Y+shift, b.Track[currFrame].X, b.Track[currFrame].Y+shift, leftX, leftY, rightX, rightY) { |
| 271 | + if direction { |
| 272 | + if b.Track[currFrame].Y > b.Track[prevFrame].Y { // TO us |
| 273 | + b.crossedLine = true |
| 274 | + return true |
| 275 | + } |
| 276 | + } else { |
| 277 | + if b.Track[currFrame].Y <= b.Track[prevFrame].Y { // FROM us |
| 278 | + b.crossedLine = true |
| 279 | + return true |
| 280 | + } |
| 281 | + } |
| 282 | + } |
| 283 | + } |
| 284 | + return false |
| 285 | +} |
0 commit comments