FaceGen 3 SDKs Reference
Loading...
Searching...
No Matches
fimImgOps.cpp
1//
2// Copyright (c) Singular Inversions Inc. 2001.
3//
4// Authors: Andrew Beatty
5// Created: June 14, 2001.
6//
7
8#include "stdafx.h"
9
10#include "matrixCT.hpp"
11#include "fimImgOps.hpp"
12#include "FgApproxFunc.hpp"
13#include "FgRgba.hpp"
14
15using namespace std;
16
17namespace Fg {
18
19// return PACS bounds of source image area to be sampled for the given IRCS destination pixel
20Vec2F getBoundsPacs(
21 uint ii, // Destination pixel
22 float ss, // Scale of source wrt dest.
23 float pp) // RCS lower edge of source region.
24{
25 if (ss > 1) { // Use inverse projected pixel rectangle for sample area.
26 float lo = (float)ii * ss + pp + 0.5f;
27 return {lo, lo+ss};
28 }
29 else { // Minimum sample area size 1 source pixel (equivalent to bilinear).
30 float lo = ((float)ii + 0.5f) * ss + pp;
31 return {lo, lo+1};
32 }
33}
34
35// returns the weight of the bounds tile over the given raster pos bin (0,1]
36float weightFn(int rasterPos,Vec2F boundsPacs,Vec2I boundFloors)
37{
38 if (rasterPos == boundFloors[0]) {
39 if (rasterPos == boundFloors[1])
40 return boundsPacs[1] - boundsPacs[0];
41 else
42 return scast<float>(boundFloors[0] + 1) - boundsPacs[0];
43 }
44 else if (rasterPos == boundFloors[1])
45 return boundsPacs[1] - scast<float>(boundFloors[1]);
46 else
47 return 1.0f;
48}
49
50// Cuts out the floating-point defined region of an image and scales separately in X and Y to fit the defined pixel size.
51// * The defined region can extend outside the source image, in which case the pixel values are interpreted to be
52// zero with zero alpha.
53// * Resizing for a shrink is by proportional area and alpha. Resizing for a stretch is by linear interpolation of values and alpha.
54// * The two methods are identical when the scale factor is 1.
55void fimCutResizeRcs(
56 FutVect2FC a_lo, // Low corner of source area (RCS)
57 FutVect2FC a_hi, // High corner of source area (RCS)
58 uint a_w, // Width of destination image
59 uint a_h, // Height of destination image
60 const FimImgRgbaUbC &srcImg, // The source image
61 FimImgRgbaUbC &dstImg) // The destination image
62{
63 FGASSERT(srcImg.imageAllocated());
64 FGASSERT(a_hi.x1 > a_lo.x1); // Ensure non-zero source area.
65 FGASSERT(a_hi.x2 > a_lo.x2);
66 FGASSERT(a_w > 0); // Ensure non-zero destination area.
67 FGASSERT(a_h > 0);
68 dstImg.resize(a_w,a_h); // Only re-allocates if necessary.
69 Vec2I srcDims {scast<int>(srcImg.width()),scast<int>(srcImg.height())};
70 Rgba8 *dstRowPtr = reinterpret_cast<Rgba8*>(dstImg.imageData());
71 Vec2F invScale {
72 (a_hi.x1 - a_lo.x1) / scast<float>(a_w),
73 (a_hi.x2 - a_lo.x2) / scast<float>(a_h),
74 };
75 for (uint yy=0; yy<a_h; yy++) {
76 Rgba8 *dPtr = dstRowPtr;
77 Vec2F boundsPacsY = getBoundsPacs(yy,invScale[1],a_lo.x2);
78 Vec2I boundFloorsY {mapFloor(boundsPacsY)}; // (lo,hi)
79 for (uint xx=0; xx<a_w; xx++) {
80 Vec2F boundsPacsX = getBoundsPacs(xx,invScale[0],a_lo.x1);
81 Vec2I boundFloorsX {mapFloor(boundsPacsX)};
82 RgbaF acc {0};
83 float accW = 0;
84 for (int yyy=boundFloorsY[0]; yyy<=boundFloorsY[1]; yyy++) {
85 float weightY = weightFn(yyy,boundsPacsY,boundFloorsY);
86 for (int xxx=boundFloorsX[0]; xxx<=boundFloorsX[1]; xxx++) {
87 float weightX = weightFn(xxx,boundsPacsX,boundFloorsX);
88 float currWgt = weightX * weightY;
89 accW += currWgt;
90 if ((xxx >= 0) && (xxx < srcDims[0]) && (yyy >= 0) && (yyy < srcDims[1])) {
91 Rgba8 const *sPtr = reinterpret_cast<Rgba8 const *>(srcImg.imageData()) + yyy * srcImg.width() + xxx;
92 RgbaF curr {*sPtr};
93 curr[3] *= currWgt;
94 acc[0] += curr[0] * curr[3];
95 acc[1] += curr[1] * curr[3];
96 acc[2] += curr[2] * curr[3];
97 acc[3] += curr[3];
98 }
99 }
100 }
101 if (acc[3] == 0.0f)
102 *(dPtr++) = Rgba8{0};
103 else {
104 *(dPtr++) = {
105 (uchar)(acc[0] / acc[3] + 0.5f),
106 (uchar)(acc[1] / acc[3] + 0.5f),
107 (uchar)(acc[2] / acc[3] + 0.5f),
108 (uchar)(acc[3] / accW + 0.5f),
109 };
110 }
111 }
112 dstRowPtr += dstImg.width();
113 }
114}
115
116static
117uint // RETURNED: The weight is set to 0 if out of bounds.
118crop(uint hiBound,int val,float & wgt)
119{
120 if (val < 0) {
121 val = 0;
122 wgt = 0.0f;
123 }
124 else if (val > static_cast<int>(hiBound)) {
125 val = hiBound;
126 wgt = 0.0f;
127 }
128 return (uint)val;
129}
130
131FimRgbaFC
132fimInterpFloatOics(
133 const FimImgRgbaUbC &img,
134 float x, // OICS X coordinate
135 float y) // OICS Y coordinate
136{
137 uint ncols = img.width();
138 uint nrows = img.height();
139 FGASSERT_FAST(img.imageAllocated());
140 FGASSERT_FAST(ncols > 0);
141 FGASSERT_FAST(nrows > 0);
142 FutVect2FC posRcs;
143 posRcs.x1 = (x + 1.0f) * img.width() * 0.5f - 0.5f;
144 posRcs.x2 = (1.0f - y) * img.height() * 0.5f - 0.5f;
145 float xf = std::floor(posRcs.x1),
146 yf = std::floor(posRcs.x2),
147 xh = posRcs.x1 - xf,
148 yh = posRcs.x2 - yf,
149 xl = 1.0f - xh,
150 yl = 1.0f - yh;
151 int yfi = int(yf),
152 xfi = int(xf);
153 uint rowLo = crop(nrows-1,yfi,yl),
154 rowHi = crop(nrows-1,yfi+1,yh),
155 colLo = crop(ncols-1,xfi,xl),
156 colHi = crop(ncols-1,xfi+1,xh);
157 FimRgbaFC retval,
158 pixLL,pixHL,pixLH,pixHH;
159 futConvert(img[rowLo][colLo],pixLL);
160 futConvert(img[rowHi][colLo],pixHL);
161 futConvert(img[rowLo][colHi],pixLH);
162 futConvert(img[rowHi][colHi],pixHH);
163 retval = pixLL * yl * xl +
164 pixHL * yh * xl +
165 pixLH * yl * xh +
166 pixHH * yh * xh;
167 return retval;
168}
169
170FimRgbaFC
171fimInterpFloatOics(
172 const FimImgRgbaFC &img,
173 float x, // OICS X coordinate
174 float y) // OICS Y coordinate
175{
176 uint ncols = img.width();
177 uint nrows = img.height();
178 FGASSERT_FAST(img.imageAllocated());
179 FGASSERT_FAST(ncols > 0);
180 FGASSERT_FAST(nrows > 0);
181 FutVect2FC posRcs;
182 posRcs.x1 = (x + 1.0f) * img.width() * 0.5f - 0.5f;
183 posRcs.x2 = (1.0f - y) * img.height() * 0.5f - 0.5f;
184 int rowFloor = futFloor(posRcs.x2),
185 colFloor = futFloor(posRcs.x1);
186 float rh = posRcs.x2 - rowFloor;
187 float ch = posRcs.x1 - colFloor;
188 float rl = 1.0f - rh;
189 float cl = 1.0f - ch;
190 uint rowLo = crop(nrows-1,rowFloor,rl),
191 rowHi = crop(nrows-1,rowFloor+1,rh),
192 colLo = crop(ncols-1,colFloor,cl),
193 colHi = crop(ncols-1,colFloor+1,ch);
194 FimRgbaFC ret =
195 img[rowLo][colLo] * rl * cl +
196 img[rowHi][colLo] * rh * cl +
197 img[rowLo][colHi] * rl * ch +
198 img[rowHi][colHi] * rh * ch;
199 return ret;
200}
201
202//**************************************************************************
203// fimShrink2
204//**************************************************************************
205//
206// Shrink an image by 2x2 averaging blocks, rounding down the image size.
207// Uses zero-weighted alpha convention. Image dimensions not divisible
208// by 2 are rounded down.
209//
210// Cut and paste if you need another since MSVC doesn't support partial
211// specialization.
212//
213void fimShrink2(
214
215 const FimImgRgbaUbC& src,
216 FimImgRgbaUbC& dst)
217{
218 FGASSERT(src.imageAllocated());
219
220 uint dstWid = src.width() / 2,
221 dstHgt = src.height() / 2;
222 dst.resize(dstWid,dstHgt);
223
224 uint srcStep = src.widthStepType();
225 uint dstStep = dst.widthStepType();
226 const FimRgbaUbC *srcPtr1 = src.imageData(),
227 *srcPtr2 = srcPtr1 + srcStep;
228 FimRgbaUbC *dstPtr = dst.imageData();
229 uint xd,yd,xs1,xs2;
230
231 for (yd=0; yd<dst.height(); yd++)
232 {
233 for (xd=0; xd<dst.width(); xd++)
234 {
235 xs1 = xd * 2;
236 xs2 = xs1+1;
237
238 dstPtr[xd].c[FIMRGBA_R] = (uchar)(
239 ((uint)srcPtr1[xs1].c[FIMRGBA_R] + (uint)srcPtr1[xs2].c[FIMRGBA_R] +
240 (uint)srcPtr2[xs1].c[FIMRGBA_R] + (uint)srcPtr2[xs2].c[FIMRGBA_R]) >> 2);
241 dstPtr[xd].c[FIMRGBA_G] = (uchar)(
242 ((uint)srcPtr1[xs1].c[FIMRGBA_G] + (uint)srcPtr1[xs2].c[FIMRGBA_G] +
243 (uint)srcPtr2[xs1].c[FIMRGBA_G] + (uint)srcPtr2[xs2].c[FIMRGBA_G]) >> 2);
244 dstPtr[xd].c[FIMRGBA_B] = (uchar)(
245 ((uint)srcPtr1[xs1].c[FIMRGBA_B] + (uint)srcPtr1[xs2].c[FIMRGBA_B] +
246 (uint)srcPtr2[xs1].c[FIMRGBA_B] + (uint)srcPtr2[xs2].c[FIMRGBA_B]) >> 2);
247 dstPtr[xd].c[FIMRGBA_A] = (uchar)(
248 ((uint)srcPtr1[xs1].c[FIMRGBA_A] + (uint)srcPtr1[xs2].c[FIMRGBA_A] +
249 (uint)srcPtr2[xs1].c[FIMRGBA_A] + (uint)srcPtr2[xs2].c[FIMRGBA_A]) >> 2);
250 }
251 dstPtr += dstStep;
252 srcPtr1 += 2 * srcStep;
253 srcPtr2 += 2 * srcStep;
254 }
255}
256
257void fimShrink2(
258
259 const FimImgRgbaFC& src,
260 FimImgRgbaFC& dst)
261{
262 FGASSERT(src.imageAllocated());
263
264 uint dstWid = src.width() / 2,
265 dstHgt = src.height() / 2;
266 dst.resize(dstWid,dstHgt);
267
268 uint srcStep = src.widthStepType();
269 uint dstStep = dst.widthStepType();
270 const FimRgbaFC *srcPtr1 = src.imageData(),
271 *srcPtr2 = srcPtr1 + srcStep;
272 FimRgbaFC *dstPtr = dst.imageData();
273 uint xd,yd,xs1,xs2;
274
275 for (yd=0; yd<dst.height(); yd++)
276 {
277 for (xd=0; xd<dst.width(); xd++)
278 {
279 xs1 = xd * 2;
280 xs2 = xs1+1;
281
282 dstPtr[xd].c[FIMRGBA_R] =
283 (srcPtr1[xs1].c[FIMRGBA_R] + srcPtr1[xs2].c[FIMRGBA_R] +
284 srcPtr2[xs1].c[FIMRGBA_R] + srcPtr2[xs2].c[FIMRGBA_R]) * 0.25f;
285 dstPtr[xd].c[FIMRGBA_G] =
286 (srcPtr1[xs1].c[FIMRGBA_G] + srcPtr1[xs2].c[FIMRGBA_G] +
287 srcPtr2[xs1].c[FIMRGBA_G] + srcPtr2[xs2].c[FIMRGBA_G]) * 0.25f;
288 dstPtr[xd].c[FIMRGBA_B] =
289 (srcPtr1[xs1].c[FIMRGBA_B] + srcPtr1[xs2].c[FIMRGBA_B] +
290 srcPtr2[xs1].c[FIMRGBA_B] + srcPtr2[xs2].c[FIMRGBA_B]) * 0.25f;
291 dstPtr[xd].c[FIMRGBA_A] =
292 (srcPtr1[xs1].c[FIMRGBA_A] + srcPtr1[xs2].c[FIMRGBA_A] +
293 srcPtr2[xs1].c[FIMRGBA_A] + srcPtr2[xs2].c[FIMRGBA_A]) * 0.25f;
294 }
295 dstPtr += dstStep;
296 srcPtr1 += 2 * srcStep;
297 srcPtr2 += 2 * srcStep;
298 }
299}
300
301//**********************************************************************************************
302// fgImgShrinkInt
303//**********************************************************************************************
304// Note that I tried expanding out the pixel type abstraction to see if I could speed it up
305// but it made no difference, MSVC8 is smart enough to optimize that out. I also tried making
306// 'buffImg' static to avoid reallocation but this made no timing difference in the PhotoFit.
307// If the source image size is not an integer multiple of the shrink factor, modulus pixels
308// are truncated.
309//
310void fgImgShrinkInt(const FimImgRgbaUbC& src,FimImgRgbaUbC& dst,uint factor)
311{
312 if (factor == 1) {dst = src; return; }
313 dst.resize(src.width()/factor,src.height()/factor);
314 FimImgRgbaUsC buffImg(1,dst.width());
315 ushort divisor = ushort(factor * factor);
316 FimRgbaUsC offset = FimRgbaUsC(divisor/2-1);
317 for (uint rowDst=0; rowDst<dst.height(); rowDst++)
318 {
319 buffImg.fill(FimRgbaUsC(0));
320 FimRgbaUsC *buffPtr;
321 for (uint rowSrc=0; rowSrc<factor; rowSrc++)
322 {
323 buffPtr = buffImg.imageData();
324 const FimRgbaUbC *srcPtr = &src[rowDst*factor+rowSrc][0];
325 for (uint colDst=0; colDst<dst.width(); colDst++)
326 {
327 for (uint colSrc=0; colSrc<factor; colSrc++)
328 {
329 FimRgbaUsC tmp = FimRgbaUsC(*srcPtr++);
330 *buffPtr += tmp;
331 }
332 buffPtr++;
333 }
334 }
335 buffPtr = buffImg.imageData();
336 FimRgbaUbC *dstPtr = &dst[rowDst][0];
337 for (uint colDst=0; colDst<dst.width(); colDst++)
338 *dstPtr++ = FimRgbaUbC(((*buffPtr++)+offset)/divisor);
339 }
340};
341
342//**********************************************************************************************
343//**********************************************************************************************
344// Multiply-accumulate-convert - converts an input image to the output
345// image pixel type, multiplies by a scaling factor, and accumulates to the
346// output image.
347//
348void fimMultAccConvRoi(
349
350 int val, // Multiplication factor
351 const FimImgT<schar> &imgInp, // Input image.
352 FimImgT<int> &imgOut, // Accumulated image
353 uint offx, // Output offset
354 uint offy)
355{
356 FGASSERT(imgInp.imageAllocated());
357 uint widIn = imgInp.width(),
358 hgtIn = imgInp.height(),
359 wid = imgOut.width(),
360 hgt = imgOut.height();
361 FGASSERT((widIn+offx < wid) && (hgtIn+offy < hgt));
362
363 const schar *img1Ptr = imgInp.imageData();
364 int *img2Ptr = &imgOut[offy][offx];
365 uint img1step = imgInp.widthStepType(),
366 img2Step = imgOut.widthStepType();
367
368 for (uint yy=0; yy<hgtIn; yy++)
369 {
370 for (uint xx=0; xx<widIn; xx++)
371 img2Ptr[xx] += val * int(img1Ptr[xx]);
372 img1Ptr += img1step;
373 img2Ptr += img2Step;
374 }
375}
376
377// ****************************************************************************
378// ****************************************************************************
379
380// Removes alpha-weighting from the colour values (according to the alpha channel, then
381// applies gamma THEN affine parameters, then adds alpha-weighting back in.
382
383struct Zga
384{
385 Zga(
386 float gamma,
387 float black,
388 float gain)
389 :
390 m_gamma(gamma), m_black(black/255.0f), m_gain(gain)
391 {}
392
393 float
394 operator()(float val)
395 {
396 float ftmp = std::pow(val,m_gamma) * m_gain - m_black;
397 if (ftmp < 0.0f) ftmp = 0.0f;
398 if (ftmp > 1.0f) ftmp = 1.0f;
399 return ftmp;
400 }
401
402 float m_gamma;
403 float m_black;
404 float m_gain;
405};
406
407struct ZgaOp
408{
409 explicit
410 ZgaOp(FgApproxFunc<float> af)
411 : m_af(af)
412 {}
413
414 void
415 opBinary(
416 const FimRgbaFC & src,
417 FimRgbaFC & dst)
418 {
419 if (src.c[FIMRGBA_A] > 0.0f)
420 {
421 float alpha = src.c[FIMRGBA_A],
422 ftmp;
423 ftmp = m_af(src.c[FIMRGBA_R] / alpha) * alpha;
424 if (ftmp > 255.0f) ftmp = 255.0f;
425 dst.c[FIMRGBA_R] = ftmp;
426 ftmp = m_af(src.c[FIMRGBA_G] / alpha) * alpha;
427 if (ftmp > 255.0f) ftmp = 255.0f;
428 dst.c[FIMRGBA_G] = ftmp;
429 ftmp = m_af(src.c[FIMRGBA_B] / alpha) * alpha;
430 if (ftmp > 255.0f) ftmp = 255.0f;
431 dst.c[FIMRGBA_B] = ftmp;
432 dst.c[FIMRGBA_A] = alpha;
433 }
434 else
435 dst = src;
436 };
437
438 FgApproxFunc<float> m_af;
439};
440
441void
442fimZwrGammaAffine(
443 float gamma,
444 float black,
445 float gain,
446 const FimImgRgbaFC &src,
447 FimImgRgbaFC & dst)
448{
449 FGASSERT(src.imageAllocated());
450 FgApproxFunc<float>
451 af(
452 Zga(gamma,black,gain),
453 0.0f,
454 1.0f,
455 256);
456 fimLoopBinary(ZgaOp(af),src,dst);
457}
458
459//**********************************************************************************************
460// fimMirrorDetailDiff
461//**********************************************************************************************
462//
463void fimMirrorDetailDiff(FimImgRgbaFC& img)
464{
465 uint wid = img.width(),
466 hgt = img.height();
467 for (uint row=0; row<hgt; row++)
468 {
469 for (uint col=0; col<wid/2; col++)
470 {
471 FimRgbaFC &vl = img[row][col];
472 FimRgbaFC &vr = img[row][wid-col-1];
473 FimRgbaFC vnew = vl + vr;
474 vl = vnew;
475 vr = vnew;
476 }
477 }
478};
479
480// ****************************************************************************
481// ****************************************************************************
482
483static
484void
485smoothHoriz(
486 const FimRgbaFC * src,
487 FimRgbaFC * dst,
488 uint wid)
489{
490 FimRgbaFC acc[3];
491 acc[0] = src[0];
492 acc[1] = src[0];
493 acc[2] = src[1];
494 uint xx;
495 for (xx=0; xx<wid-2; xx++)
496 {
497 dst[xx] = acc[0] + acc[1]*2.0f + acc[2];
498 acc[0] = acc[1];
499 acc[1] = acc[2];
500 acc[2] = src[xx+2];
501 }
502 dst[xx++] = acc[0] + acc[1]*2.0f + acc[2]; // xx = wid-2
503 dst[xx] = acc[1] + acc[2]*3.0f; // xx = wid-1
504};
505
506
507void
508fimSmoothF(
509 const FimImgRgbaFC & src,
510 FimImgRgbaFC & dst)
511{
512 FGASSERT((src.width() > 1) && (src.height() > 1)); // Algorithm not designed for dim < 2
513 dst.resize(src.width(),src.height());
514 const float denom = 1.0f / 16.0f;
515 FimImgRgbaFC acc(src.width(),3);
516 const FimRgbaFC *srcPtr = src.imageData();
517 FimRgbaFC *dstPtr = dst.imageData();
518 FimRgbaFC *accPtr0 = acc.imageData(),
519 *accPtr1 = accPtr0 + acc.widthStepType(),
520 *accPtr2 = accPtr1 + acc.widthStepType(),
521 *accTmp;
522 smoothHoriz(srcPtr,accPtr0,src.width());
523 srcPtr += src.widthStepType();
524 smoothHoriz(srcPtr,accPtr1,src.width());
525 srcPtr += src.widthStepType();
526 smoothHoriz(srcPtr,accPtr2,src.width());
527 for (uint xx=0; xx<src.width(); xx++)
528 {
529 FimRgbaFC tmp = accPtr0[xx] * 3.0f + accPtr1[xx];
530 dstPtr[xx] = tmp * denom;
531 }
532 dstPtr += dst.widthStepType();
533 for (uint yy=1; yy<src.height(); yy++)
534 {
535 for (uint xx=0; xx<src.width(); xx++)
536 {
537 FimRgbaFC tmp = accPtr0[xx] + accPtr1[xx] * 2.0f + accPtr2[xx];
538 dstPtr[xx] = tmp * denom;
539 }
540 accTmp = accPtr0;
541 accPtr0 = accPtr1;
542 accPtr1 = accPtr2;
543 srcPtr += src.widthStepType();
544 dstPtr += dst.widthStepType();
545 if (yy < src.height()-2)
546 {
547 accPtr2 = accTmp;
548 smoothHoriz(srcPtr,accPtr2,src.width());
549 }
550 }
551};
552
553}