// ------ public MultiDimFloatTexture computeNextMIPMapLevel_BoxFilter() { int w = (int)Math.Max(1, m_iW >> 1); int h = (int)Math.Max(1, m_iH >> 1); MultiDimFloatTexture tex = new MultiDimFloatTexture(w, h, m_iNumComp); for (int j = 0; j < h; j++) { for (int i = 0; i < w; i++) { for (int c = 0; c < m_iNumComp; c++) { float val = 0.25f * ( get(i * 2, j * 2, c) + get(i * 2 + 1, j * 2, c) + get(i * 2, j * 2 + 1, c) + get(i * 2 + 1, j * 2 + 1, c)); tex.set(val, i, j, c); } } } return(tex); }
unsafe bool quantizePixel(int numcomp, MultiDimFloatTexture tex, int texPixIndex, int quantizedPixIndex) { bool valid = true; Globals.Assert(m_iQBits <= 32); int maxint = (1 << m_iQBits); int [] _quantized = m_Quantized.getData(); for (int i = 0; i < numcomp; i++) { float ctrval = tex.getData()[texPixIndex + i] - m_Center[i]; ctrval = ctrval / m_Radius[i]; ctrval = (ctrval + 1.0f) / 2.0f; _quantized[quantizedPixIndex + i] = (int)(ctrval * (maxint - 1)); if (_quantized[quantizedPixIndex + i] > maxint - 1) { valid = false; _quantized[quantizedPixIndex + i] = maxint - 1; } if (_quantized[quantizedPixIndex + i] < 0) { valid = false; _quantized[quantizedPixIndex + i] = 0; } Globals.Assert(_quantized[quantizedPixIndex + i] >= 0 && _quantized[quantizedPixIndex + i] <= maxint - 1); } return(valid); }
// ------ // Extract a sub-tile of the texture public MultiDimFloatTexture extract(int x, int y, int w, int h) { MultiDimFloatTexture res = new MultiDimFloatTexture(w, h, m_iNumComp); for (int j = 0; j < h; j++) { for (int i = 0; i < w; i++) { int xs = x + i; if (xs < 0 || xs >= m_iW) { continue; } int ys = y + j; if (ys < 0 || ys >= m_iH) { continue; } for (int c = 0; c < m_iNumComp; c++) { res.set(get(xs, ys, c), i, j, c); } } } return(res); }
static MultiDimFloatTexture makeGaussKernel(int filtersz, float sigma) { MultiDimFloatTexture kernel = new MultiDimFloatTexture(filtersz, filtersz, 1); float sum = 0; for (int j = 0; j < filtersz; j++) { for (int i = 0; i < filtersz; i++) { float di = (float)(i - filtersz / 2); float dj = (float)(j - filtersz / 2); float u2 = (di * di + dj * dj) / ((float)filtersz * filtersz); kernel.set((float)Math.Exp(-u2 / (2.0f * sigma * sigma)), i, j, 0); sum += kernel.get(i, j, 0); } } // . normalize for (int j = 0; j < filtersz; j++) { for (int i = 0; i < filtersz; i++) { kernel.set(kernel.get(i, j, 0) / sum, i, j, 0); } } return(kernel); }
// constructs a neighborhood from a list of offsets public void construct(MultiDimFloatTexture t, accessor access, bool checkcrossing, int l, int i, int j) { //CLM this used to be static... s_OffsetTable = new OffsetTableInitializer(Globals.NeighborhoodSynth_NUM_NEIGHBORS); m_iI = (short)i; m_iJ = (short)j; m_bCrossing = false; int w = t.getWidth(); int h = t.getHeight(); int n = 0; for (int k = 0; k < T_iNumNeighbors; k++) { int offsi = s_OffsetTable.m_OffsetTable[k][0]; int offsj = s_OffsetTable.m_OffsetTable[k][1]; int ni = 0, nj = 0; access.neigh_ij(i, j, offsi, offsj, ref ni, ref nj); ni = ((ni % w) + w) % w; // FIXME TODO make this faster nj = ((nj % h) + h) % h; for (int c = 0; c < T_iC; c++) { float v = t.get(ni, nj, c); m_Values[n++] = v; } } // border detection // . if the neighborhood crosses the boundary of a non-toroidal texture, // it will not be used as a candidate. Therefore, synthesis will not use it. if (checkcrossing) //l < FIRST_LEVEL_WITH_BORDER) // FIXME TODO: how to choose this automatically ? { int hl = (1 << l); if (i < Globals.NeighborhoodSynth_SIZE / 2 * hl || i >= w - Globals.NeighborhoodSynth_SIZE / 2 * hl) { m_bCrossing = true; } if (j < Globals.NeighborhoodSynth_SIZE / 2 * hl || j >= h - Globals.NeighborhoodSynth_SIZE / 2 * hl) { m_bCrossing = true; } } Globals.Assert(n == e_numdim); }
public MultiDimFloatTexture computeNextMIPMapLevel_GaussianFilter(int filtersz) { MultiDimFloatTexture filtered = applyGaussianFilter_Separable(filtersz); int w = (int)Math.Max(1, m_iW / 2); int h = (int)Math.Max(1, m_iH / 2); MultiDimFloatTexture tex = new MultiDimFloatTexture(w, h, m_iNumComp); for (int v = 0; v < h; v++) { for (int u = 0; u < w; u++) { for (int c = 0; c < m_iNumComp; c++) { tex.set(filtered.get(u * 2, v * 2, c), u, v, c); } } } filtered = null; return(tex); }
// ----------------------------------------------------- int computeStackLevels(MultiDimFloatTexture tex, bool toroidal, ref MultiDimFloatTexture[] _lvls) { MultiDimFloatTexture large = enlargeTexture(tex, toroidal ? 0 : 1); int nlvl = 0; if (Globals.isDefined("gauss") || Globals.FORCE_GAUSS) { if (Globals.PRINT_DEBUG_MSG) { Debug.Print("(using Gauss filter)"); } nlvl = computeStackLevels_Gauss(large, ref _lvls); } else { nlvl = computeStackLevels_Box(large, ref _lvls); } large = null; return(nlvl); }
public Quantizer(MultiDimFloatTexture tex, int qbits, float percent) { Globals.Assert(percent > 0.0f && percent <= 100.0f); ScopeTimer tm = new ScopeTimer("[Quantizer]"); m_Texture = tex; m_Quantized = new MultiDimIntTexture(tex.width(), tex.height(), tex.numComp()); m_iQBits = qbits; m_Mean = new float[tex.numComp()]; m_StdDev = new float[tex.numComp()]; m_Center = new float[tex.numComp()]; m_Radius = new float[tex.numComp()]; // . compute std dev and mean of every components float [] sum = new float[tex.numComp()]; float [] sumsq = new float[tex.numComp()]; for (int j = 0; j < tex.height(); j++) { for (int i = 0; i < tex.width(); i++) { for (int c = 0; c < tex.numComp(); c++) { float f = tex.get(i, j, c); sum[c] += f; sumsq[c] += f * f; } } } float num = (float)(tex.width() * tex.height()); for (int c = 0; c < tex.numComp(); c++) { m_Mean[c] = sum[c] / num; float sqstddev = (sumsq[c] - sum[c] * sum[c] / num) / (num - 1.0f); if (sqstddev > BMathLib.cFloatCompareEpsilon) { m_StdDev[c] = (float)Math.Sqrt(sqstddev); } else { m_StdDev[c] = 0.0f; } } // . for every component // compute center and radius so that 'percent' samples are within quantization space for (int c = 0; c < tex.numComp(); c++) { float [] samples = new float[tex.width() * tex.height()]; // add samples to array for (int j = 0; j < tex.height(); j++) { for (int i = 0; i < tex.width(); i++) { samples[i + j * tex.width()] = tex.get(i, j, c); } } // sort array Sort.RadixSort rs = new Sort.RadixSort(); rs.Sort(samples);//sort(samples.begin(),samples.end()); // find left and right elements so that 'percent' elements are inside float pout = ((100.0f - percent) / 2) / 100.0f; int left = (int)((samples.Length - 1) * pout); int right = (int)((samples.Length - 1) * (1.0f - pout)); m_Center[c] = (samples[right] + samples[left]) / 2.0f; m_Radius[c] = samples[right] - samples[left]; } // . quantize int num_outside = 0; for (int j = 0; j < tex.height(); j++) { for (int i = 0; i < tex.width(); i++) { if (!quantizePixel(tex.numComp(), tex, tex.getpixIndex(i, j), m_Quantized.getpixIndex(i, j))) { num_outside++; } } } float percent_out = num_outside * 100.0f / (tex.width() * tex.height()); if (Globals.PRINT_DEBUG_MSG) { Debug.Print("-----------.>> Quantizer: num outside = " + percent_out + "%"); } tm.destroy(); tm = null; }
unsafe void synthesisNeighborhoodsToD3DTexture(Exemplar a, int l) { // synthesis neighborhoods // . build float multidim texture from projected neighborhoods MultiDimFloatTexture tex = new MultiDimFloatTexture(a.recoloredStack(l).width(), a.recoloredStack(l).height(), Globals.NUM_RUNTIME_PCA_COMPONENTS); for (int j = 0; j < tex.height(); j++) { for (int i = 0; i < tex.width(); i++) { NeighborhoodSynth nproj = a.getProjectedSynthNeighborhood(l, i, j); for (int c = 0; c < tex.numComp(); c++) { tex.set(nproj.getValue(c), i, j, c); } } } // . quantize Quantizer q = new Quantizer(tex, Globals.QUANTIZE_NUM_BITS, Globals.QUANTIZE_PERCENT_INSIDE); m_d3dNeighborhoods_0_3 = new Texture(BRenderDevice.getDevice(), tex.width(), tex.height(), 1, 0, Format.A8R8G8B8, Pool.Managed); // . fill GraphicsStream texstream = m_d3dNeighborhoods_0_3.LockRectangle(0, LockFlags.None); byte * data = (byte *)texstream.InternalDataPointer; int rectPitch = tex.width() * 4; for (int j = 0; j < tex.height(); j++) { for (int i = 0; i < tex.width(); i++) { int v0 = q.quantized().get(i, j, 0); int v1 = q.quantized().get(i, j, 1); int v2 = q.quantized().get(i, j, 2); int v3 = q.quantized().get(i, j, 3); Globals.Assert(v0 >= 0 && v0 <= 255); Globals.Assert(v1 >= 0 && v1 <= 255); Globals.Assert(v2 >= 0 && v2 <= 255); Globals.Assert(v3 >= 0 && v3 <= 255); data[i * 4 + j * rectPitch + 2] = (byte)(v0); data[i * 4 + j * rectPitch + 1] = (byte)(v1); data[i * 4 + j * rectPitch + 0] = (byte)(v2); data[i * 4 + j * rectPitch + 3] = (byte)(v3); } } m_d3dNeighborhoods_0_3.UnlockRectangle(0); // de-quantization parameters m_UnqNeighborhoods_Scale = new List <float>(8); m_UnqNeighborhoods_Mean = new List <float>(8); for (int c = 0; c < q.quantized().numComp(); c++) { m_UnqNeighborhoods_Scale.Add(q.radius(c)); m_UnqNeighborhoods_Mean.Add(q.center(c)); } qn_mean_0_3 = new Vector4(m_UnqNeighborhoods_Mean[0], m_UnqNeighborhoods_Mean[1], m_UnqNeighborhoods_Mean[2], m_UnqNeighborhoods_Mean[3]); qn_scale_0_3 = new Vector4(m_UnqNeighborhoods_Scale[0], m_UnqNeighborhoods_Scale[1], m_UnqNeighborhoods_Scale[2], m_UnqNeighborhoods_Scale[3]); // expression in the shader is // (v*2.0-1.0)*UnqNeighborhoods_Scale_0_3 + UnqNeighborhoods_Mean_0_3 // => this is baked into the ants to reduce work load qn_mean_0_3 = qn_mean_0_3 - qn_scale_0_3; qn_scale_0_3 = qn_scale_0_3 * 2.0f; }
// Apply a Gaussian filter of size filtersz - separable implementation private MultiDimFloatTexture applyGaussianFilter_Separable(int filtersz) { // . compute Gauss kernel MultiDimFloatTexture kernel = new MultiDimFloatTexture(filtersz, 1, 1); float sum = 0; for (int i = 0; i < filtersz; i++) { float di = (float)(i - filtersz / 2); float u2 = (di * di) / ((float)filtersz * filtersz); float sigma = 1.0f / 3.0f; // > 3 std dev => assume 0 kernel.set((float)Math.Exp(-u2 / (2.0f * sigma * sigma)), i, 0, 0); sum += kernel.get(i, 0, 0); } // . normalize for (int i = 0; i < filtersz; i++) { kernel.set(kernel.get(i, 0, 0) / sum, i, 0, 0); } // . alloc result and tmp buffer (initialized to 0) MultiDimFloatTexture tmp = new MultiDimFloatTexture(m_iW, m_iH, m_iNumComp); MultiDimFloatTexture res = new MultiDimFloatTexture(m_iW, m_iH, m_iNumComp); // . convolve - pass 1 (treat texture as toroidal) for (int v = 0; v < m_iH; v++) { for (int u = 0; u < m_iW; u++) { for (int i = 0; i < filtersz; i++) { int x = u + i - filtersz / 2; int y = v; for (int c = 0; c < numComp(); c++) { float val = tmp.get(u, v, c); tmp.set(val + ((float)getmod(x, y, c)) * kernel.get(i, 0, 0), u, v, c); } } } } // . convolve - pass 2 (treat texture as toroidal) for (int v = 0; v < m_iH; v++) { for (int u = 0; u < m_iW; u++) { for (int j = 0; j < filtersz; j++) { int x = u; int y = v + j - filtersz / 2; for (int c = 0; c < m_iNumComp; c++) { float val = res.get(u, v, c); res.set(val + ((float)tmp.getmod(x, y, c)) * kernel.get(j, 0, 0), u, v, c); } } } } kernel = null; tmp = null; return(res); }
public MultiDimFloatTexture(MultiDimFloatTexture tex) { m_Name = tex.m_Name; }
// ----------------------------------------------------- MultiDimFloatTexture enlargeTexture(MultiDimFloatTexture ex, int type) { ScopeTimer tm = new ScopeTimer("[Exemplar::enlargeTexture]"); int w = ex.getWidth(); int h = ex.getHeight(); MultiDimFloatTexture large = new MultiDimFloatTexture(w * 2, h * 2, ex.numComp()); for (int j = 0; j < h * 2; j++) { for (int i = 0; i < w * 2; i++) { int ri, rj; // where to read ? if (type == 0) { // Toroidal // ri if (i < w / 2) { ri = (i + w / 2) % w; } else if (i < w + w / 2) { ri = i - w / 2; } else { ri = ((i - w / 2) % w); } // rj if (j < h / 2) { rj = (j + h / 2) % h; } else if (j < h + h / 2) { rj = j - h / 2; } else { rj = ((j - h / 2) % h); } } else { // Mirror // ri if (i < w / 2) { ri = (w / 2 - i); } else if (i < w + w / 2) { ri = i - w / 2; } else { ri = (w - 1 - (i - (w / 2 + w))); } // rj if (j < h / 2) { rj = (h / 2 - j); } else if (j < h + h / 2) { rj = j - h / 2; } else { rj = (h - 1 - (j - (h / 2 + h))); } } for (int c = 0; c < ex.numComp(); c++) { large.set(ex.get(ri, rj, c), i, j, c); } } } tm.destroy(); tm = null; return(large); }
int computeStackLevels_Gauss(MultiDimFloatTexture tex, ref MultiDimFloatTexture[] _lvls) { Globals.Assert(false); /* * static char str[256]; * //SimpleTimer tm("\n[Exemplar::computeStackLevels] %02d min %02d sec %d ms\n"); * * List<MultiDimFloatTexture > tmplvls; * * // compute stack of averages on large texture * * // . finest level * MultiDimFloatTexture first=tex; * MultiDimFloatTexture lvl =first; * tmplvls.clear(); * tmplvls.push_back(tex); * * int numlvl=(int)(0.01 + log2((float)lvl.width())); * int l=0; * // . compute successive filtered versions - FIXME: stack alignement ?? * do * { * MultiDimFloatTexture *next=first.applyGaussianFilter_Separable((1 << l)*2+1); // FIXME size ? * if (lvl != first) * delete (lvl); * lvl=next; * * tmplvls.push_back(lvl); * * l++; * } while ((1 << l) < lvl.width()); * * // clamp stack * int w=tex.getWidth()/2; * int h=tex.getHeight()/2; * _lvls.clear(); * * int sz=w; * l=0; * for (vector<MultiDimFloatTexture *>::iterator L=tmplvls.begin();L!=tmplvls.end();L++) * { * int oi=0,oj=0; * * * MultiDimFloatTexture *clamped=(*L).extract<MultiDimFloatTexture>(w/2+oi,h/2+oj,w,h); * _lvls.push_back(clamped); #ifdef SAVE_STACK * CTexture *tmp=clamped.toRGBTexture(); * sprintf(str,"__stack_gauss_clamped_%d.png",l); * CTexture::saveTexture(tmp,str); * delete (tmp); #endif * l++; * sz >>= 1; * if (sz < 1) * break; * } * * // free memory * for (vector<MultiDimFloatTexture *>::iterator L=tmplvls.begin();L!=tmplvls.end();L++) * if ((*L) != tex) * delete ((*L)); * * return ((int)_lvls.size()); * */ return(0); }
int computeStackLevels_Box(MultiDimFloatTexture tex, ref MultiDimFloatTexture[] _lvls) { List <MultiDimFloatTexture> tmplvls = new List <MultiDimFloatTexture>(); // compute stack of averages on large texture // . add unmodifed finest level tmplvls.Clear(); tmplvls.Add(tex); int l = 0; MultiDimFloatTexture lvl = tex; float [] lvlDat = lvl.getData(); int num = (int)(0.01 + BMathLib.log2((float)lvl.getWidth())); do // TODO: speed this up - extremely unefficient !!! (not critical though) { stack_accessor_v2 access = new stack_accessor_v2(l); // l is destination level for children // (children *are* at level l) MultiDimFloatTexture stackl = new MultiDimFloatTexture(lvl.getWidth(), lvl.getHeight(), lvl.numComp()); List <float> avg = new List <float>(); for (int q = 0; q < lvl.numComp(); q++) { avg.Add(0); } for (int i = 0; i < stackl.getWidth(); i++) { for (int j = 0; j < stackl.getHeight(); j++) { for (int q = 0; q < avg.Count; q++) { avg[q] = 0; } for (int li = 0; li < 2; li++) { for (int lj = 0; lj < 2; lj++) { int ci = 0, cj = 0; access.child_ij(i, j, li, lj, ref ci, ref cj); //CLM large amount of modification here.. check back w/ origional if problems occur if (ci < 0 || ci >= lvl.getWidth() || cj < 0 || cj >= lvl.getHeight()) { continue; } int idx = lvl.getpixmodIndex(ci, cj); for (int c = 0; c < stackl.numComp(); c++) { avg[c] += lvlDat[idx + c]; } } } int opix = stackl.getpixIndex(i, j); for (int c = 0; c < stackl.numComp(); c++) { stackl.getData()[opix + c] = avg[c] / 4.0f; } } } lvl = stackl; tmplvls.Add(lvl); l++; } while ((1 << l) <= lvl.getWidth()); // clamp stack int w = tex.getWidth() / 2; int h = tex.getHeight() / 2; _lvls = new MultiDimFloatTexture[m_iNbLevels]; l = 0; int sz = w; for (int i = 0; i < tmplvls.Count; i++) { MultiDimFloatTexture ktex = tmplvls[i]; MultiDimFloatTexture clamped = ktex.extract(w / 2, h / 2, w, h); _lvls[i] = clamped; l++; sz >>= 1; if (sz < 1) { break; } } for (int i = 0; i < tmplvls.Count; i++) { tmplvls[i] = null; } tmplvls.Clear(); return((int)_lvls.Length); }
bool loadExemplarData(StreamReader f) { //grab our ID m_iExemplarId = Convert.ToInt32(f.ReadLine()); // read exemplar flags int flags = Convert.ToInt32(f.ReadLine()); m_bToroidal = (flags & (int)eExemplarFlags.EXEMPLAR_TOROIDAL_FLAG) != 0 ? true : false; // read periods m_iPeriodX = Convert.ToInt32(f.ReadLine()); m_iPeriodY = Convert.ToInt32(f.ReadLine()); // read number of levels m_iNbLevels = Convert.ToInt32(f.ReadLine()); // read exemplar name & load our image stack String exname = f.ReadLine(); MultiDimFloatTexture image = new MultiDimFloatTexture(exname); { bool succede = computeStackLevels(image, m_bToroidal, ref m_Stack) == m_iNbLevels; Globals.Assert(succede); } m_Stack[0].setName(image.getName()); //read our presentation flagss String name = null; int present = Convert.ToInt32(f.ReadLine()); if ((present & (int)eExemplarFlags.FLAG_CONSTRAINT) != 0) { // read constraint texture name name = f.ReadLine(); MultiDimFloatTexture constraint = new MultiDimFloatTexture(name); { bool succede = (computeStackLevels(constraint, m_bToroidal, ref m_Constraints) == m_iNbLevels); Globals.Assert(succede); } m_Constraints[0].setName(constraint.getName()); } if ((present & (int)eExemplarFlags.FLAG_FMAP) != 0) { // NOTE: feature map is no longer saved // this is done for backward comp. name = f.ReadLine(); } if ((present & (int)eExemplarFlags.FLAG_PRTCOLORMAP) != 0) { name = f.ReadLine(); //m_PRTColorMap=CTexture::loadTexture(name.c_str()); } //load our Knearest neighbors (see TONG et al. 2002 "Synthesis of bidirectional texture functions on arbitrary surfaces.") mKNS = new KNearestSimilartySet(f, this, m_iNbLevels); // load recolored exemplar (see HOPPE et al. 2006 "Appearance Space Texture Synthesis") m_RecoloredStack = new MultiDimFloatTexture[m_iNbLevels]; for (int l = 0; l < m_iNbLevels; l++) { name = f.ReadLine(); m_RecoloredStack[l] = new MultiDimFloatTexture(name); } loadHighResExemplarImg(m_name); loadRandomTexture(m_name); return(true); }
void computeSynthNeighborhoods() { ScopeTimer tm = new ScopeTimer("[computeSynthNeighborhoods]"); m_SynthNeighborhoods = new NeighborhoodSynth[mNumLevels][]; // foreach level for (int level = 0; level < mNumLevels; level++) { MultiDimFloatTexture recolored_level = null; if (Globals.isDefined("4D")) { // . keep only 4 dimension from recolored exemplar MultiDimFloatTexture level_4D = new MultiDimFloatTexture(mOwner.recoloredStack(level).width(), mOwner.recoloredStack(level).height(), mOwner.recoloredStack(level).numComp()); int w = level_4D.getWidth(); int h = level_4D.getHeight(); Globals.Assert(w == mOwner.stack(level).getWidth() && h == mOwner.stack(level).height()); Globals.Assert(level_4D.numComp() == Globals.NUM_RECOLORED_PCA_COMPONENTS); for (int i = 0; i < w; i++) { for (int j = 0; j < h; j++) { // . copy first four channels for (int c = 0; c < 4; c++) { level_4D.set(mOwner.recoloredStack(level).get(i, j, c), i, j, c); } // . zero out all channels above 4 for (int c = 4; c < level_4D.numComp(); c++) { level_4D.set(0, i, j, c); } } } recolored_level = level_4D; } else { // . keep all dimensions recolored_level = mOwner.recoloredStack(level); } m_SynthNeighborhoods[level] = new NeighborhoodSynth[recolored_level.width() * recolored_level.height()]; stack_accessor_v2 access = new stack_accessor_v2(level); for (int j = 0; j < recolored_level.height(); j++) { for (int i = 0; i < recolored_level.width(); i++) { int index = i + j * recolored_level.width(); m_SynthNeighborhoods[level][index] = new NeighborhoodSynth(); m_SynthNeighborhoods[level][index].construct( recolored_level, access, (!mOwner.isToroidal()) && level < (mNumLevels - Globals.NUM_LEVELS_WITHOUT_BORDER), //(!m_bToroidal) && l < FIRST_LEVEL_WITH_BORDER, level, i, j); } } } tm.destroy(); tm = null; }