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 | static void compute_box_stats(PaletteGenContext *s, struct range_box *box) | 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 | /* Compute average color */ |
| 153 | * range) and its boundings */ | 154 | uint64_t sr = 0, sg = 0, sb = 0; |
| 154 | uint8_t min[3] = {0xff, 0xff, 0xff}; | ||
| 155 | uint8_t max[3] = {0x00, 0x00, 0x00}; | ||
| 156 | box->weight = 0; | 155 | box->weight = 0; |
| 157 | for (int i = box->start; i < box->start + box->len; i++) { | 156 | for (int i = box->start; i < box->start + box->len; i++) { |
| 158 | const struct color_ref *ref = s->refs[i]; | 157 | const struct color_ref *ref = s->refs[i]; |
| 159 | const uint32_t rgb = ref->color; | 158 | sr += (ref->color >> 16 & 0xff) * ref->count; |
| 160 | const uint8_t r = rgb >> 16 & 0xff, g = rgb >> 8 & 0xff, b = rgb & 0xff; | 159 | sg += (ref->color >> 8 & 0xff) * ref->count; |
| 161 | min[0] = FFMIN(r, min[0]), max[0] = FFMAX(r, max[0]); | 160 | sb += (ref->color & 0xff) * ref->count; |
| 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]); | ||
| 164 | box->weight += ref->count; | 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 */ | 167 | /* Compute squared error of each color channel */ |
| 168 | rr = max[0] - min[0]; | 168 | for (int i = box->start; i < box->start + box->len; i++) { |
| 169 | gr = max[1] - min[1]; | 169 | const struct color_ref *ref = s->refs[i]; |
| 170 | br = max[2] - min[2]; | 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 | box->major_axis = 1; // pick green by default (the color the eye is the most sensitive to) | 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; | 180 | if (er2[2] >= er2[0] && er2[2] >= er2[1]) box->major_axis = 2; |
| 173 | if (rr >= gr && rr >= br) box->major_axis = 0; | 181 | if (er2[0] >= er2[1] && er2[0] >= er2[2]) box->major_axis = 0; |
| 174 | if (gr >= rr && gr >= br) box->major_axis = 1; // prefer green again | 182 | if (er2[1] >= er2[0] && er2[1] >= er2[2]) box->major_axis = 1; // prefer green again |
| 175 | } | 183 | } |
| 176 | 184 | ||
| 177 | /** | 185 | /** |