Hi Yu, Thanks you four your comments. To find the parameters I just tried some values.
On this image https://github.com/antigol/xf86-input-wacom/blob/master/distortion.svg you can see which constraints I choose * F(0) = p * F(d) = d * F(a) = h * F'(d) = 1 * F'(a) = 1 As you can see, the constraints I choose are not yet the good ones because the polynomial can become non-monotone. Unfortunately monotonic condition cannot be added into the linear system. I'm looking for something better, but I like the polynomials because they are easy to compute. I don't have create a tool to calibrate for the moment. Thanks, Mario Geiger Le mar. 25 août 2015 à 01:27, Feng Yu <rainwood...@gmail.com> a écrit : > HI Mario, > > This is great! finally a sensible solution to the non-linear calibration. > > Sometime ago I wrote a patch that does the nonlinear calibration via a > huge 2d calibration grid: > > https://github.com/rainwoodman/xf86-input-wacom/tree/gridcal > > The grid was an ugly huge XAtom. A parametrized approach, like your > polynomials make much more sense -- just a few bytes to > send around. > > > To fix it I added an opposite distortion with a polynomial function. Each > > polynomial depend on 4 parameters. There is 4 polynomials, one per > border. > > - I didn't manage to make floating point parameters > > > - I added some functions to solve a linear system but I'm sure that > > including a mathematical library should be better. > > Pulling in a giant mathematical library (e.g. BLAS) into a driver is > probably not a good idea. > Since solve_lu is called only when the property is set (and the > precision doesn't matter much in this context), > I think a simple function like what you have is sufficient. > > > - I'm afraid that my calculus with variables of type double slows the > > execution. But I didn't manage to measure it. > > > > I suspect there are already quite a few floating point operations in > the linear driver, and usually user apps add even more > floating point operations after the event is dispatched. Thus my guess > is a few more floating point operations will not make a > noticeable difference. > > > For example on my laptop I use the following parameters : > >> xinput set-prop "Wacom ISDv4 EC Pen stylus" --type=int "Wacom Tablet > >> Border Distortion Top Y" 100 0 55 68 > >> xinput set-prop "Wacom ISDv4 EC Pen stylus" --type=int "Wacom Tablet > >> Border Distortion Top X" 80 0 20 26 > > How did you find these numbers? > > Did you also write a calibration tool? Otherwise, it may be possible > to modify the calibration tool I wrote for the grid > to write out the best fit parameters for your polynomials. (at > > https://github.com/rainwoodman/xf86-input-wacom/blob/gridcal/tools/calibrate.py > ) > > > Thanks, > > Yu > > > > Yours faithfully, > > Mario Geiger > > > > From efa7dbb3a0bd54e3b58fba9541d54669c1224d5e Mon Sep 17 00:00:00 2001 > > From: Mario Geiger <geiger.ma...@gmail.com> > > Date: Mon, 24 Aug 2015 23:29:35 +0200 > > Subject: [PATCH] distortion > > > > --- > > include/wacom-properties.h | 6 ++ > > src/wcmCommon.c | 52 ++++++++-- > > src/wcmXCommand.c | 232 > > +++++++++++++++++++++++++++++++++++++++++++++ > > src/xf86WacomDefs.h | 10 ++ > > 4 files changed, 293 insertions(+), 7 deletions(-) > > > > diff --git a/include/wacom-properties.h b/include/wacom-properties.h > > index b845083..e25a944 100644 > > --- a/include/wacom-properties.h > > +++ b/include/wacom-properties.h > > @@ -27,6 +27,12 @@ > > /* 32 bit, 4 values, top x, top y, bottom x, bottom y */ > > #define WACOM_PROP_TABLET_AREA "Wacom Tablet Area" > > > > +/* 32 bit, 4 values, border width, border offset, point physical, point > > logical */ > > +#define WACOM_PROP_TABLET_DISTORTION_TOP_X "Wacom Tablet Border > Distortion > > Top X" > > +#define WACOM_PROP_TABLET_DISTORTION_TOP_Y "Wacom Tablet Border > Distortion > > Top Y" > > +#define WACOM_PROP_TABLET_DISTORTION_BOTTOM_X "Wacom Tablet Border > > Distortion Bottom X" > > +#define WACOM_PROP_TABLET_DISTORTION_BOTTOM_Y "Wacom Tablet Border > > Distortion Bottom Y" > > + > > /* 8 bit, 1 value, [0 - 3] (NONE, CW, CCW, HALF) */ > > #define WACOM_PROP_ROTATION "Wacom Rotation" > > > > diff --git a/src/wcmCommon.c b/src/wcmCommon.c > > index 9408f42..00c5dc5 100644 > > --- a/src/wcmCommon.c > > +++ b/src/wcmCommon.c > > @@ -438,6 +438,21 @@ static void sendCommonEvents(InputInfoPtr pInfo, > const > > WacomDeviceState* ds, > > sendWheelStripEvents(pInfo, ds, first_val, num_vals, valuators); > > } > > > > +static double distortionCorrectionInBorders(double coord, double border, > > double* polynomial) > > +{ > > + if (coord < border) { > > + double x = coord; > > + int i; > > + > > + coord = 0.0; > > + for (i = 0; i < 5; ++i) { > > + coord *= x; > > + coord += polynomial[i]; > > + } > > + } > > + return coord; > > +} > > + > > /* rotate x and y before post X inout events */ > > void wcmRotateAndScaleCoordinates(InputInfoPtr pInfo, int* x, int* y) > > { > > @@ -446,19 +461,42 @@ void wcmRotateAndScaleCoordinates(InputInfoPtr > pInfo, > > int* x, int* y) > > DeviceIntPtr dev = pInfo->dev; > > AxisInfoPtr axis_x, axis_y; > > int tmp_coord; > > + double dcoord; > > > > /* scale into on topX/topY area */ > > axis_x = &dev->valuator->axes[0]; > > axis_y = &dev->valuator->axes[1]; > > > > /* Don't try to scale relative axes */ > > - if (axis_x->max_value > axis_x->min_value) > > - *x = xf86ScaleAxis(*x, axis_x->max_value, axis_x->min_value, > > - priv->bottomX, priv->topX); > > - > > - if (axis_y->max_value > axis_y->min_value) > > - *y = xf86ScaleAxis(*y, axis_y->max_value, axis_y->min_value, > > - priv->bottomY, priv->topY); > > + if (axis_x->max_value > axis_x->min_value) { > > + dcoord = (*x - priv->topX) / (double)(priv->bottomX - priv->topX); > > + dcoord = distortionCorrectionInBorders(dcoord, > > priv->distortion_topX_border, priv->distortion_topX_poly); > > + dcoord = 1.0 - dcoord; > > + dcoord = distortionCorrectionInBorders(dcoord, > > priv->distortion_bottomX_border, priv->distortion_bottomX_poly); > > + dcoord = 1.0 - dcoord; > > + > > + //*x = dcoord * (priv->bottomX - priv->topX) + priv->topX; > > + //*x = xf86ScaleAxis(*x, axis_x->max_value, axis_x->min_value, > > + // priv->bottomX, priv->topX); > > + > > + *x = round(dcoord * (axis_x->max_value - axis_x->min_value) + > > axis_x->min_value); > > + if (*x < axis_x->min_value) *x = axis_x->min_value; > > + if (*x > axis_x->max_value) *x = axis_x->max_value; > > + } > > + > > + if (axis_y->max_value > axis_y->min_value) { > > + dcoord = (*y - priv->topY) / (double)(priv->bottomY - priv->topY); > > + dcoord = distortionCorrectionInBorders(dcoord, > > priv->distortion_topY_border, priv->distortion_topY_poly); > > + dcoord = 1.0 - dcoord; > > + dcoord = distortionCorrectionInBorders(dcoord, > > priv->distortion_bottomY_border, priv->distortion_bottomY_poly); > > + dcoord = 1.0 - dcoord; > > + //*y = dcoord * (priv->bottomY - priv->topY) + priv->topY; > > + //*y = xf86ScaleAxis(*y, axis_y->max_value, axis_y->min_value, > > + // priv->bottomY, priv->topY); > > + *y = round(dcoord * (axis_y->max_value - axis_y->min_value) + > > axis_y->min_value); > > + if (*y < axis_y->min_value) *y = axis_y->min_value; > > + if (*y > axis_y->max_value) *y = axis_y->max_value; > > + } > > > > /* coordinates are now in the axis rage we advertise for the device */ > > > > diff --git a/src/wcmXCommand.c b/src/wcmXCommand.c > > index 346ff61..2a620f0 100644 > > --- a/src/wcmXCommand.c > > +++ b/src/wcmXCommand.c > > @@ -34,6 +34,8 @@ > > #endif > > > > static void wcmBindToSerial(InputInfoPtr pInfo, unsigned int serial); > > +static int distortionCorrectionComputePolynomial(double p, double a, > double > > h, double d, double* poly); > > + > > > > > > > /***************************************************************************** > > * wcmDevSwitchModeCall -- > > @@ -82,6 +84,10 @@ int wcmDevSwitchMode(ClientPtr client, DeviceIntPtr > dev, > > int mode) > > static Atom prop_devnode; > > static Atom prop_rotation; > > static Atom prop_tablet_area; > > +static Atom prop_tablet_distortion_topX; > > +static Atom prop_tablet_distortion_topY; > > +static Atom prop_tablet_distortion_bottomX; > > +static Atom prop_tablet_distortion_bottomY; > > static Atom prop_pressurecurve; > > static Atom prop_serials; > > static Atom prop_serial_binding; > > @@ -227,6 +233,18 @@ void InitWcmDeviceProperties(InputInfoPtr pInfo) > > prop_tablet_area = InitWcmAtom(pInfo->dev, WACOM_PROP_TABLET_AREA, > > XA_INTEGER, 32, 4, values); > > } > > > > + if (!IsPad(priv)) { > > + /* border_width border_offset point_physical point_logical */ > > + values[0] = 0; > > + values[1] = 0; > > + values[2] = 0; > > + values[3] = 0; > > + prop_tablet_distortion_topX = InitWcmAtom(pInfo->dev, > > WACOM_PROP_TABLET_DISTORTION_TOP_X, XA_INTEGER, 32, 4, values); > > + prop_tablet_distortion_topY = InitWcmAtom(pInfo->dev, > > WACOM_PROP_TABLET_DISTORTION_TOP_Y, XA_INTEGER, 32, 4, values); > > + prop_tablet_distortion_bottomX = InitWcmAtom(pInfo->dev, > > WACOM_PROP_TABLET_DISTORTION_BOTTOM_X, XA_INTEGER, 32, 4, values); > > + prop_tablet_distortion_bottomY = InitWcmAtom(pInfo->dev, > > WACOM_PROP_TABLET_DISTORTION_BOTTOM_Y, XA_INTEGER, 32, 4, values); > > + } > > + > > values[0] = common->wcmRotate; > > if (!IsPad(priv)) { > > prop_rotation = InitWcmAtom(pInfo->dev, WACOM_PROP_ROTATION, > XA_INTEGER, > > 8, 1, values); > > @@ -717,6 +735,66 @@ int wcmSetProperty(DeviceIntPtr dev, Atom property, > > XIPropertyValuePtr prop, > > priv->bottomX = values[2]; > > priv->bottomY = values[3]; > > } > > + } else if (property == prop_tablet_distortion_topX) > > + { > > + INT32 *values = (INT32*)prop->data; > > + > > + if (prop->size != 4 || prop->format != 32) > > + return BadValue; > > + > > + if (!checkonly) > > + { > > + /* border_width border_offset point_physical point_logical */ > > + priv->distortion_topX_border = values[0]/1000.0; > > + distortionCorrectionComputePolynomial( > > + values[1]/1000.0, values[2]/1000.0, values[3]/1000.0, values[0]/1000.0, > > + priv->distortion_topX_poly); > > + } > > + } else if (property == prop_tablet_distortion_topY) > > + { > > + INT32 *values = (INT32*)prop->data; > > + > > + if (prop->size != 4 || prop->format != 32) > > + return BadValue; > > + > > + if (!checkonly) > > + { > > + /* border_width border_offset point_physical point_logical */ > > + priv->distortion_topY_border = values[0]/1000.0; > > + distortionCorrectionComputePolynomial( > > + values[1]/1000.0, values[2]/1000.0, values[3]/1000.0, values[0]/1000.0, > > + priv->distortion_topY_poly); > > + } > > + } else if (property == prop_tablet_distortion_bottomX) > > + { > > + INT32 *values = (INT32*)prop->data; > > + > > + if (prop->size != 4 || prop->format != 32) > > + return BadValue; > > + > > + if (!checkonly) > > + { > > + /* border_width border_offset point_physical point_logical */ > > + priv->distortion_bottomX_border = values[0]/1000.0; > > + distortionCorrectionComputePolynomial( > > + values[1]/1000.0, values[2]/1000.0, values[3]/1000.0, values[0]/1000.0, > > + priv->distortion_bottomX_poly); > > + } > > + } else if (property == prop_tablet_distortion_bottomY) > > + { > > + INT32 *values = (INT32*)prop->data; > > + > > + if (prop->size != 4 || prop->format != 32) > > + return BadValue; > > + > > + if (!checkonly) > > + { > > + /* border_width border_offset point_physical point_logical */ > > + priv->distortion_bottomY_border = values[0]/1000.0; > > + distortionCorrectionComputePolynomial( > > + values[1]/1000.0, values[2]/1000.0, values[3]/1000.0, values[0]/1000.0, > > + priv->distortion_bottomY_poly); > > + } > > } else if (property == prop_pressurecurve) > > { > > INT32 *pcurve; > > @@ -1073,4 +1151,158 @@ wcmBindToSerial(InputInfoPtr pInfo, unsigned int > > serial) > > > > } > > > > +/* P A = L U > > + * P : permutation matrix > > + * L : lower matrix > > + * U : upper matrix > > +*/ > > +static void > > +lu_decomposition(int n, const double* A, double* P, double* L, double* > U) > > +{ > > + int k,i,r; > > + double m; > > + > > + // P,L = identity > > + for (i = 0; i < n*n; ++i) P[i] = L[i] = 0.0; > > + for (i = 0; i < n*n; i += n+1) P[i] = L[i] = 1.0; > > + > > + // U = A > > + for (i = 0; i < n*n; ++i) U[i] = A[i]; > > + > > + for (k = 0; k < n; ++k) { > > + // find maximum in column k > > + r = k; > > + m = U[r*n+k]; > > + for (i = k; i < n; ++i) if (U[i*n+k] > m) { > > + r = i; > > + m = U[i*n+k]; > > + } > > + > > + if (m > 0.0) { > > + // swap lines r,k > > + if (r != k) { > > + for (i = 0; i < n; ++i) { > > + m = U[r*n+i]; > > + U[r*n+i] = U[k*n+i]; > > + U[k*n+i] = m; > > + m = P[r*n+i]; > > + P[r*n+i] = P[k*n+i]; > > + P[k*n+i] = m; > > + } > > + for (i = 0; i < k; ++i) { > > + m = L[r*n+i]; > > + L[r*n+i] = L[k*n+i]; > > + L[k*n+i] = m; > > + } > > + } > > + > > + // make 0's > > + for (i = k+1; i < n; ++i) { > > + L[i*n+k] = U[i*n+k] / U[k*n+k]; > > + for (r = 0; r < n; ++r) U[i*n+r] -= L[i*n+k] * U[k*n+r]; > > + } > > + } > > + } > > +} > > + > > +/* LUx = b */ > > +static int > > +solve_lu(int n, const double* L, const double* U, const double* b, > double* > > x) > > +{ > > + int k, i; > > + double s; > > + double* y = (double*)malloc(sizeof(double)*n); > > + > > + // Ly = b > > + for (k = 0; k < n; ++k) { > > + s = 0.0; > > + for (i = 0; i < k; ++i) s += L[k*n+i] * y[i]; > > + y[k] = b[k] - s; > > + } > > + > > + // Ux = y > > + for (k = n-1; k >= 0; --k) { > > + s = 0.0; > > + for (i = n-1; i > k; --i) s += U[k*n+i] * x[i]; > > + if (U[k*n+k] == 0.0) { > > + free(y); > > + return 1; > > + } > > + x[k] = (y[k] - s) / U[k*n+k]; > > + } > > + free(y); > > + return 0; > > +} > > + > > + > > +/* Ax = b */ > > +static int > > +solve_ls(int n, const double* A, const double* b, double* x) > > +{ > > + int i,k; > > + double *P,*L,*U,*Pb; > > + P = (double*)malloc(sizeof(double)*n*n); > > + L = (double*)malloc(sizeof(double)*n*n); > > + U = (double*)malloc(sizeof(double)*n*n); > > + Pb = (double*)malloc(sizeof(double)*n); > > + lu_decomposition(n, A, P, L, U); > > + for (i = 0; i < n; ++i) { > > + Pb[i] = 0; > > + for (k = 0; k < n; ++k) { > > + Pb[i] += P[i*n+k] * b[k]; > > + } > > + } > > + i = solve_lu(n, L, U, Pb, x); > > + free(P); > > + free(L); > > + free(U); > > + free(Pb); > > + return i; > > +} > > + > > +static int > > +distortionCorrectionComputePolynomial(double p, double a, double h, > double > > d, double* poly) > > +{ > > + /* p = offset at the border > > + * (a,h) = dirtortion point > > + * d = width of the border zone > > + * > > + * poly = {c4, c3... c0} > > + */ > > + > > + /* F(x) = c4 x^4 + c3 x^3 + c2 x^2 + c1 x + c0 > > + * > > + * Constraints => Matrix > > + * F(0) = p => 0 0 0 0 1 : p > > + * F(d) = d => d^4 d^3 d^2 d 1 : d > > + * F(a) = h => a^4 a^3 a^2 a 1 : h > > + * F'(d) = 1 => 4d^3 3d^2 2d 1 0 : 1 > > + * F'(a) = 1 => 4a^3 3a^2 2a 1 0 : 1 > > + */ > > + int r; > > + > > + const double Matrix[25] = { > > + 0, 0, 0, 0, 1, > > + d*d*d*d, d*d*d, d*d, d, 1, > > + a*a*a*a, a*a*a, a*a, a, 1, > > + 4*d*d*d, 3*d*d, 2*d, 1, 0, > > + 4*a*a*a, 3*a*a, 2*a, 1, 0 > > + }; > > + const double rhs[5] = { > > + p, > > + d, > > + h, > > + 1, > > + 1 > > + }; > > + > > + r = solve_ls(5, Matrix, rhs, poly); > > + if (r != 0) { > > + // set to F(x) = x > > + poly[0] = poly[1] = poly[2] = poly[4] = 0.0; > > + poly[3] = 1.0; > > + } > > + return r; > > +} > > + > > /* vim: set noexpandtab tabstop=8 shiftwidth=8: */ > > diff --git a/src/xf86WacomDefs.h b/src/xf86WacomDefs.h > > index 1575960..45679ec 100644 > > --- a/src/xf86WacomDefs.h > > +++ b/src/xf86WacomDefs.h > > @@ -262,6 +262,16 @@ struct _WacomDeviceRec > > unsigned int cur_serial; /* current serial in prox */ > > int cur_device_id; /* current device ID in prox */ > > > > + /* distortion */ > > + double distortion_topX_border; > > + double distortion_topY_border; > > + double distortion_bottomX_border; > > + double distortion_bottomY_border; > > + double distortion_topX_poly[5]; > > + double distortion_topY_poly[5]; > > + double distortion_bottomX_poly[5]; > > + double distortion_bottomY_poly[5]; > > + > > /* button mapping information > > * > > * 'button' variables are indexed by physical button number > (0..nbuttons) > > -- > > 2.1.4 > > > > > > > ------------------------------------------------------------------------------ > > > > _______________________________________________ > > Linuxwacom-devel mailing list > > Linuxwacom-devel@lists.sourceforge.net > > https://lists.sourceforge.net/lists/listinfo/linuxwacom-devel > > >
------------------------------------------------------------------------------
_______________________________________________ Linuxwacom-devel mailing list Linuxwacom-devel@lists.sourceforge.net https://lists.sourceforge.net/lists/listinfo/linuxwacom-devel