This is better than just testing that the tree is not overdetermined.
Unified · Split
| @@ -31,15 +31,16 @@ · 30 unmodified lines | |||
| 31 | #include "libavcodec/mjpegenc_huffman.c" | 31 | #include "libavcodec/mjpegenc_huffman.c" |
| 32 | 32 | ||
| 33 | // Validate the computed lengths satisfy the JPEG restrictions and is optimal. | 33 | // Validate the computed lengths satisfy the JPEG restrictions and is optimal. |
| 34 | static int check_lengths(int L, int expected_length, | 34 | static int check_lengths(int L, const int *probs, int nprobs, |
| 35 | const int *probs, int nprobs) | 35 | int expected_length, const uint8_t expected_len_counts[/* L + 1 */]) |
| 36 | { | 36 | { |
| 37 | HuffTable lengths[256]; | 37 | HuffTable lengths[256]; |
| 38 | PTable val_counts[256]; | 38 | PTable val_counts[256]; |
| 39 | uint8_t len_counts[17] = { 0 }; | ||
| 39 | int actual_length = 0, i, j, k, prob, length; | 40 | int actual_length = 0, i, j, k, prob, length; |
| 40 | int ret = 0; | 41 | int ret = 0; |
| 41 | double cantor_measure = 0; | ||
| 42 | av_assert0(nprobs <= 256); | 42 | av_assert0(nprobs <= 256); |
| 43 | av_assert0(L < FF_ARRAY_ELEMS(len_counts)); | ||
| 43 | 44 | ||
| 44 | for (i = 0; i < nprobs; i++) { | 45 | for (i = 0; i < nprobs; i++) { |
| 45 | val_counts[i] = (PTable){.value = i, .prob = probs[i]}; | 46 | val_counts[i] = (PTable){.value = i, .prob = probs[i]}; |
| @@ -47,6 +48,26 @@ static int check_lengths(int L, int expected_length, · 1 unmodified line | |||
| 47 | 48 | ||
| 48 | mjpegenc_huffman_compute_bits(val_counts, lengths, nprobs, L); | 49 | mjpegenc_huffman_compute_bits(val_counts, lengths, nprobs, L); |
| 49 | 50 | ||
| 51 | for (int i = 0; i < nprobs; ++i) { | ||
| 52 | if (lengths[i].length > L) { | ||
| 53 | fprintf(stderr, | ||
| 54 | "Len %d of element %d of Huffman table with %d elements exceeds max length %d\n", | ||
| 55 | lengths[i].length, i, nprobs, L); | ||
| 56 | return 1; | ||
| 57 | } | ||
| 58 | len_counts[lengths[i].length]++; | ||
| 59 | } | ||
| 60 | // Test that the lengths can be made part of a complete, prefix-free tree: | ||
| 61 | unsigned code = 0; | ||
| 62 | for (int i = 1; i <= L; ++i) { | ||
| 63 | code <<= 1; | ||
| 64 | code += len_counts[i]; | ||
| 65 | } | ||
| 66 | if (code > 1U << L) { | ||
| 67 | fprintf(stderr, "Huffman tree overdetermined/invalid\n"); | ||
| 68 | ret = 1; | ||
| 69 | } | ||
| 70 | |||
| 50 | for (i = 0; i < nprobs; i++) { | 71 | for (i = 0; i < nprobs; i++) { |
| 51 | // Find the value's prob and length | 72 | // Find the value's prob and length |
| 52 | for (j = 0; j < nprobs; j++) | 73 | for (j = 0; j < nprobs; j++) |
| @@ -59,22 +80,18 @@ static int check_lengths(int L, int expected_length, · 6 unmodified lines | |||
| 59 | 80 | ||
| 60 | if (prob) { | 81 | if (prob) { |
| 61 | actual_length += prob * length; | 82 | actual_length += prob * length; |
| 62 | cantor_measure += 1. / (1 << length); | ||
| 63 | } | 83 | } |
| 64 | 84 | ||
| 65 | if (length > L || length < 1) return 1; | 85 | if (length > L || length < 1) return 1; |
| 66 | } | 86 | } |
| 67 | // Check that the codes can be prefix-free. | ||
| 68 | if (cantor_measure > 1) ret = 1; | ||
| 69 | // Check that the total length is optimal | 87 | // Check that the total length is optimal |
| 70 | if (actual_length != expected_length) ret = 1; | 88 | if (actual_length != expected_length) ret = 1; |
| 71 | 89 | ||
| 72 | if (ret == 1) { | 90 | if (ret == 1) { |
| 73 | fprintf(stderr, | 91 | fprintf(stderr, |
| 74 | "Cantor measure: %f\n" | ||
| 75 | "Actual length: %d\n" | 92 | "Actual length: %d\n" |
| 76 | "Expected length: %d\n", | 93 | "Expected length: %d\n", |
| 77 | cantor_measure, actual_length, expected_length); | 94 | actual_length, expected_length); |
| 78 | } | 95 | } |
| 79 | 96 | ||
| 80 | return ret; | 97 | return ret; |
| @@ -83,6 +100,9 @@ static int check_lengths(int L, int expected_length, · 2 unmodified lines | |||
| 83 | static const int probs_zeroes[] = { | 100 | static const int probs_zeroes[] = { |
| 84 | 6, 6, 0, 0, 0 | 101 | 6, 6, 0, 0, 0 |
| 85 | }; | 102 | }; |
| 103 | static const uint8_t len_counts_zeroes[] = { | ||
| 104 | 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, | ||
| 105 | }; | ||
| 86 | 106 | ||
| 87 | static const int probs_skewed[] = { | 107 | static const int probs_skewed[] = { |
| 88 | 2, 0, 0, 0, 0, 1, 0, 0, 20, 0, 2, 0, 10, 5, 1, 1, 9, 1, 1, 6, 0, 5, 0, 1, 0, 7, 6, | 108 | 2, 0, 0, 0, 0, 1, 0, 0, 20, 0, 2, 0, 10, 5, 1, 1, 9, 1, 1, 6, 0, 5, 0, 1, 0, 7, 6, |
| @@ -96,6 +116,9 @@ static const int probs_skewed[] = { · 7 unmodified lines | |||
| 96 | 0, 3, 0, 0, 28, 0, 2, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 23, 0, 0, 0, 0, | 116 | 0, 3, 0, 0, 28, 0, 2, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 23, 0, 0, 0, 0, |
| 97 | 0, 21, 1, 0, 3, 24, 2, 0, 0, 7, 0, 0, 1, 5, 1, 2, 0, 5 | 117 | 0, 21, 1, 0, 3, 24, 2, 0, 0, 7, 0, 0, 1, 5, 1, 2, 0, 5 |
| 98 | }; | 118 | }; |
| 119 | static const uint8_t len_counts_skewed[] = { | ||
| 120 | 0, 1, 0, 0, 1, 2, 7, 11, 18, 31, 28, 40, 0, 1, 0, 0, 116, | ||
| 121 | }; | ||
| 99 | 122 | ||
| 100 | static const int probs_sat[] = { | 123 | static const int probs_sat[] = { |
| 101 | 74, 8, 14, 7, 9345, 40, 0, 2014, 2, 1, 115, 0, 2, 1, 194, 388, 20, 0, 0, 2, 1, 121, | 124 | 74, 8, 14, 7, 9345, 40, 0, 2014, 2, 1, 115, 0, 2, 1, 194, 388, 20, 0, 0, 2, 1, 121, |
| @@ -110,6 +133,9 @@ static const int probs_sat[] = { · 8 unmodified lines | |||
| 110 | 0, 1085, 0, 0, 0, 3, 489, 36, 1, 0, 1, 9420, 294, 28, 0, 57, 5, 0, 9, 2, 0, 1, 2, | 133 | 0, 1085, 0, 0, 0, 3, 489, 36, 1, 0, 1, 9420, 294, 28, 0, 57, 5, 0, 9, 2, 0, 1, 2, |
| 111 | 2, 0, 0, 9, 2, 29, 2, 2, 7, 0, 5, 490, 0, 7, 5, 0, 1, 8, 0, 0, 23255, 0, 1 | 134 | 2, 0, 0, 9, 2, 29, 2, 2, 7, 0, 5, 490, 0, 7, 5, 0, 1, 8, 0, 0, 23255, 0, 1 |
| 112 | }; | 135 | }; |
| 136 | static const uint8_t len_counts_sat[] = { | ||
| 137 | 0, 1, 0, 2, 1, 2, 2, 5, 5, 7, 7, 8, 17, 23, 16, 24, 136, | ||
| 138 | }; | ||
| 113 | 139 | ||
| 114 | // Test the example given on @see | 140 | // Test the example given on @see |
| 115 | // http://guru.multimedia.cx/small-tasks-for-ffmpeg/ | 141 | // http://guru.multimedia.cx/small-tasks-for-ffmpeg/ |
| @@ -154,13 +180,13 @@ int main(int argc, char **argv) · 38 unmodified lines | |||
| 154 | } | 180 | } |
| 155 | 181 | ||
| 156 | // Check handling of zero probabilities | 182 | // Check handling of zero probabilities |
| 157 | if (check_lengths(16, 18, probs_zeroes, FF_ARRAY_ELEMS(probs_zeroes))) | 183 | if (check_lengths(16, probs_zeroes, FF_ARRAY_ELEMS(probs_zeroes), 18, len_counts_zeroes)) |
| 158 | ret = 1; | 184 | ret = 1; |
| 159 | // Check skewed distribution over 256 without saturated lengths | 185 | // Check skewed distribution over 256 without saturated lengths |
| 160 | if (check_lengths(16, 41282, probs_skewed, FF_ARRAY_ELEMS(probs_skewed))) | 186 | if (check_lengths(16, probs_skewed, FF_ARRAY_ELEMS(probs_skewed), 41282, len_counts_skewed)) |
| 161 | ret = 1; | 187 | ret = 1; |
| 162 | // Check skewed distribution over 256 with saturated lengths | 188 | // Check skewed distribution over 256 with saturated lengths |
| 163 | if (check_lengths(16, 669904, probs_sat, FF_ARRAY_ELEMS(probs_sat))) | 189 | if (check_lengths(16, probs_sat, FF_ARRAY_ELEMS(probs_sat), 669904, len_counts_sat)) |
| 164 | ret = 1; | 190 | ret = 1; |
| 165 | 191 | ||
| 166 | return ret; | 192 | return ret; |