0

PenInput & PenSmoot I have two curves. One handdrawn and one is a smoothed version of the handdrawn. The data of each curve is stored in 2 seperate vector arrays. Time Delta is also stored in the handdrawn curve vector, so i can replay the drawing process and so that it looks natural.

Now i need to transfer the Time Delta from Curve 1 (Raw input) to Curve 2 (already smoothed curve).

Sometimes the size of the first vector is larger and sometimes smaller than the second vector.
(Depends on the input draw speed)

So my question is: How do i fill vector PenSmoot.time with the correct values?

Case 1: Input vector is larger

PenInput.time[0] = 0         PenSmoot.time[0] = 0
PenInput.time[1] = 5         PenSmoot.time[1] = ?
PenInput.time[2] = 12        PenSmoot.time[2] = ?
PenInput.time[3] = 2         PenSmoot.time[3] = ?
PenInput.time[4] = 50        PenSmoot.time[4] = ?
PenInput.time[5] = 100
PenInput.time[6] = 20
PenInput.time[7] = 3
PenInput.time[8] = 9
PenInput.time[9] = 33

Case 2: Input vector is smaller

PenInput.time[0] = 0         PenSmoot.time[0] = 0
PenInput.time[1] = 5         PenSmoot.time[1] = ?
PenInput.time[2] = 12        PenSmoot.time[2] = ?
PenInput.time[3] = 2         PenSmoot.time[3] = ?
PenInput.time[4] = 50        PenSmoot.time[4] = ?
                             PenSmoot.time[5] = ?
                             PenSmoot.time[6] = ?
                             PenSmoot.time[7] = ?
                             PenSmoot.time[8] = ?
                             PenSmoot.time[9] = ?

Simplyfied representation:

PenInput holds the whole data of a drawn curve (Raw Input)

PenInput.x         // X coordinate)
PenInput.y         // Y coordinate)
PenInput.pressure  // The pressure of the pen)
PenInput.timetotl  // Total elapsed time)
PenInput.timepart  // Time fragments)

PenSmoot holds the data of the massaged (smoothed,evenly distributed) curve of PenInput

PenSmoot.x         // X coordinate)
PenSmoot.y         // Y coordinate)
PenSmoot.pressure  // Unknown - The pressure of the pen)
PenSmoot.timetotl  // Unknown - Total elapsed time)
PenSmoot.timepart  // Unknown - Time fragments)


This is the struct that i have.

struct Pencil 
{
    sf::VertexArray vertices;
    std::vector<int> pressure;
    std::vector<sf::Int32> timetotl;
    std::vector<sf::Int32> timepart;
};

martineau
  • 119,623
  • 25
  • 170
  • 301
Chris
  • 79
  • 1
  • 8
  • Code example would be more helpful. – Tagger5926 Oct 10 '19 at 00:22
  • What have you attempted to do so far to solve this yourself? – Remy Lebeau Oct 10 '19 at 00:22
  • Please most the math you would like to use to fill the second vector. – R Sahu Oct 10 '19 at 00:23
  • Iam still researching and trying to wrap my head around it. I will post something asap. – Chris Oct 10 '19 at 00:24
  • What is the relationship between `PenInput` and `PenSmoot`? What is the purpose of `PenSmoot`? – Thomas Matthews Oct 10 '19 at 00:44
  • 1
    For a linear interpolation, you have two-points (generally `x,y` values) and you have some third point `x` between the two for which you want to find the `y` value for. What are the points for your problem, and what are the list of `x` values you want to interpolate to find the value of `y` at? See [Linear interpolation](https://en.wikipedia.org/wiki/Linear_interpolation) – David C. Rankin Oct 10 '19 at 00:47
  • @DavidC.Rankin The points for my problem are: value of index[0] to value of index[last] of input vector vs. value of index[0] to value of index[last] of output vector. So i have no 2D data but only 1D. – Chris Oct 10 '19 at 00:53
  • Try asking the question again when you have figured out how to specify the exact requirements. Avoid focusing on your interpretation of what the solution is, as that is possibly XY Problem. When specifying requirements, you must describe the _meaning_ of all inputs and how they are collected, their relationship to the output, and how the output will be used. – paddy Oct 10 '19 at 01:03
  • @ThomasMatthews PenInput is the input data from a mouse or pen drawing.
    PenInput.x and PenInput.y and PenInput.time.
    PenSmoot is the result of smoothing the original PenInput and also applying evenly distributed points to it. What is missing is the recorded time from PenInput.
    – Chris Oct 10 '19 at 01:05
  • Okay, I'm still confused. What is the *independent variable* (`x`) and that are the *dependent values* (`y`) for the original points. and then what are the new *independent values* of `x` you want to interpolate the new *dependent values* at? I see `PenInput.time` array assigned varying values. Are those the new *independent* values you are supposed to find a value for and store in even increments in the `PenSmoot.time` array? Are the indexes supposed to represent an *independent* value? Still confused. – David C. Rankin Oct 10 '19 at 01:07
  • So `PenInput` is an *array-of-struct*? Please post the relevant structs and members. – David C. Rankin Oct 10 '19 at 01:08
  • @paddy I dont know how i could describe it any better than i already did. I need to adjust the values given in vector Input to match the size or amount of Indexes of the output vector. The values are milliseconds. so when you add them all together you get the time that it took to draw that curve. Each Index has also an x,y coordinate but thats not relevant because i have those coordinates already in the Output (smoothed) curve. What is missing is the time. So each index represents also the position of a x,y,time,penpressure,color etc. – Chris Oct 10 '19 at 01:13
  • `struct Pencil { sf::VertexArray vertices; std::vector pressure; std::vector timetotl; std::vector timepart; };` – Chris Oct 10 '19 at 01:15
  • 1
    If you're simply wanting to distribute the time values evenly, it's pretty trivial. Convert the times to store the cumulative sum, then for each index in the target, calculate a (possibly fractional) index to sample the "noisy" times. Use basic linear interpolation to compute the time for fractional indices. Now your smooth times are also a cumulative sum resampled. Now reverse that to turn those times back into deltas, and you're done. – paddy Oct 10 '19 at 01:20
  • @DavidC.Rankin I dont know what dependent and independent means but the Indexes represent the actual curve. And the lenght of the curve is represented by the size() of the vector. – Chris Oct 10 '19 at 01:43
  • @paddy - I dont want to distribute the values evenly. Thats exactly what i want to avoid. – Chris Oct 10 '19 at 04:28
  • 1
    I was talking about _resampling_ the time deltas from one sample count to a new sample count, assuming that you wanted to preserve them as best as possible. The trivial case being where the noisy and smooth counts are the same, and all other cases being approximated via linear interpolation of a cumulative sum. In all cases, the final result is that the noisy and smoothed times should add up to the same total time, and have roughly the same "shape". – paddy Oct 10 '19 at 04:36
  • @paddy Yes i want to preserve the original time deltas as close as possible. If the smoothed curve has more points the time has to be interpolated to fill the "gaps". If the smoothed curve has less points than the original then some points have to vanish. But the TOTAL time must always be the same. Also i dont know why my thread has been blocked(on hold). Thats not really helpful. Mr. JerryCoffin immediatly grasped what i want. – Chris Oct 10 '19 at 05:06
  • @paddy Do you think i should rename the thread to: "Resampling time deltas from one sample count to a new sample count" ? Is this clearer? – Chris Oct 10 '19 at 05:09
  • That's up to you. The thread has been closed. The reason there were so many comments is because there are several interpretations of what it might mean to interpolate/smooth values. It seemed like either you were assuming all readers knew what you're trying to do, or you didn't realize how many possible interpretations there are for your problem as originally specified. In any case, did my comment not give you a suitable solution? The current answer to this question seems to spread the time values out uniformly, which is maybe not what you want. My approach fits the values into a new range. – paddy Oct 10 '19 at 05:14
  • Did you try it? Did it work? – paddy Oct 10 '19 at 05:25
  • @paddy Iam confused now which of your answers i should use. – Chris Oct 10 '19 at 05:33
  • I only gave you one. – paddy Oct 10 '19 at 05:34
  • @paddy You mean this? quote: "The current answer to this question seems to spread the time values out uniformly, which is maybe not what you want. " ----- quot: "If you're simply wanting to distribute the time values evenly, it's pretty trivial. Convert the times to store the cumulative sum, then for each index in the target, calculate a (possibly fractional) ...." – Chris Oct 10 '19 at 05:40
  • Yes, by "current answer" I was talking about the answer written by Jerry Coffin. By "my approach", I was referring to the solution that I gave earlier which you have quoted in the second part of your comment. I know you got hung up on me saying "distribute the time values evenly"... I did not mean "make them all the same". If you read the comment and the approach, maybe that will become clear. – paddy Oct 10 '19 at 05:43
  • @paddy Okay. Iam trying your solution. First i have to decipher what you mean. – Chris Oct 10 '19 at 05:54
  • Here: [I made you a simple demo](https://ideone.com/r1Gtnq).. Might have bugs ;) – paddy Oct 10 '19 at 06:40
  • Oh wow! I didnt expect that! I was still trying to figure out what you said, lol. Thank you! – Chris Oct 10 '19 at 08:35

1 Answers1

2

[This answer has been extensively revised based on editing to the question.]

Okay, it seems to me that you just about need to interpolate the time stamps in parallel with the points.

I'm going to guess that the incoming data is something on the order of an array of points (e.g., X, Y coordinates) and an array of time deltas with the same number of each, so time-delta N tells you the time it took to get from point N-1 to point N.

When you interpolate the points, you're probably going to want to do it intelligently. For example, in the shape shown in the question, we have what look like two nearly straight lines, one with positive slope, and the other with negative slope. According to the picture, that's composed of 263 points. We could reduce that to three points and still have a fairly reasonable representation of the original shape by choosing the two end-points plus one point where the two lines meet.

We probably don't need to go quite that far though. Especially taking time into account, we'd probably want to use at least 7 points for the output--one for each end-point of each colored segment. That would give us 6 straight line segments. Let's say those are at points 0, 30, 140, 180, 200, 250, and 263.

We'd then use exactly the same segmentation on the time deltas. Add up the deltas from 0 to 30 to get an average speed for the first segment. Add up the deltas for 31 through 140 to get an average speed for the second segment (and so on to the end).

Increasing the number of points works out roughly the same way. We need to look at exactly which input points were used to create a pair of output points. For a simplistic example, let's assume we produced output that was precisely double the number of input points. We'd then interpolate time deltas exactly halfway between each pair of input points.

In the case shown in the question, we start with unevenly distributed inputs, but produce evenly distributed outputs. So the second output point might be an average of the first four input points. The next output point might be an average of three input points (and so on). In many cases, it's likely that neither end-point of a segment in the output corresponds precisely to any point in the input.

That's fine too. We interpolate between two points of the input to figure out the time hack for the starting point of the output segment. Likewise for the ending point. Then we can compute the total time it should have taken to travel between them based on the time delta between the points.

If you want to get fancy, you could use a higher order interpolation instead of linear. That does require more input points per interpolation, but it looks like you probably have plenty to do something like a quadratic or cubic interpolation (in most cases). This is likely to make the most differences at transitions--places the "pen" was accelerating or decelerating quickly. In such an place, linear interpolation can give somewhat misleading results (though, given the number of points you seem to be working with, it may not make enough difference to notice).

As an illustration, let's consider a straight line. We're going to start from 5 input points, and produce 7 output points.

So, the input points are [0, 2, 7, 10, 15], and the associated time deltas are [0, 1, 4, 8, 3].

So, out total distance traveled is 16, and we want our output points to be evenly distributed. So, the distance between output points will be 16/7 = (roughly) 2.29.

So, obviously the first output point and time are both 0. The second output point is 2.29. To compute the output time, we take the entirety of the time to the first input point (0->2), plus .29 / (7-2) * (4-1). That interpolated section gives 1.37, so our first output time delta is 2.37.

The next output point should be at a distance of 4.58. Since the second input segment goes from 2 to 7, our entire second output segment will lie within the second input segment. So, we take 2.29 / (7-2), telling use that this output segment occupies .458 of the input segment. We then multiply that by the time for the second input segment to get the time delta for the second output segment: .458 * (4-1) = 1.374.

[...and it continues on the same way until we reach the end.]

Jerry Coffin
  • 476,176
  • 80
  • 629
  • 1,111
  • Hello and thanks for your answer. The thing is that i HAVE both curves already. One Raw input and one smoothed. What is missing is the TIME for the already smoothed curve. I want to reproduce how the user drew. So when i playback it looks natural. But just with a smoothed curve instead of the Raw input. I used a RamerDouglasPeucker to reduce the RAW curve, then i apply an ChaikinSmooth and lastly i apply a UniforLinearInterpolation to have the points evenly spaced so that, when i draw the actual brush has no spacing. – Chris Oct 10 '19 at 01:40
  • Yes, you are right, this are deltas! Time fractions. When u add them together you see how long it took to draw the whole curve. – Chris Oct 10 '19 at 02:00
  • WoW! Thank you! I think i have to read that 10 times more but i believe that i understand the basics. When you say quote "Especially taking time into account, we'd probably want to use at least 7 points for the output". Does this apply to any kind of curve/time combination? What if one draws a square for example? Then the most time concentration would be in the 4 corners. How does the allgorithm "detect" such areas of time concentrations and splits accordingly? – Chris Oct 10 '19 at 08:25
  • @Chris: I discussed one possibility for how to do splits in [an old answer](https://stackoverflow.com/a/17480086/179910). As it stands that uses a 40% change as signaling a change in group, but that's arbitrarily chosen--you'll probably need some adjustments to get good results for your data. In this situation, you also don't want to sort the data before grouping. – Jerry Coffin Oct 10 '19 at 14:38
  • @Chris: Oh, one other detail: depending on the frequency at which you're collecting data, chances are decent that you'll want to look at more than just one pair of points at a time to make a decision about grouping. For hand input, you probably want/need to do some averaging before making a decision, to be sure you're seeing a real change in motion rather than just somebody's hand being a bit shaky. – Jerry Coffin Oct 10 '19 at 16:26
  • Thank you very much. I will try to transfer your instructions into code. – Chris Oct 11 '19 at 11:38