Skip to content

Commit c2a8d76

Browse files
committed
add oblique line intersections checks
1 parent 901dd39 commit c2a8d76

File tree

3 files changed

+229
-0
lines changed

3 files changed

+229
-0
lines changed

v2/blob/blob.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,4 +30,6 @@ type Blobie interface {
3030
DrawTrack(mat *gocv.Mat, optionalText string)
3131
IsCrossedTheLine(vertical, leftX, rightX int, direction bool) bool
3232
IsCrossedTheLineWithShift(vertical, leftX, rightX int, direction bool, shift int) bool
33+
IsCrossedTheObliqueLine(leftX, leftY, rightX, rightY int, direction bool) bool
34+
IsCrossedTheObliqueLineWithShift(leftX, leftY, rightX, rightY int, direction bool, shift int) bool
3335
}

v2/blob/line_cross.go

Lines changed: 188 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,87 @@
11
package blob
22

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+
385
// IsCrossedTheLine - Check if blob crossed the HORIZONTAL line
486
func (b *SimpleBlobie) IsCrossedTheLine(vertical, leftX, rightX int, direction bool) bool {
587
trackLen := len(b.Track)
@@ -48,6 +130,59 @@ func (b *SimpleBlobie) IsCrossedTheLineWithShift(vertical, leftX, rightX int, di
48130
return false
49131
}
50132

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+
51186
// IsCrossedTheLine - Check if blob crossed the HORIZONTAL line
52187
func (b *KalmanBlobie) IsCrossedTheLine(vertical, leftX, rightX int, direction bool) bool {
53188
trackLen := len(b.Track)
@@ -95,3 +230,56 @@ func (b *KalmanBlobie) IsCrossedTheLineWithShift(vertical, leftX, rightX int, di
95230
}
96231
return false
97232
}
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+
}

v2/blob/line_cross_test.go

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
package blob
2+
3+
import (
4+
"image"
5+
"testing"
6+
)
7+
8+
func TestHorizontalLineTest(t *testing.T) {
9+
horizontalLine := [][]int{
10+
[]int{4, 35},
11+
[]int{73, 35},
12+
}
13+
direction := true // true - TO us
14+
allblobies := NewBlobiesDefaults()
15+
16+
simpleB_time0 := NewSimpleBlobie(image.Rect(26, 8, 44, 18), nil)
17+
simpleB_time1 := NewSimpleBlobie(image.Rect(26, 20, 44, 30), nil)
18+
simpleB_time2 := NewSimpleBlobie(image.Rect(26, 32, 44, 42), nil)
19+
20+
allblobies.MatchToExisting([]Blobie{simpleB_time0, simpleB_time1, simpleB_time2})
21+
22+
for _, b := range allblobies.Objects {
23+
if b.IsCrossedTheLine(horizontalLine[0][1], horizontalLine[0][0], horizontalLine[1][0], direction) {
24+
t.Logf("Correct when direction is TO US")
25+
} else {
26+
t.Logf("Incorrect when direction is TO US")
27+
}
28+
29+
if b.IsCrossedTheLine(horizontalLine[0][1], horizontalLine[0][0], horizontalLine[1][0], !direction) {
30+
t.Logf("Incorrect when direction is FROM US")
31+
} else {
32+
t.Logf("Correct when direction is FROM US")
33+
}
34+
}
35+
}
36+
37+
func TestObliqueLineTest(t *testing.T) {
38+
39+
}

0 commit comments

Comments
 (0)