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

Reply via email to