|
11 | 11 | //
|
12 | 12 |
|
13 | 13 | #include "extras/extras.h"
|
14 |
| -#include "webp/format_constants.h" |
15 |
| -#include "src/dsp/dsp.h" |
16 | 14 |
|
17 | 15 | #include <assert.h>
|
| 16 | +#include <limits.h> |
18 | 17 | #include <string.h>
|
19 | 18 |
|
| 19 | +#include "extras/sharpyuv_risk_table.h" |
| 20 | +#include "sharpyuv/sharpyuv.h" |
| 21 | +#include "src/dsp/dsp.h" |
| 22 | +#include "src/utils/utils.h" |
| 23 | +#include "webp/format_constants.h" |
| 24 | +#include "webp/types.h" |
| 25 | + |
20 | 26 | #define XTRA_MAJ_VERSION 1
|
21 | 27 | #define XTRA_MIN_VERSION 3
|
22 | 28 | #define XTRA_REV_VERSION 2
|
@@ -160,3 +166,159 @@ int WebPUnmultiplyARGB(WebPPicture* pic) {
|
160 | 166 | }
|
161 | 167 |
|
162 | 168 | //------------------------------------------------------------------------------
|
| 169 | +// 420 risk metric |
| 170 | + |
| 171 | +#define YUV_FIX 16 // fixed-point precision for RGB->YUV |
| 172 | +static const int kYuvHalf = 1 << (YUV_FIX - 1); |
| 173 | + |
| 174 | +// Maps a value in [0, (256 << YUV_FIX) - 1] to [0, |
| 175 | +// precomputed_scores_table_sampling - 1]. It is important that the extremal |
| 176 | +// values are preserved and 1:1 mapped: |
| 177 | +// ConvertValue(0) = 0 |
| 178 | +// ConvertValue((256 << 16) - 1) = rgb_sampling_size - 1 |
| 179 | +static int SharpYuvConvertValueToSampledIdx(int v, int rgb_sampling_size) { |
| 180 | + v = (v + kYuvHalf) >> YUV_FIX; |
| 181 | + v = (v < 0) ? 0 : (v > 255) ? 255 : v; |
| 182 | + return (v * (rgb_sampling_size - 1)) / 255; |
| 183 | +} |
| 184 | + |
| 185 | +#undef YUV_FIX |
| 186 | + |
| 187 | +// For each pixel, computes the index to look up that color in a precomputed |
| 188 | +// risk score table where the YUV space is subsampled to a size of |
| 189 | +// precomputed_scores_table_sampling^3 (see sharpyuv_risk_table.h) |
| 190 | +static int SharpYuvConvertToYuvSharpnessIndex( |
| 191 | + int r, int g, int b, const SharpYuvConversionMatrix* matrix, |
| 192 | + int precomputed_scores_table_sampling) { |
| 193 | + const int y = SharpYuvConvertValueToSampledIdx( |
| 194 | + matrix->rgb_to_y[0] * r + matrix->rgb_to_y[1] * g + |
| 195 | + matrix->rgb_to_y[2] * b + matrix->rgb_to_y[3], |
| 196 | + precomputed_scores_table_sampling); |
| 197 | + const int u = SharpYuvConvertValueToSampledIdx( |
| 198 | + matrix->rgb_to_u[0] * r + matrix->rgb_to_u[1] * g + |
| 199 | + matrix->rgb_to_u[2] * b + matrix->rgb_to_u[3], |
| 200 | + precomputed_scores_table_sampling); |
| 201 | + const int v = SharpYuvConvertValueToSampledIdx( |
| 202 | + matrix->rgb_to_v[0] * r + matrix->rgb_to_v[1] * g + |
| 203 | + matrix->rgb_to_v[2] * b + matrix->rgb_to_v[3], |
| 204 | + precomputed_scores_table_sampling); |
| 205 | + return y + u * precomputed_scores_table_sampling + |
| 206 | + v * precomputed_scores_table_sampling * |
| 207 | + precomputed_scores_table_sampling; |
| 208 | +} |
| 209 | + |
| 210 | +static void SharpYuvRowToYuvSharpnessIndex( |
| 211 | + const uint8_t* r_ptr, const uint8_t* g_ptr, const uint8_t* b_ptr, |
| 212 | + int rgb_step, int rgb_bit_depth, int width, uint16_t* dst, |
| 213 | + const SharpYuvConversionMatrix* matrix, |
| 214 | + int precomputed_scores_table_sampling) { |
| 215 | + int i; |
| 216 | + assert(rgb_bit_depth == 8); |
| 217 | + (void)rgb_bit_depth; // Unused for now. |
| 218 | + for (i = 0; i < width; |
| 219 | + ++i, r_ptr += rgb_step, g_ptr += rgb_step, b_ptr += rgb_step) { |
| 220 | + dst[i] = |
| 221 | + SharpYuvConvertToYuvSharpnessIndex(r_ptr[0], g_ptr[0], b_ptr[0], matrix, |
| 222 | + precomputed_scores_table_sampling); |
| 223 | + } |
| 224 | +} |
| 225 | + |
| 226 | +#define SAFE_ALLOC(W, H, T) ((T*)WebPSafeMalloc((uint64_t)(W) * (H), sizeof(T))) |
| 227 | + |
| 228 | +static int DoEstimateRisk(const uint8_t* r_ptr, const uint8_t* g_ptr, |
| 229 | + const uint8_t* b_ptr, int rgb_step, int rgb_stride, |
| 230 | + int rgb_bit_depth, int width, int height, |
| 231 | + const SharpYuvOptions* options, |
| 232 | + const uint8_t precomputed_scores_table[], |
| 233 | + int precomputed_scores_table_sampling, |
| 234 | + float* score_out) { |
| 235 | + const int sampling3 = precomputed_scores_table_sampling * |
| 236 | + precomputed_scores_table_sampling * |
| 237 | + precomputed_scores_table_sampling; |
| 238 | + const int kNoiseLevel = 4; |
| 239 | + double total_score = 0; |
| 240 | + double count = 0; |
| 241 | + // Rows of indices in |
| 242 | + uint16_t* row1 = SAFE_ALLOC(width, 1, uint16_t); |
| 243 | + uint16_t* row2 = SAFE_ALLOC(width, 1, uint16_t); |
| 244 | + uint16_t* tmp; |
| 245 | + int i, j; |
| 246 | + |
| 247 | + if (row1 == NULL || row2 == NULL) { |
| 248 | + WebPFree(row1); |
| 249 | + WebPFree(row2); |
| 250 | + return 0; |
| 251 | + } |
| 252 | + |
| 253 | + // Convert the first row ahead. |
| 254 | + SharpYuvRowToYuvSharpnessIndex(r_ptr, g_ptr, b_ptr, rgb_step, rgb_bit_depth, |
| 255 | + width, row2, options->yuv_matrix, |
| 256 | + precomputed_scores_table_sampling); |
| 257 | + |
| 258 | + for (j = 1; j < height; ++j) { |
| 259 | + r_ptr += rgb_stride; |
| 260 | + g_ptr += rgb_stride; |
| 261 | + b_ptr += rgb_stride; |
| 262 | + // Swap row 1 and row 2. |
| 263 | + tmp = row1; |
| 264 | + row1 = row2; |
| 265 | + row2 = tmp; |
| 266 | + // Convert the row below. |
| 267 | + SharpYuvRowToYuvSharpnessIndex(r_ptr, g_ptr, b_ptr, rgb_step, rgb_bit_depth, |
| 268 | + width, row2, options->yuv_matrix, |
| 269 | + precomputed_scores_table_sampling); |
| 270 | + for (i = 0; i < width - 1; ++i) { |
| 271 | + const int idx0 = row1[i + 0]; |
| 272 | + const int idx1 = row1[i + 1]; |
| 273 | + const int idx2 = row2[i + 0]; |
| 274 | + const int score = precomputed_scores_table[idx0 + sampling3 * idx1] + |
| 275 | + precomputed_scores_table[idx0 + sampling3 * idx2] + |
| 276 | + precomputed_scores_table[idx1 + sampling3 * idx2]; |
| 277 | + if (score > kNoiseLevel) { |
| 278 | + total_score += score; |
| 279 | + count += 1.0; |
| 280 | + } |
| 281 | + } |
| 282 | + } |
| 283 | + if (count > 0.) total_score /= count; |
| 284 | + |
| 285 | + // If less than 1% of pixels were evaluated -> below noise level. |
| 286 | + if (100. * count / (width * height) < 1.) total_score = 0.; |
| 287 | + |
| 288 | + // Rescale to [0:100] |
| 289 | + total_score = (total_score > 25.) ? 100. : total_score * 100. / 25.; |
| 290 | + |
| 291 | + WebPFree(row1); |
| 292 | + WebPFree(row2); |
| 293 | + |
| 294 | + *score_out = (float)total_score; |
| 295 | + return 1; |
| 296 | +} |
| 297 | + |
| 298 | +#undef SAFE_ALLOC |
| 299 | + |
| 300 | +int SharpYuvEstimate420Risk(const void* r_ptr, const void* g_ptr, |
| 301 | + const void* b_ptr, int rgb_step, int rgb_stride, |
| 302 | + int rgb_bit_depth, int width, int height, |
| 303 | + const SharpYuvOptions* options, float* score) { |
| 304 | + if (width < 1 || height < 1 || width == INT_MAX || height == INT_MAX || |
| 305 | + r_ptr == NULL || g_ptr == NULL || b_ptr == NULL || options == NULL || |
| 306 | + score == NULL) { |
| 307 | + return 0; |
| 308 | + } |
| 309 | + if (rgb_bit_depth != 8) { |
| 310 | + return 0; |
| 311 | + } |
| 312 | + |
| 313 | + if (width <= 4 || height <= 4) { |
| 314 | + *score = 0.0f; // too small, no real risk. |
| 315 | + return 1; |
| 316 | + } |
| 317 | + |
| 318 | + return DoEstimateRisk( |
| 319 | + (const uint8_t*)r_ptr, (const uint8_t*)g_ptr, (const uint8_t*)b_ptr, |
| 320 | + rgb_step, rgb_stride, rgb_bit_depth, width, height, options, |
| 321 | + kSharpYuvPrecomputedRisk, kSharpYuvPrecomputedRiskYuvSampling, score); |
| 322 | +} |
| 323 | + |
| 324 | +//------------------------------------------------------------------------------ |
0 commit comments