This is following the results from personal research¹. ¹: https://github.com/ubitux/research/tree/main/color-quantization#results
Showing the changes to libavfilter (1 of 3 files). Show all
Unified · Split
| @@ -147,31 +147,39 @@ static av_always_inline int diff(const uint32_t a, const uint32_t b) · 146 unmodified lines | ||
| 147 | 147 | |
| 148 | 148 | static void compute_box_stats(PaletteGenContext *s, struct range_box *box) |
| 149 | 149 | { |
| 150 | − int rr, gr, br; | |
| 150 | + int avg[3]; | |
| 151 | + int64_t er2[3] = {0}; | |
| 151 | 152 | |
| 152 | − /* compute the box weight (sum all the weights of the colors in the | |
| 153 | − * range) and its boundings */ | |
| 154 | − uint8_t min[3] = {0xff, 0xff, 0xff}; | |
| 155 | − uint8_t max[3] = {0x00, 0x00, 0x00}; | |
| 153 | + /* Compute average color */ | |
| 154 | + uint64_t sr = 0, sg = 0, sb = 0; | |
| 156 | 155 | box->weight = 0; |
| 157 | 156 | for (int i = box->start; i < box->start + box->len; i++) { |
| 158 | 157 | const struct color_ref *ref = s->refs[i]; |
| 159 | − const uint32_t rgb = ref->color; | |
| 160 | − const uint8_t r = rgb >> 16 & 0xff, g = rgb >> 8 & 0xff, b = rgb & 0xff; | |
| 161 | − min[0] = FFMIN(r, min[0]), max[0] = FFMAX(r, max[0]); | |
| 162 | − min[1] = FFMIN(g, min[1]), max[1] = FFMAX(g, max[1]); | |
| 163 | − min[2] = FFMIN(b, min[2]), max[2] = FFMAX(b, max[2]); | |
| 158 | + sr += (ref->color >> 16 & 0xff) * ref->count; | |
| 159 | + sg += (ref->color >> 8 & 0xff) * ref->count; | |
| 160 | + sb += (ref->color & 0xff) * ref->count; | |
| 164 | 161 | box->weight += ref->count; |
| 165 | 162 | } |
| 163 | + avg[0] = sr / box->weight; | |
| 164 | + avg[1] = sg / box->weight; | |
| 165 | + avg[2] = sb / box->weight; | |
| 166 | 166 | |
| 167 | − /* define the axis to sort by according to the widest range of colors */ | |
| 168 | − rr = max[0] - min[0]; | |
| 169 | − gr = max[1] - min[1]; | |
| 170 | − br = max[2] - min[2]; | |
| 167 | + /* Compute squared error of each color channel */ | |
| 168 | + for (int i = box->start; i < box->start + box->len; i++) { | |
| 169 | + const struct color_ref *ref = s->refs[i]; | |
| 170 | + const int64_t dr = (int)(ref->color >> 16 & 0xff) - avg[0]; | |
| 171 | + const int64_t dg = (int)(ref->color >> 8 & 0xff) - avg[1]; | |
| 172 | + const int64_t db = (int)(ref->color & 0xff) - avg[2]; | |
| 173 | + er2[0] += dr * dr * ref->count; | |
| 174 | + er2[1] += dg * dg * ref->count; | |
| 175 | + er2[2] += db * db * ref->count; | |
| 176 | + } | |
| 177 | + | |
| 178 | + /* Define the best axis candidate for cutting the box */ | |
| 171 | 179 | box->major_axis = 1; // pick green by default (the color the eye is the most sensitive to) |
| 172 | − if (br >= rr && br >= gr) box->major_axis = 2; | |
| 173 | − if (rr >= gr && rr >= br) box->major_axis = 0; | |
| 174 | − if (gr >= rr && gr >= br) box->major_axis = 1; // prefer green again | |
| 180 | + if (er2[2] >= er2[0] && er2[2] >= er2[1]) box->major_axis = 2; | |
| 181 | + if (er2[0] >= er2[1] && er2[0] >= er2[2]) box->major_axis = 0; | |
| 182 | + if (er2[1] >= er2[0] && er2[1] >= er2[2]) box->major_axis = 1; // prefer green again | |
| 175 | 183 | } |
| 176 | 184 | |
| 177 | 185 | /** |