// -- Adds a run to the CCImage inline void CCImage::add_single_run(int y, int x1, int x2, int ccid) { int index = runs.hbound(); runs.touch(++index); Run& run = runs[index]; run.y = y; run.x1 = x1; run.x2 = x2; run.ccid = ccid; }
int DjVuPalette::compute_palette(int maxcolors, int minboxsize) { if (!hist) G_THROW( ERR_MSG("DjVuPalette.no_color") ); if (maxcolors<1 || maxcolors>MAXPALETTESIZE) G_THROW( ERR_MSG("DjVuPalette.many_colors") ); // Paul Heckbert: "Color Image Quantization for Frame Buffer Display", // SIGGRAPH '82 Proceedings, page 297. (also in ppmquant) // Collect histogram colors int sum = 0; int ncolors = 0; GTArray<PData> pdata; { // extra nesting for windows for (GPosition p = *hist; p; ++p) { pdata.touch(ncolors); PData &data = pdata[ncolors++]; int k = hist->key(p); data.p[0] = (k>>16) & 0xff; data.p[1] = (k>>8) & 0xff; data.p[2] = (k) & 0xff; data.w = (*hist)[p]; sum += data.w; } } // Create first box GList<PBox> boxes; PBox newbox; newbox.data = pdata; newbox.colors = ncolors; newbox.boxsize = 256; newbox.sum = sum; boxes.append(newbox); // Repeat spliting boxes while (boxes.size() < maxcolors) { // Find suitable box GPosition p; for (p=boxes; p; ++p) if (boxes[p].colors>=2 && boxes[p].boxsize>minboxsize) break; if (! p) break; // Find box boundaries PBox &splitbox = boxes[p]; unsigned char pmax[3]; unsigned char pmin[3]; pmax[0] = pmin[0] = splitbox.data->p[0]; pmax[1] = pmin[1] = splitbox.data->p[1]; pmax[2] = pmin[2] = splitbox.data->p[2]; { // extra nesting for windows for (int j=1; j<splitbox.colors; j++) { pmax[0] = umax(pmax[0], splitbox.data[j].p[0]); pmax[1] = umax(pmax[1], splitbox.data[j].p[1]); pmax[2] = umax(pmax[2], splitbox.data[j].p[2]); pmin[0] = umin(pmin[0], splitbox.data[j].p[0]); pmin[1] = umin(pmin[1], splitbox.data[j].p[1]); pmin[2] = umin(pmin[2], splitbox.data[j].p[2]); } } // Determine split direction and sort int bl = pmax[0]-pmin[0]; int gl = pmax[1]-pmin[1]; int rl = pmax[2]-pmin[2]; splitbox.boxsize = (bl>gl ? (rl>bl ? rl : bl) : (rl>gl ? rl : gl)); if (splitbox.boxsize <= minboxsize) continue; if (gl == splitbox.boxsize) qsort(splitbox.data, splitbox.colors, sizeof(PData), gcomp); else if (rl == splitbox.boxsize) qsort(splitbox.data, splitbox.colors, sizeof(PData), rcomp); else qsort(splitbox.data, splitbox.colors, sizeof(PData), bcomp); // Find median int lowercolors = 0; int lowersum = 0; while (lowercolors<splitbox.colors-1 && lowersum+lowersum<splitbox.sum) lowersum += splitbox.data[lowercolors++].w; // Compute new boxes newbox.data = splitbox.data + lowercolors; newbox.colors = splitbox.colors - lowercolors; newbox.sum = splitbox.sum - lowersum; splitbox.colors = lowercolors; splitbox.sum = lowersum; // Insert boxes at proper location GPosition q; for (q=p; q; ++q) if (boxes[q].sum < newbox.sum) break; boxes.insert_before(q, newbox); for (q=p; q; ++q) if (boxes[q].sum < splitbox.sum) break; boxes.insert_before(q, boxes, p); } // Fill palette array ncolors = 0; palette.empty(); palette.resize(0,boxes.size()-1); { // extra nesting for windows for (GPosition p=boxes; p; ++p) { PBox &box = boxes[p]; // Compute box representative color float bsum = 0; float gsum = 0; float rsum = 0; for (int j=0; j<box.colors; j++) { float w = (float)box.data[j].w; bsum += box.data[j].p[0] * w; gsum += box.data[j].p[1] * w; rsum += box.data[j].p[2] * w; } PColor &color = palette[ncolors++]; color.p[0] = (unsigned char) fmin(255, bsum/box.sum); color.p[1] = (unsigned char) fmin(255, gsum/box.sum); color.p[2] = (unsigned char) fmin(255, rsum/box.sum); color.p[3] = ( color.p[0]*BMUL + color.p[1]*GMUL + color.p[2]*RMUL) / SMUL; } } // Save dominant color PColor dcolor = palette[0]; // Sort palette colors in luminance order qsort((PColor*)palette, ncolors, sizeof(PColor), lcomp); // Clear invalid data colordata.empty(); delete pmap; pmap = 0; // Return dominant color return color_to_index_slow(dcolor.p); }
// -- Merges small ccs of similar color and splits large ccs void CCImage::merge_and_split_ccs(int smallsize, int largesize) { int ncc = ccs.size(); int nruns = runs.size(); int splitsize = largesize; if (ncc <= 0) return; // Associative map for storing merged ccids GMap<Grid_x_Color,int> map; nregularccs = ncc; // Set the correct ccids for the runs for (int ccid=0; ccid<ccs.size(); ccid++) { CC* cc = &ccs[ccid]; if (cc->nrun <= 0) continue; Grid_x_Color key; key.color = cc->color; int ccheight = cc->bb.height(); int ccwidth = cc->bb.width(); if (ccheight<=smallsize && ccwidth<=smallsize) { key.gridi = (cc->bb.ymin+cc->bb.ymax)/splitsize/2; key.gridj = (cc->bb.xmin+cc->bb.xmax)/splitsize/2; int newccid = makeccid(key, map, ncc); for(int runid=cc->frun; runid<cc->frun+cc->nrun; runid++) runs[runid].ccid = newccid; } else if (ccheight>=largesize || ccwidth>=largesize) { for(int runid=cc->frun; runid<cc->frun+cc->nrun; runid++) { Run *r = & runs[runid]; key.gridi = r->y/splitsize; key.gridj = r->x1/splitsize; int gridj_end = r->x2/splitsize; int gridj_span = gridj_end - key.gridj; r->ccid = makeccid(key, map, ncc); if (gridj_span>0) { // truncate current run runs.touch(nruns+gridj_span-1); r = &runs[runid]; int x = key.gridj*splitsize + splitsize; int x_end = r->x2; r->x2 = x-1; // append additional runs to the runs array while (++key.gridj < gridj_end) { Run& newrun = runs[nruns++]; newrun.y = r->y; newrun.x1 = x; x += splitsize; newrun.x2 = x-1; newrun.color = key.color; newrun.ccid = makeccid(key, map, ncc); } // append last run to the run array Run& newrun = runs[nruns++]; newrun.y = r->y; newrun.x1 = x; newrun.x2 = x_end; newrun.color = key.color; newrun.ccid = makeccid(key, map, ncc); } } } } // Recompute cc descriptors make_ccs_from_ccids(); }
// -- Performs color connected component analysis void CCImage::make_ccids_by_analysis() { // Sort runs runs.sort(); // Single Pass Connected Component Analysis (with unodes) int n; int p=0; GTArray<int> umap; for (n=0; n<=runs.hbound(); n++) { int y = runs[n].y; int x1 = runs[n].x1 - 1; int x2 = runs[n].x2 + 1; int color = runs[n].color; int id = (umap.hbound() + 1); // iterate over previous line runs if (p>0) p--; for(;runs[p].y < y-1;p++); for(;(runs[p].y < y) && (runs[p].x1 <= x2);p++ ) { if ( runs[p].x2 >= x1 ) { if (runs[p].color == color) { // previous run touches current run and has same color int oid = runs[p].ccid; while (umap[oid] < oid) oid = umap[oid]; if ((int)id > umap.hbound()) { id = oid; } else if (id < oid) { umap[oid] = id; } else { umap[id] = oid; id = oid; } // freshen previous run id runs[p].ccid = id; } // stop if previous run goes past current run if (runs[p].x2 >= x2) break; } } // create new entry in umap runs[n].ccid = id; if (id > umap.hbound()) { umap.touch(id); umap[id] = id; } } // Update umap and ccid for (n=0; n<=runs.hbound(); n++) { Run &run = runs[n]; int ccid = run.ccid; while (umap[ccid] < ccid) ccid = umap[ccid]; umap[run.ccid] = ccid; run.ccid = ccid; } }
// -- Merges small ccs and split large ccs void CCImage::merge_and_split_ccs() { int ncc = ccs.size(); int nruns = runs.size(); int splitsize = largesize; if (ncc <= 0) return; // Grid of special components int gridwidth = (width+splitsize-1)/splitsize; nregularccs = ncc; // Set the correct ccids for the runs for (int ccid=0; ccid<ncc; ccid++) { CC* cc = &ccs[ccid]; if (cc->nrun <= 0) continue; int ccheight = cc->bb.height(); int ccwidth = cc->bb.width(); if (ccheight<=smallsize && ccwidth<=smallsize) { int gridi = (cc->bb.ymin+cc->bb.ymax)/splitsize/2; int gridj = (cc->bb.xmin+cc->bb.xmax)/splitsize/2; int newccid = ncc + gridi*gridwidth + gridj; for(int runid=cc->frun; runid<cc->frun+cc->nrun; runid++) runs[runid].ccid = newccid; } else if (ccheight>=largesize || ccwidth>=largesize) { for(int runid=cc->frun; runid<cc->frun+cc->nrun; runid++) { Run& r = runs[runid]; int y = r.y; int x_start = r.x1; int x_end = r.x2; int gridi = y/splitsize; int gridj_start = x_start/splitsize; int gridj_end = x_end/splitsize; int gridj_span = gridj_end-gridj_start; int newccid = ncc + gridi*gridwidth + gridj_start; if (! gridj_span) { r.ccid = newccid; } else // gridj_span>0 { // truncate the current run r.ccid = newccid++; int x = (gridj_start+1)*splitsize; r.x2 = x-1; runs.touch(nruns+gridj_span-1); // append additional runs to the runs array for(int gridj=gridj_start+1; gridj<gridj_end; gridj++) { Run& newrun = runs[nruns++]; newrun.y = y; newrun.x1 = x; x += splitsize; newrun.x2 = x-1; newrun.ccid = newccid++; } // append last run to the run array Run& newrun = runs[nruns++]; newrun.y = y; newrun.x1 = x; newrun.x2 = x_end; newrun.ccid = newccid++; } } } } // Recompute cc descriptors make_ccs_from_ccids(); }