/***********************************************************************/ /* Open Visualization Data Explorer */ /* (C) Copyright IBM Corp. 1989,1999 */ /* ALL RIGHTS RESERVED */ /* This code licensed under the */ /* "IBM PUBLIC LICENSE - Open Visualization Data Explorer" */ /***********************************************************************/ #include #include #include #ifndef TRUE #define TRUE 1 #define FALSE 0 #endif #ifndef MAXDIMS #define MAXDIMS 32 #endif #define DEFAULT_BINS 100 #define DEFAULT_BINS_C 256 #define TASK_GROUP(_f,_d,_n)\ {\ if (_n > 0)\ {\ if (! DXCreateTaskGroup ())\ goto cleanup;\ if (! DXAddLikeTasks ((PFE) _f, (Pointer) &(_d), sizeof (_d), 1.0, _n))\ {\ DXAbortTaskGroup ();\ goto cleanup;\ }\ if (! DXExecuteTaskGroup ())\ goto cleanup;\ }\ } typedef Error (*PFE)(); typedef struct EQData *EQDP; typedef struct EQData { Object obj; /* copy of object to equalize */ int nbin; /* number of bins or 0 */ int minset; /* min has been set */ int maxset; /* max has been set */ float minv; /* min value */ float maxv; /* max value */ /* transfer functions */ EQDP resolved; /* where numbers were resolved */ Object ito; /* input histogram */ Object oto; /* output histogram */ char *itf; /* input named function */ char *otf; /* output named function */ Pointer base; /* handle to allocated space */ float *idpd; /* input delta prob. dist. */ float *icpd; /* input cumm. prob. dist. */ float *odpd; /* input delta prob. dist. */ float *ocpd; /* input cumm. prob. dist. */ } EQData; Object _dxfHistogram (Object o, int bins, float *min, float *max, int inout); static Error ComputeCPD (int nbin, float *del, float *cum, int invert); static Error Equalize (EQData *eq, Type type, int n, Pointer src, Pointer dst); static Error EqualizeObject (EQData *eq); static Error EqualizeField (EQData *eq); static Error EqualizeGroup (EQData *eq); static Error FuncDistribution (char *f, int nbin, float *pd); static Error HistDistribution (Object o, int nbin, float *pd); static int IsAHistogram (Object obj); static void Resample (int srcn, int *src, int dstn, float *dst); static Error ResolveTransferFunctions (EQData *eq); static Error SimpleArgs (Object *in, EQData *eq); static Error TransferFunctions (Object *in, EQData *eq); static Error WorkerEG (EQData *eq, int n); /* * Equalize (input, nbins, min, max, ihist, ohist) */ #define Input in[0] #define NBin in[1] #define Min in[2] #define Max in[3] #define IDist in[4] #define ODist in[5] Error m_Equalize (Object *in, Object *out) { Error ret = ERROR; EQData eq; memset (&eq, NULL, sizeof (eq)); if (! SimpleArgs (in, &eq)) goto cleanup; if (! TransferFunctions (in, &eq)) goto cleanup; eq.obj = DXCopy (Input, COPY_STRUCTURE); if (! eq.obj) goto cleanup; if (! EqualizeObject (&eq)) goto cleanup; out[0] = eq.obj; eq.obj = NULL; ret = OK; cleanup: if (eq.obj != Input) DXDelete (eq.obj); DXDelete (eq.ito); DXDelete (eq.oto); return (ret); } static Error SimpleArgs (Object *in, EQData *eq) { Error ret = ERROR; if (! Input) { DXSetError (ERROR_BAD_PARAMETER, "#10000", "input"); goto cleanup; } if (NBin) { if ((! DXExtractInteger (NBin, &eq->nbin)) || (eq->nbin < 0)) { DXSetError (ERROR_BAD_PARAMETER, "#10030", "number of bins"); goto cleanup; } } if (Min) { if (! DXExtractFloat (Min, &eq->minv)) { if (! DXStatistics (Min, "data", &eq->minv, &eq->maxv, NULL, NULL)) { DXSetError (ERROR_BAD_PARAMETER, "#10080", "bin mininum"); goto cleanup; } if (! Max) eq->maxset = TRUE; } eq->minset = TRUE; } if (Max) { if (! DXExtractFloat (Max, &eq->maxv) && ! DXStatistics (Max, "data", NULL, &eq->maxv, NULL, NULL)) { DXSetError (ERROR_BAD_PARAMETER, "#10080", "bin maximum"); goto cleanup; } eq->maxset = TRUE; } if (eq->minset && eq->maxset && (eq->minv >= eq->maxv)) { DXSetError (ERROR_BAD_PARAMETER, "#11220", "min", "or equal to max"); goto cleanup; } ret = OK; cleanup: return (ret); } static Error TransferFunctions (Object *in, EQData *eq) { Error ret = ERROR; Object ihist = NULL; float *minp = NULL; float *maxp = NULL; char *c; /* * Compute the input transfer function. If no input distribution was * specified then just histogram the input for now. Otherwise, it may * be one of several things: * $$$$$ 1. A string specifying the desired function * 2. A previously computed histogram * $$$$$ 3. An object whose histogram we will take */ if (eq->minset) minp = &eq->minv; if (eq->maxset) maxp = &eq->maxv; if (IDist) { if (IsAHistogram (IDist)) eq->ito = DXReference (IDist); else if (DXExtractString (IDist, &c)) eq->itf = c; else { DXSetError (ERROR_BAD_PARAMETER, "#10370", "ihist", "a histogram"); goto cleanup; } } else { ihist = _dxfHistogram (Input, eq->nbin, minp, maxp, FALSE); if (! ihist) goto cleanup; eq->ito = DXReference (ihist); ihist = NULL; } /* * Compute the output transfer function. A similar set of thing apply * here. */ if (ODist) { if (IsAHistogram (ODist)) eq->oto = DXReference (ODist); else if (DXExtractString (ODist, &c)) eq->otf = c; else { DXSetError (ERROR_BAD_PARAMETER, "#10370", "ohist", "a histogram"); goto cleanup; } } ret = OK; cleanup: DXDelete (ihist); return (ret); } /* * Checks to make sure that this looks like something produced by * the Histogram module. */ static int IsAHistogram (Object obj) { Array ad; Array ac; Array ap; Class c; int n; Object attr; char *str; if (obj == NULL) return (FALSE); if (DXGetObjectClass (obj) != CLASS_FIELD) return (FALSE); ad = (Array) DXGetComponentValue ((Field) obj, "data"); ac = (Array) DXGetComponentValue ((Field) obj, "connections"); ap = (Array) DXGetComponentValue ((Field) obj, "positions"); if (! ad || ! ac || ! ap) return (FALSE); if (! DXTypeCheckV (ad, TYPE_INT, CATEGORY_REAL, 0, NULL)) return (FALSE); /* * Make sure that the connections are a 1D entity. Unfortunately Construct * wraps the simple path array in a 1D mesh array. */ c = DXGetArrayClass (ac); if (c != CLASS_PATHARRAY && c != CLASS_MESHARRAY) return (FALSE); if (c == CLASS_MESHARRAY) { if (! DXQueryGridConnections (ac, &n, NULL) || n != 1) return (FALSE); } /* * Make sure that the positions are a 1D entity. Unfortunately Construct * wraps the simple regular array in a 1D product array. */ c = DXGetArrayClass (ap); if (c != CLASS_REGULARARRAY && c != CLASS_PRODUCTARRAY) return (FALSE); if (c == CLASS_PRODUCTARRAY) { if (! DXQueryGridPositions (ap, &n, NULL, NULL, NULL) || n != 1) return (FALSE); } attr = DXGetAttribute ((Object) ac, "element type"); if (attr == NULL || ! DXExtractString (attr, &str) || strcmp (str, "lines")) return (FALSE); attr = DXGetAttribute ((Object) ad, "dep"); if (attr == NULL || ! DXExtractString (attr, &str) || strcmp (str, "connections")) return (FALSE); /* * Could also check that: * ac ref positions */ return (TRUE); } static Error EqualizeObject (EQData *eq) { Object kid; EQData leq; switch (DXGetObjectClass (eq->obj)) { case CLASS_FIELD: return (EqualizeField (eq)); case CLASS_GROUP: return (EqualizeGroup (eq)); case CLASS_XFORM: if (! DXGetXformInfo ((Xform) eq->obj, &kid, NULL)) return (ERROR); leq = *eq; leq.obj = kid; return (EqualizeObject (&leq)); case CLASS_CLIPPED: if (! DXGetClippedInfo ((Clipped) eq->obj, &kid, NULL)) return (ERROR); leq = *eq; leq.obj = kid; return (EqualizeObject (&leq)); default: DXSetError (ERROR_BAD_PARAMETER, "#10190", "input"); return (ERROR); } } static Error WorkerEG (EQData *eq, int n) { EQData leq; leq = *eq; leq.obj = DXGetEnumeratedMember ((Group) leq.obj, n, NULL); if (leq.ito && DXGetObjectClass (leq.ito) != CLASS_FIELD) leq.ito = DXGetEnumeratedMember ((Group) leq.ito, n, NULL); return (EqualizeObject (&leq)); } static Error EqualizeGroup (EQData *eq) { EQData leq; int n; Class c; Error ret = ERROR; leq = *eq; for (n = 0; DXGetEnumeratedMember ((Group) leq.obj, n, NULL); n++) continue; c = DXGetGroupClass ((Group) leq.obj); if (c == CLASS_COMPOSITEFIELD || c == CLASS_SERIES) if (! ResolveTransferFunctions (&leq)) goto cleanup; TASK_GROUP (WorkerEG, leq, n); ret = OK; cleanup: if (&leq == leq.resolved) DXFree (leq.base); return (ret); } static Error EqualizeField (EQData *eq) { Error ret = ERROR; EQData leq; Field f; Array srca; Array dsta = NULL; int items; Type type; Category cat; int rank; int shape[MAXDIMS]; Pointer src; Pointer dst; f = (Field) eq->obj; if (DXEmptyField (f)) return (OK); leq = *eq; if (! ResolveTransferFunctions (&leq)) goto cleanup; srca = (Array) DXGetComponentValue (f, "data"); if (! srca) { DXSetError (ERROR_BAD_PARAMETER, "#10240", "data"); goto cleanup; } if (! DXGetArrayInfo (srca, &items, &type, &cat, &rank, shape)) goto cleanup; if (cat != CATEGORY_REAL) { DXSetError (ERROR_BAD_PARAMETER, "#11150", "data"); goto cleanup; } if (! (rank == 0 || rank == 1 && shape[0] == 1)) { DXSetError (ERROR_BAD_PARAMETER, "#10370", "data", "rank 0 or rank 1 shape 1"); goto cleanup; } dsta = DXNewArrayV (type, cat, rank, shape); if (! dsta) goto cleanup; if (! DXAddArrayData (dsta, 0, items, NULL)) goto cleanup; src = DXGetArrayData (srca); dst = DXGetArrayData (dsta); if (src == NULL || dst == NULL) goto cleanup; if (! Equalize (&leq, type, items, src, dst)) goto cleanup; if (! DXCopyAttributes ((Object) dsta, (Object) srca)) goto cleanup; if (! DXSetComponentValue (f, "data", (Object) dsta)) goto cleanup; dsta = NULL; if (! DXChangedComponentValues (f, "data")) goto cleanup; if (! DXEndField (f)) goto cleanup; ret = OK; cleanup: if (&leq == leq.resolved) DXFree (leq.base); DXDelete ((Object) dsta); return (ret); } static Error ResolveTransferFunctions (EQData *eq) { Error ret = ERROR; float minv; float maxv; Type type; int nbin; int size; Pointer base = NULL; int output; float *idpd = NULL; /* input delta prob. dist. */ float *icpd = NULL; /* input cumm. prob. dist. */ float *odpd = NULL; /* output delta prob. dist. */ float *ocpd = NULL; /* output cumm. prob. dist. */ if (eq->resolved != NULL) return (OK); /* * Make sure that we have set the min and max values. */ if (! eq->maxset || ! eq->minset) { if (! DXStatistics (eq->obj, "data", &minv, &maxv, NULL, NULL)) goto cleanup; if (! eq->minset) eq->minv = minv; if (! eq->maxset) eq->maxv = maxv; eq->minset = TRUE; eq->maxset = TRUE; } minv = eq->minv; maxv = eq->maxv; /* * Make sure that the number of bins is set. */ if (eq->nbin == 0) { if (! DXGetType (eq->obj, &type, NULL, NULL, NULL)) goto cleanup; if (type == TYPE_UBYTE) eq->nbin = (int) maxv - (int) minv + 1; else eq->nbin = DEFAULT_BINS; } nbin = eq->nbin; /* * Allocate enough space to compute the probability distributions, * both delta and cummulative, for the input and output. */ output = eq->oto || eq->otf; size = output ? 4 : 2; size *= sizeof (float); size *= nbin; base = DXAllocate (size); if (! base) goto cleanup; eq->idpd = idpd = (float *) base; eq->icpd = icpd = idpd + nbin; if (output) { eq->odpd = odpd = icpd + nbin; eq->ocpd = ocpd = odpd + nbin; } /* * Extract/Compute the input and output functions. If we are extracting * the we may also have to resample it so that it fits into the right * number of bins. */ if (eq->ito && ! HistDistribution (eq->ito, nbin, idpd)) goto cleanup; if (eq->itf && ! FuncDistribution (eq->itf, nbin, idpd)) goto cleanup; if (! ComputeCPD (nbin, idpd, icpd, FALSE)) goto cleanup; if (output) { if (eq->oto && ! HistDistribution (eq->oto, nbin, odpd)) goto cleanup; if (eq->otf && ! FuncDistribution (eq->otf, nbin, odpd)) goto cleanup; if (! ComputeCPD (nbin, odpd, ocpd, TRUE)) goto cleanup; } eq->resolved = eq; eq->base = base; base = NULL; ret = OK; cleanup: DXFree (base); return (ret); } static Error HistDistribution (Object o, int nbin, float *pd) { Field f; Error ret = ERROR; Array a; int items; Type type; Category cat; int i; int rank; int shape[MAXDIMS]; int *data; if (DXGetObjectClass (o) != CLASS_FIELD) { DXSetError (ERROR_BAD_PARAMETER, "#10370", "histogram", "a field"); goto cleanup; } f = (Field) o; a = (Array) DXGetComponentValue (f, "data"); if (! a) { DXSetError (ERROR_BAD_PARAMETER, "#10240", "data"); goto cleanup; } if (! DXGetArrayInfo (a, &items, &type, &cat, &rank, shape)) goto cleanup; if ((cat != CATEGORY_REAL) || (! (rank == 0 || rank == 1 && shape[0] == 1))) { DXSetError (ERROR_BAD_PARAMETER, "#10370", "histogram", "a real integer field"); goto cleanup; } data = (int *) DXGetArrayData (a); if (! data) goto cleanup; if (items == nbin) { for (i=0; i minv && val < maxv)\ {\ bin = (int) ((val - minv) * ideltab);\ bdist = (val - ibase[bin]) * ideltab;\ norm = icpd[bin] + idpd[bin] * bdist;\ val = minv + norm * deltav;\ } #define ILOOP(_type)\ {\ register _type *s = (_type *) src;\ register _type *d = (_type *) dst;\ for (i = 0; i < n; i++)\ {\ val = (float) *s++;\ IVAL ();\ *d++ = val;\ }\ } #define ILOOPK(_type,_n,_d)\ {\ register _type *s = (_type *) src;\ register _type *d = (_type *) dst;\ _type lookup[_n];\ for (i = 0; i < _n; i++)\ {\ val = (float) i + _d;\ IVAL ();\ lookup[i] = (_type) val;\ }\ for (i = 0; i < n; i++)\ *d++ = lookup[(int) *s++];\ } /* * 1. map from value to 0.0 - 1.0 via the input function * 2. map from normalized value to new normalized value via inverse of output function * 3. map from normalized value to minv - maxv range */ #define IOVAL()\ if (val > minv && val < maxv)\ {\ bin = (int) ((val - minv) * ideltab);\ bdist = (val - ibase[bin]) * ideltab;\ norm = icpd[bin] + idpd[bin] * bdist;\ \ if (norm <= maxnorm)\ {\ for (bin = lbin = 0, hbin = nbin - 1; lbin < hbin; )\ {\ bin = (lbin + hbin) >> 1;\ if (norm < ocpd[bin])\ {\ hbin = bin;\ continue;\ }\ if (norm <= ocpd[bin] + odpd[bin])\ break;\ lbin = bin + 1;\ }\ bdist = (norm - ocpd[bin]) * obase[bin];\ norm = ((float) bin + bdist) * odeltab;\ }\ \ val = minv + norm * deltav;\ } #define IOLOOP(_type)\ {\ register _type *s = (_type *) src;\ register _type *d = (_type *) dst;\ for (i = 0; i < n; i++)\ {\ val = (float) *s++;\ IOVAL ();\ *d++ = val;\ }\ } #define IOLOOPK(_type,_n,_d)\ {\ register _type *s = (_type *) src;\ register _type *d = (_type *) dst;\ _type lookup[_n];\ for (i = 0; i < _n; i++)\ {\ val = (float) i + _d;\ IOVAL ();\ lookup[i] = (_type) val;\ }\ for (i = 0; i < n; i++)\ *d++ = lookup[(int) *s++];\ } static Error Equalize (EQData *eq, Type type, int n, Pointer src, Pointer dst) { Error ret = ERROR; register int i; int output = eq->oto || eq->otf; register int nbin = eq->nbin; register float minv = eq->minv; register float maxv = eq->maxv; register float val; register int bin; register float bdist; register float norm; register float deltav = maxv - minv; register float deltab = deltav / nbin; register float ideltab; register float odeltab = (float) 1.0 / nbin; float *base = NULL; register float *ibase; register float *obase; register float *idpd; register float *icpd; register float *odpd; register float *ocpd; float *fp; int size; if (deltab == (float) 0.0) { size = n * DXTypeSize (type); memcpy (dst, src, size); ret = OK; goto cleanup; } ideltab = (float) 1.0 / deltab; /* * If we are on the PVS, or any other NUMA architecture for that matter, * then we want to copy the things that we will be using a lot, such as * the probability distributions into local memory. This is particularly * important on the PVS since without doing so we will totally abuse * the line cache. If we don't succeed then too bad for the cache * but at least we'll run. */ #if DXD_HAS_LOCAL_MEMORY size = nbin; size *= output ? 6 : 3; size *= sizeof (float); base = (float *) DXAllocateLocal (size); if (base) { ibase = base; idpd = ibase + nbin; icpd = idpd + nbin; obase = icpd + nbin; odpd = obase + nbin; ocpd = odpd + nbin; size = nbin * sizeof (float); memcpy (idpd, eq->idpd, size); memcpy (icpd, eq->icpd, size); if (output) { memcpy (odpd, eq->odpd, size); memcpy (ocpd, eq->ocpd, size); } } else #endif { DXResetError (); size = nbin; size *= output ? 2 : 1; size *= sizeof (float); base = (float *) DXAllocate (size); if (! base) goto cleanup; ibase = base; obase = ibase + nbin; idpd = eq->idpd; icpd = eq->icpd; odpd = eq->odpd; ocpd = eq->ocpd; } for (i = 0, fp = ibase; i < nbin; i++) *fp++ = minv + i * deltab; if (! output) { switch (type) { case TYPE_BYTE: ILOOPK (byte,256,-128); break; case TYPE_UBYTE: ILOOPK (ubyte,256,0); break; case TYPE_USHORT: ILOOP (ushort); break; case TYPE_UINT: ILOOP (uint); break; case TYPE_SHORT: ILOOP (short); break; case TYPE_INT: ILOOP (int); break; case TYPE_FLOAT: ILOOP (float); break; case TYPE_DOUBLE: ILOOP (double); break; default: DXSetError (ERROR_BAD_PARAMETER, "#10320", ""); goto cleanup; } } else { register int lbin, hbin; register float maxnorm = ocpd[nbin - 1] + odpd[nbin - 1]; for (i = 0, fp = obase; i < nbin; i++) { *fp++ = odpd[i] == (float) 0.0 ? (float) 0.0 : (float) 1.0 / odpd[i]; } switch (type) { case TYPE_BYTE: IOLOOPK (byte,256,-128);break; case TYPE_UBYTE: IOLOOPK (ubyte,256,0); break; case TYPE_USHORT: IOLOOP (ushort); break; case TYPE_UINT: IOLOOP (uint); break; case TYPE_SHORT: IOLOOP (short); break; case TYPE_INT: IOLOOP (int); break; case TYPE_FLOAT: IOLOOP (float); break; case TYPE_DOUBLE: IOLOOP (double); break; default: DXSetError (ERROR_BAD_PARAMETER, "#10320", ""); goto cleanup; } } ret = OK; cleanup: DXFree ((Pointer) base); return (ret); }