Làm thế nào tôi có thể định lượng độ thẳng của đường vẽ?


37

Tôi đang làm việc với một trò chơi yêu cầu người chơi vẽ một đường thẳng từ điểm A (x1, y1) đến điểm B (x2, y2) khác trên màn hình của thiết bị Android.

Tôi muốn tìm xem bản vẽ đó phù hợp như thế nào với một đường thẳng. Chẳng hạn, kết quả 90% có nghĩa là bản vẽ gần như hoàn toàn phù hợp với đường kẻ. Nếu người chơi vẽ một đường cong từ A đến B, nó sẽ nhận được điểm thấp.

Các điểm cuối không được biết trước. Tôi có thể làm cái này như thế nào?


1
Bạn có biết trước hai điểm cuối của bạn là gì không? Hoặc được xác định tại thời điểm người dùng ngừng chạm vào màn hình?
Vaillancourt

Xin lỗi nếu mô tả của tôi không rõ ràng với bạn. Chà, điểm bắt đầu A (x, y) là lần chạm đầu tiên và điểm kết thúc B (x, y) là khi chúng tôi phát hành từ màn hình cảm ứng như bạn đã nói.
dùng3637362

Chúng tôi có một câu hỏi liên quan đến việc ghép các chữ cái do người chơi vẽ .
Anko

3
Vui lòng không đăng hình ảnh cho mã nguồn trong tương lai.
Josh

1
@ user3637362 Tôi hiểu rằng bạn đang bắt đầu j=1để bạn có thể so sánh touchList[j]với touchList[j-1], nhưng khi nào touch.phase == TouchPhase.Beganhoặc touch.phase == TouchPhase.Endedcác vị trí không được thêm vào touchListvà sau đó không được bao gồm trong sumLength. Lỗi này sẽ xuất hiện trong mọi trường hợp nhưng sẽ rõ ràng hơn khi dòng có ít phân đoạn.
Kelly Thomas

Câu trả lời:


52

Một đường thẳng hoàn hảo cũng sẽ là đường ngắn nhất có thể có tổng chiều dài sqrt((x1-x2)² + (y1-y2)²). Một dòng nguệch ngoạc hơn sẽ là một kết nối ít lý tưởng hơn và do đó chắc chắn sẽ dài hơn.

Khi bạn lấy tất cả các điểm riêng biệt của đường dẫn mà người dùng đã vẽ và tổng hợp khoảng cách giữa chúng, bạn có thể so sánh tổng chiều dài với độ dài lý tưởng. Tổng chiều dài chia cho chiều dài lý tưởng càng nhỏ thì đường càng tốt.

Đây là một hình dung. Khi các chấm đen là điểm cuối của cử chỉ và các điểm màu xanh là các điểm bạn đo được trong cử chỉ, bạn sẽ tính toán và cộng các độ dài của các đường màu xanh lá cây và chia cho độ dài của đường màu đỏ:

nhập mô tả hình ảnh ở đây

Điểm số hoặc chỉ số tội lỗi là 1 sẽ là hoàn hảo, bất cứ điều gì cao hơn sẽ không hoàn hảo, bất cứ điều gì dưới 1 sẽ là một lỗi. Khi bạn muốn có điểm theo phần trăm, hãy chia 100% cho số đó.


34
Có một vấn đề nhỏ với cách tiếp cận này là các polylines có độ dài bằng nhau không bằng 'thẳng'. Một đường chao đảo với độ lệch thấp (nhưng nhiều lần) về đường thẳng là 'căng hơn' so với một đường có độ dài bằng nhau lệch ra một điểm rồi quay lại.
Dancrumb

Tôi không thể +1 @Dancrumbs nhận xét đủ - đó là một hạn chế khá quan trọng với phương pháp này vì nếu người dùng đang vẽ một đường thẳng, họ sẽ chao đảo một chút để điều này giống như một trường hợp sử dụng thông thường.
T. Kiley

@Dancrumb chỉ là yếu tố trong khoảng cách trung bình từ đường hoặc yếu tố trong "khoảng cách tối đa" bất kỳ điểm nào là từ đường. Sau đó, bạn có thể cân nhắc thuật toán đối với các đường lung lay hơn với biên độ sai lệch nhỏ hơn và tránh xa các đường đi xa khỏi đường dẫn dự kiến.
Superdoggy

2
@Dancrumb nghe có vẻ như điều này có thể mang lại lợi ích cho trường hợp sử dụng của OP. Tất nhiên, các đường vẽ tay sẽ có độ lệch nhỏ. Cách tiếp cận này thực sự có thể làm việc để làm giảm hiệu quả của những khác biệt dự kiến.

2
@ user3637362 bạn có một lỗi trong mã của bạn. Một lời giải thích có thể là bạn đã quên tính khoảng cách giữa điểm bắt đầu và điểm đầu tiên hoặc điểm cuối và điểm cuối cùng, nhưng không nhìn vào mã của bạn thì không thể biết lỗi của bạn có thể là gì.
Philipp

31

Đây có thể không phải là cách tốt nhất để thực hiện điều này, nhưng tôi đề xuất RMSD (độ lệch bình phương trung bình gốc) có thể tốt hơn, chỉ đơn thuần là phương pháp khoảng cách, trong các trường hợp được đề cập bởi Dancrumb (xem hai dòng đầu tiên bên dưới).

RMSD = sqrt(mean(deviation^2))

Chú thích:

  • Tổng độ lệch tuyệt đối (giống như tích phân) có thể tốt hơn, vì nó không tính trung bình các lỗi dương với các sai số âm. ( =sum(abs(deviation)))
  • Bạn có thể sẽ phải tìm kiếm khoảng cách ngắn nhất đến đường thẳng nếu có cách tạo khoảng cách ngắn hơn so với thả vuông góc.

vẽ

(Xin thứ lỗi cho chất lượng thấp của bản vẽ của tôi)

Như bạn thấy, bạn phải

  1. tìm một vectơ trực giao cho dòng của bạn ( sản phẩm chấm bằng 0 ).
    Nếu dòng của bạn hướng tới (1, 3) bạn muốn (3, -1)(máng gốc)
  2. Đo khoảng cách h từ đường lý tưởng đến người dùng, song song với vectơ đó.
  3. Tính RMSD hoặc tổng của sự khác biệt tuyệt đối.

Câu trả lời của Joel Bosveld chỉ ra một trường hợp thú vị: một đường thẳng gần như hoàn hảo với các góc ở đầu và cuối. Nếu dòng được vẽ tự do bởi người dùng, đây thực sự là một vấn đề. Tuy nhiên tôi nghĩ rằng phương pháp này có thể bao gồm kịch bản đó. Người ta thực sự có thể thực hiện sự phù hợp với RMSD hoặc Integral tuyệt đối như một giá trị tối thiểu hóa. Giá trị bắt đầu có thể là điểm bắt đầu và điểm kết thúc. Vì chiều dài không thành vấn đề, nó cũng không phù hợp nếu tối ưu hóa di chuyển các điểm sao cho đường lý tưởng vươn xa hơn hoặc ngắn hơn (chiều cao phải được tính theo niveau đó).
gr4nt3d

1
Một trường hợp khác dường như không bao gồm: Giả sử mọi điểm được đo nằm trên trục x, nhưng đường đảo ngược hướng nhiều lần. Điều này sẽ trả về một lỗi 0.
dave mankoff

23

Các câu trả lời hiện tại không tính đến việc các điểm cuối là tùy ý (chứ không phải đưa ra). Do đó, khi đo độ thẳng của đường cong, sẽ không có ý nghĩa khi sử dụng các điểm cuối (ví dụ: để tính chiều dài, góc, vị trí dự kiến). Một ví dụ đơn giản sẽ là một đường thẳng với cả hai đầu kincked. Nếu chúng ta đo bằng cách sử dụng khoảng cách từ đường cong và đường thẳng giữa các điểm cuối thì điều này sẽ khá lớn, vì đường thẳng mà chúng ta đã vẽ được bù từ đường thẳng giữa các điểm cuối.

Làm thế nào để chúng ta nói làm thế nào đường cong là? Giả sử rằng đường cong đủ mịn, chúng ta muốn biết trung bình, tiếp tuyến của đường cong đang thay đổi bao nhiêu. Đối với một dòng, điều này sẽ bằng không (vì tiếp tuyến là hằng số).

Nếu chúng ta để vị trí tại thời điểm t là (x (t), y (t)), thì tiếp tuyến là (Dx (t), Dy (t)), trong đó Dx (t) là đạo hàm của x tại thời điểm t (trang web này dường như thiếu hỗ trợ TeX). Nếu đường cong không được tham số hóa theo chiều dài cung, chúng ta sẽ chuẩn hóa bằng cách chia cho | | (Dx (t), Dy (t)) ||. Vậy ta có một vectơ đơn vị (hoặc góc) của tiếp tuyến với đường cong tại thời điểm t. Vậy, góc là a (t) = (Dx (t), Dy (t)) / || (Dx (t), Dy (t)) | |

Sau đó, chúng tôi quan tâm đến | | Da (t) || ^ 2 được tích hợp dọc theo đường cong.

Vì chúng ta rất có thể có các điểm dữ liệu riêng biệt thay vì một đường cong, chúng ta phải sử dụng các khác biệt hữu hạn để tính gần đúng các đạo hàm. Thế là, Da (t) trở thành (a(t+h)-a(t))/h. Và, một (t) trở thành ((x(t+h)-x(t))/h,(y(t+h)-y(t))/h)/||((x(t+h)-x(t))/h,(y(t+h)-y(t))/h)||. Sau đó, chúng tôi nhận được S bằng cách tổng hợp h||Da(t)||^2tất cả các điểm dữ liệu và có thể bình thường hóa theo độ dài của đường cong. Nhiều khả năng, chúng tôi sử dụng h=1, nhưng nó thực sự chỉ là một yếu tố quy mô tùy ý.

Để nhắc lại, S sẽ bằng 0 đối với một dòng và càng lớn thì nó càng lệch khỏi một dòng. Để chuyển đổi sang định dạng cần thiết, sử dụng 1/(1+S). Cho rằng thang đo có phần tùy ý, có thể nhân S với một số dương (hoặc biến đổi nó theo một cách khác, ví dụ sử dụng bS ^ c thay vì S) để điều chỉnh mức độ đường cong nhất định.


2
Đây là định nghĩa hợp lý nhất về độ thẳng.
Marcks Thomas

1
Đây là câu trả lời hợp lý nhất và tôi chắc chắn rằng những người khác sẽ trở nên rất bực bội. Thật không may, hình thức mà giải pháp được trình bày hơi mơ hồ hơn các hình thức khác, nhưng tôi khuyên OP nên tiếp tục.
Dan Sheppard

Nói chung tôi cũng nghĩ rằng câu trả lời này thực sự là tốt nhất. Mặc dù một vấn đề làm phiền tôi: Điều gì xảy ra nếu dòng không "đủ mượt"? Ví dụ: nếu bạn có hai đoạn thẳng hoàn toàn với một góc, giả sử, 90 °. Tôi đã nhầm lẫn hay điều này sẽ dẫn đến một kết quả khá thấp so với một đường thẳng thực sự trơn tru? (Tôi nghĩ trường hợp người dùng của Dancrumb với một dòng chao đảo là một vấn đề tương tự) ... Mặc dù vậy, đây chắc chắn là cách tốt nhất.
gr4nt3d

3

Đây là một hệ thống dựa trên lưới, phải không? Tìm điểm riêng của bạn cho đường và tính độ dốc của đường. Bây giờ, bằng cách sử dụng phép tính đó, xác định các điểm hợp lệ mà dòng sẽ đi qua, đưa ra một số lỗi sai giá trị chính xác.

Thông qua một thử nghiệm ngắn và thử nghiệm lỗi, xác định lượng điểm phù hợp tốt và xấu sẽ tồn tại và thiết lập trò chơi của bạn bằng cách sử dụng thang đo cho cùng kết quả từ thử nghiệm của bạn.

tức là Một đường ngắn có độ dốc gần như ngang có thể có 7 điểm mà bạn có thể vẽ qua. Nếu bạn luôn có thể khớp 6 hoặc nhiều hơn trong số 7 được xác định là một phần của đường thẳng, thì đó sẽ là điểm cao nhất. Việc chấm điểm cho độ dài và độ chính xác phải là một phần của việc chấm điểm.


3

Một biện pháp rất dễ dàng và trực quan là khu vực giữa đường thẳng phù hợp nhất và đường cong thực tế. Xác định điều này khá đơn giản:

  1. Sử dụng một hình vuông nhỏ nhất phù hợp với tất cả các điểm (điều này ngăn chặn vấn đề kết thúc được đề cập bởi Joel Bosveld).
  2. Đối với tất cả các điểm trên đường cong, xác định khoảng cách đến đường. Đây cũng là một vấn đề tiêu chuẩn. (đại số tuyến tính, biến đổi cơ sở.)
  3. Tính tổng mọi khoảng cách.

Bạn có thể phiền nếu tôi hỏi bạn về một số mã hóa văn bản (JS, C #) hoặc mã giả, vì hầu hết các câu trả lời ở trên được mô tả trong lý thuyết, tôi không biết bắt đầu như thế nào?
dùng3637362

1
@ user3637362: StackOverflow có câu trả lời thiết thực: stackoverflow.com/questions/6195335/ Thẻ stackoverflow.com/questions/849211/NH
MSalters

2

Ý tưởng là giữ cho tất cả các điểm mà người dùng chạm vào, sau đó đánh giá và tính tổng khoảng cách giữa mỗi điểm đó với đường được hình thành khi người dùng nhả màn hình.

Đây là một cái gì đó để giúp bạn bắt đầu bằng mã giả:

bool mIsRecording = false;
point[] mTouchedPoints = new point[];

function onTouch
  mIsRecording = true

functon update
  if mIsRecording
    mTouchedPoints.append(currentlyTouchedLocation)

function onRelease
  mIsRecording = false

  cumulativeDistance = 0

  line = makeLine( mTouchedPoints.first, mTouchedPoints.last )

  for each point in mTouchedPoints:
    cumulativeDistance = distanceOfPointToLine(point, line)

  mTouchedPoints = new point[]

Những gì cumulativeDistancecó thể cung cấp cho bạn một ý tưởng về phù hợp. Khoảng cách bằng 0 có nghĩa là người dùng luôn đi thẳng. Bây giờ bạn phải thực hiện một số thử nghiệm để xem cách nó hoạt động trong ngữ cảnh của bạn. Và bạn có thể muốn khuếch đại giá trị được trả về bằng distanceOfPointToLinecách bình phương nó để xử phạt nhiều hơn khoảng cách lớn từ đường.

Tôi không quen thuộc với sự thống nhất, nhưng mã ở updateđây có thể đi vào một onDragchức năng.

Và bạn có thể muốn thêm vào một nơi nào đó trong đó một số mã để ngăn đăng ký một điểm nếu nó giống với mã cuối cùng được đăng ký. Bạn không muốn đăng ký công cụ khi người dùng không di chuyển.


5
Khi bạn cộng khoảng cách giữa đường lý tưởng và điểm cho mỗi điểm đo, bạn cần tính đến số lượng biện pháp bạn đã thực hiện, nếu không, khi người dùng rút chậm hơn hoặc sử dụng thiết bị có tốc độ quét nhanh hơn, họ sẽ đăng ký nhiều hơn điểm có nghĩa là họ sẽ nhận được điểm kém hơn.
Philipp

@Philipp Có bạn làm! Tôi phải thừa nhận cách làm của bạn có vẻ tốt hơn của tôi: P
Vaillancourt

Tôi nghĩ rằng phương pháp này được cải thiện bằng cách lấy khoảng cách trung bình , thay vì tích lũy.
Dancrumb

@Dancrumb Thực sự, nó phụ thuộc vào nhu cầu, nhưng vâng, đó sẽ là một cách để làm điều đó.
Vaillancourt

2

Một phương pháp bạn có thể sử dụng là chia dòng thành các phân đoạn và thực hiện một sản phẩm chấm vectơ giữa mỗi vectơ đại diện cho phân đoạn và một vectơ biểu thị một đường thẳng giữa điểm đầu tiên và điểm cuối cùng. Điều này có lợi ích cho phép bạn tìm thấy các phân đoạn cực kỳ "nhọn" một cách dễ dàng.

Chỉnh sửa:

Ngoài ra, tôi sẽ xem xét sử dụng độ dài của phân khúc ngoài sản phẩm chấm. Một vectơ rất ngắn nhưng trực giao nên được tính ít hơn một vectơ dài có ít sai lệch.


1

Cách dễ nhất và nhanh nhất có thể chỉ đơn giản là tìm ra độ dày của đường kẻ phải bao gồm tất cả các điểm của đường kẻ được vẽ.

Đường kẻ càng dày thì người dùng càng tệ trong việc vẽ đường kẻ của họ.


0

Bằng cách nào đó đề cập đến MSalters Trả lời, đây là một số thông tin cụ thể hơn.

Sử dụng phương pháp bình phương tối thiểu để phù hợp với một dòng cho điểm của bạn. Về cơ bản, bạn đang tìm kiếm một hàm y = f (x) phù hợp nhất. Khi bạn đã có nó, bạn có thể sử dụng các giá trị y thực tế để tính tổng bình phương:

s = tổng trên ((yf (x)) ^ 2)

Tổng càng nhỏ, đường thẳng càng căng.

Làm thế nào để có được xấp xỉ tốt nhất, được giải thích tại đây: http://math.mit.edu/~gs/linearalgebra/ila0403.pdf

Chỉ cần đọc từ "Lắp một đường thẳng". Lưu ý rằng t được sử dụng thay vì x và b thay vì y. C và D được xác định là gần đúng, khi đó bạn có f (x) = C + Dx

Lưu ý bổ sung: Rõ ràng, bạn cũng phải tính đến độ dài của dòng. Mỗi dòng bao gồm 2 Điểm sẽ hoàn hảo. Tôi không biết chính xác bối cảnh, nhưng tôi đoán tôi sẽ sử dụng tổng bình phương chia cho số điểm làm xếp hạng. Ngoài ra tôi sẽ thêm yêu cầu về độ dài tối thiểu, số điểm tối thiểu. (Có thể khoảng 75% chiều dài maximun)

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.