mirror of
https://github.com/opencv/opencv.git
synced 2026-07-24 21:03:03 +04:00
Compare commits
400 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| bda89a6469 | |||
| ee4feb4b09 | |||
| 1fac1421e5 | |||
| 4de1efd3c8 | |||
| 545f8a8c93 | |||
| 97b6068c46 | |||
| 64e6cf9fe5 | |||
| ed788229ed | |||
| 71ef76ecad | |||
| 4c86fc13cb | |||
| b379969c63 | |||
| 9564aa1fdb | |||
| 24b05cb308 | |||
| dff8e29f98 | |||
| 4733a19bab | |||
| 5bf7345743 | |||
| 4342657762 | |||
| e801f0e954 | |||
| 9cd1d087c3 | |||
| 8d22ac200f | |||
| 28a5f7d66b | |||
| a8345133ac | |||
| f5a84f75c4 | |||
| b8e0898c7c | |||
| aa80f754f4 | |||
| 5d15c65e48 | |||
| f8b16fa293 | |||
| af04b422c9 | |||
| c97ff6c0f9 | |||
| 61969dc158 | |||
| 8f910d2d49 | |||
| fd4fac946a | |||
| 287874a444 | |||
| c6c8783c60 | |||
| 971ae00942 | |||
| a11f9e1963 | |||
| d427cebd12 | |||
| e5b58b1020 | |||
| 2c0d9fa81f | |||
| cf93df41fc | |||
| ba7b0f4c54 | |||
| 21ee54c3d1 | |||
| 90a17cf964 | |||
| 17c485eb03 | |||
| 60ba6ef916 | |||
| 424e1150ae | |||
| d1c35e7b61 | |||
| 3d60a9b96c | |||
| 5b0b59ecfb | |||
| 60f81032bb | |||
| a45928045a | |||
| f62241b780 | |||
| 43302c79ca | |||
| 94f73ee680 | |||
| ff4d3d27dd | |||
| 682653c6f8 | |||
| c2b6c67431 | |||
| e6ce752da1 | |||
| 4a4ff6749b | |||
| f270e8d040 | |||
| 3cd8976493 | |||
| 92b9888837 | |||
| 1c4a64f0a1 | |||
| afa072578c | |||
| 5ee7abbe3c | |||
| 121bc50ca9 | |||
| f2cce5fd8c | |||
| 7d61426279 | |||
| 8b97c93f3d | |||
| 0d19fa0720 | |||
| 2a11103a73 | |||
| b8ef2036b0 | |||
| 8b9982a0a5 | |||
| 15612ebd39 | |||
| d1bb2055da | |||
| 6b6c2f6087 | |||
| 416848066c | |||
| 76b5e19eb3 | |||
| a675c4937a | |||
| 6ea29a7696 | |||
| b89581960c | |||
| 4ec4ec844f | |||
| e47f3e5bcc | |||
| 9a27901edc | |||
| 7b298b0995 | |||
| 435c97c7a2 | |||
| 939099b9ce | |||
| 2a19db0f0a | |||
| fe77223dee | |||
| 9a18330f3a | |||
| c89780dfe0 | |||
| 3fddd3bf93 | |||
| 45f6931352 | |||
| b9435b9e38 | |||
| a2642d83d3 | |||
| a4cb914656 | |||
| 2817cbe450 | |||
| c2ca3ee2fa | |||
| 13bc55a015 | |||
| b505cf84de | |||
| 476a02739e | |||
| 544ceedcac | |||
| 766465ce94 | |||
| a011035ed6 | |||
| 734de34b7a | |||
| e0e683aedf | |||
| 883c4c60c3 | |||
| b1b505f783 | |||
| dd3f517fe9 | |||
| 65d606630d | |||
| 816f82682b | |||
| ad56838040 | |||
| 202ba124a5 | |||
| 629d47fcd8 | |||
| beb5b291b9 | |||
| a91eca6ec2 | |||
| c0503718cd | |||
| 51d54ad4f0 | |||
| 1ddcfc5c68 | |||
| 9b8ddba4d1 | |||
| b2b6f52d14 | |||
| fb5db23463 | |||
| 0a35b97e75 | |||
| 15621532b8 | |||
| 774d28efb1 | |||
| eea517be92 | |||
| b369c456f2 | |||
| c3023fb52b | |||
| 660a709840 | |||
| ccf415c341 | |||
| d99d18304a | |||
| 76a27e3399 | |||
| 134094a442 | |||
| 8108fb0575 | |||
| c790779a37 | |||
| 72f35e0626 | |||
| f21bde4d9f | |||
| 986c5084a4 | |||
| 38180c2c97 | |||
| 95e36fd488 | |||
| 36c9f7c3df | |||
| d8e10f3a8d | |||
| b4a3c92867 | |||
| 72315e7b00 | |||
| 1633c29f29 | |||
| 4dfa0a0383 | |||
| 818585fd12 | |||
| 529a241a26 | |||
| 5e3a7ac8a7 | |||
| eb44e0a556 | |||
| 63b154396a | |||
| fbb3f64a1a | |||
| 0dc0bc80ae | |||
| ca1ba7a53d | |||
| 4b0132ed7a | |||
| 7523c777c5 | |||
| 43d58aa760 | |||
| 20ac7f40f6 | |||
| 78c5e41c23 | |||
| 8a18d132fc | |||
| 9a4404276a | |||
| f5b9705c70 | |||
| 101a147496 | |||
| 01a28db949 | |||
| 8d74101f07 | |||
| 41af8aa046 | |||
| 0dfab6bbd0 | |||
| 53b6fb465c | |||
| 873250f5de | |||
| 6c37e10c83 | |||
| 70146700aa | |||
| a7acb8805f | |||
| fb5e7964b3 | |||
| 5af747c6fd | |||
| d9efb55d29 | |||
| 17dfae77af | |||
| af997529a1 | |||
| 5639f5a296 | |||
| 9276f1910b | |||
| 0c644d1ec8 | |||
| a093a0e05c | |||
| cdc469786c | |||
| 5ff1fababc | |||
| 6276b86a78 | |||
| 6e14cc2189 | |||
| 9e906d9e21 | |||
| dc4af58be0 | |||
| 4a3ab8fbdb | |||
| b38547ee9a | |||
| 501ff7f056 | |||
| ad0ab4109a | |||
| 373160ce00 | |||
| 50ac880335 | |||
| 54e6f5c237 | |||
| 3266ac7667 | |||
| ca28274895 | |||
| ec55b6f6db | |||
| 16ad53f354 | |||
| 86042af108 | |||
| e459a5108c | |||
| a2aa8db5a9 | |||
| 75315fb297 | |||
| c40fbad12e | |||
| a4d16acd00 | |||
| 5d96f984b1 | |||
| 744798aadd | |||
| 318cba4ce3 | |||
| 9d14c0b37a | |||
| 2f636b4456 | |||
| e07a488012 | |||
| 77bf5593e5 | |||
| 26513285dd | |||
| de64db3064 | |||
| cf93217365 | |||
| ec8db970f9 | |||
| 09d54c9f52 | |||
| d00eb451bf | |||
| 3f431a16a3 | |||
| 593e240d7c | |||
| fb9d59ab15 | |||
| aff8c9bd28 | |||
| 0867e3188d | |||
| 1261559305 | |||
| 2185bce4b7 | |||
| af230ec133 | |||
| fc41c18c6f | |||
| f4d55d512f | |||
| 955b20230c | |||
| 686ea5c1a6 | |||
| 1acadd363b | |||
| 7ec91aefc1 | |||
| 973b367da6 | |||
| 1d33335e33 | |||
| 1f57eb93fd | |||
| ac2dc29525 | |||
| 6773b938b3 | |||
| d1c4e4b5a5 | |||
| ac4fd4f4ae | |||
| beb14c70da | |||
| 94f1c3d152 | |||
| b5426a8679 | |||
| ad8d7e84b9 | |||
| b48959fa57 | |||
| 7a52a4c134 | |||
| 3cdbcc1645 | |||
| 763b80d5fa | |||
| d0c767428b | |||
| a67248f386 | |||
| c638f085aa | |||
| 7ecdcf6ca6 | |||
| 363976694e | |||
| b6a58818bb | |||
| af92a517a7 | |||
| ba58bba4e8 | |||
| e0b5637474 | |||
| d32d31577c | |||
| e9dcecf9b4 | |||
| fccf284088 | |||
| cfc781949d | |||
| 3755099bd4 | |||
| af23375352 | |||
| 6053996253 | |||
| dd9262c318 | |||
| 35cebbd167 | |||
| d66aa2e0ff | |||
| 5dd3e6052e | |||
| 3d47e043af | |||
| 895fc72a0b | |||
| f42d5399aa | |||
| ad5874779c | |||
| d19bccad74 | |||
| 7c4158d8c2 | |||
| c546a27de7 | |||
| 377fcc062d | |||
| 07ef08e966 | |||
| 2e20f06f8e | |||
| 54d9597522 | |||
| f3e788b8ab | |||
| cdbfdcc363 | |||
| 417034518c | |||
| 2112aa31e6 | |||
| 2c2716de0f | |||
| dcf72e49e2 | |||
| c4c4ac28dc | |||
| 6d5b900324 | |||
| a165f55579 | |||
| 0d7f770996 | |||
| 53139e6ebe | |||
| a893969ec9 | |||
| d5bbb16066 | |||
| 21c38bbdaf | |||
| b7c8e9e874 | |||
| e65b51ca3c | |||
| d56535afce | |||
| 4e156a162f | |||
| ed7e4273cd | |||
| 0ca5f68d72 | |||
| 48073e6d95 | |||
| 657c17bb8c | |||
| cec7cc037b | |||
| 5c12bafe80 | |||
| d2e02779c4 | |||
| c2f2ea6b85 | |||
| af433d0352 | |||
| ee044771a7 | |||
| edc5518f68 | |||
| 79f792ad05 | |||
| 469addeca3 | |||
| c394847c35 | |||
| ea64bb58a5 | |||
| a9d23a6479 | |||
| ea5499fa51 | |||
| bad4e5c3eb | |||
| e86c888a92 | |||
| a71ff50130 | |||
| 278b4ca40c | |||
| cd6d79d106 | |||
| 80c4cedd25 | |||
| 02f4d6ca53 | |||
| 732657cc46 | |||
| 86a8ff6129 | |||
| d8ab83600b | |||
| 6ec5ae0215 | |||
| 8dfba51884 | |||
| 7cf1054d36 | |||
| 42b1d04999 | |||
| 055ffc0425 | |||
| eabe679f78 | |||
| 1ce5a724c7 | |||
| d65fead337 | |||
| 17e2bf5717 | |||
| 87a692b89e | |||
| 5e8d8df847 | |||
| c46f119e0e | |||
| 09619c1c45 | |||
| 1accf3b3f4 | |||
| 8bf5bed0a9 | |||
| 5ffeebabad | |||
| e89c1103ff | |||
| 5a6a49405d | |||
| fe4f27b99b | |||
| 1864b64f64 | |||
| 4bd2ad30c4 | |||
| 6e9eca29e2 | |||
| 471b40040a | |||
| dfe0368835 | |||
| 613c12e590 | |||
| 8ec6544624 | |||
| a8d14e88fe | |||
| 6576e8b927 | |||
| acb3b3bd4d | |||
| da0fec7308 | |||
| af61a15839 | |||
| 3ada597449 | |||
| 938d8dce06 | |||
| 6e85e852d4 | |||
| 70a5553499 | |||
| 90af2835a2 | |||
| 83e2e124a3 | |||
| 2477103707 | |||
| adbd613660 | |||
| 849d8d31fe | |||
| 24ebca5c59 | |||
| ead7d6d80f | |||
| 34df28db2b | |||
| 2ff12c4981 | |||
| a2b3cd9a2c | |||
| d31da08d43 | |||
| 17e9fde75a | |||
| 692e1eccb6 | |||
| 009f5f74ef | |||
| 4c7db02925 | |||
| c4e2e17b0c | |||
| 9255df44d0 | |||
| 0e40c8a031 | |||
| 86e7d82418 | |||
| 3b070517ad | |||
| ad5d14ec0e | |||
| bce653117f | |||
| ad172726c0 | |||
| cc9b199ecb | |||
| b1a8de0901 | |||
| 823884b064 | |||
| a84969bdcd | |||
| 6a7d1c15d3 | |||
| 823e767eed | |||
| e42560bed5 | |||
| d6630ab35b | |||
| d154fa4b4c | |||
| 47fc889df0 | |||
| 027769bf5d | |||
| 9ca9249992 | |||
| ec91a3d59d | |||
| 2f30c3fd4f | |||
| 2f9351d995 | |||
| 230e44b01d | |||
| 46206b4814 | |||
| ec37364762 | |||
| 23288b7cb5 | |||
| 0a1b957331 |
@@ -22,3 +22,4 @@ bin/
|
||||
*.log
|
||||
*.tlog
|
||||
build
|
||||
node_modules
|
||||
|
||||
Vendored
+5
-5
@@ -1,8 +1,8 @@
|
||||
# Binaries branch name: ffmpeg/master_20190910
|
||||
# Binaries were created for OpenCV: bea2c7545243ba2dabce6badc94dd55894a8e5ca
|
||||
ocv_update(FFMPEG_BINARIES_COMMIT "197f87f7e811a9ded35d989b37e50501ff6afaa4")
|
||||
ocv_update(FFMPEG_FILE_HASH_BIN32 "ab380c9dde361f30dd3604f88eef6c48")
|
||||
ocv_update(FFMPEG_FILE_HASH_BIN64 "90260a4da737fc045c9279567313ee9d")
|
||||
# Binaries branch name: ffmpeg/master_20191119
|
||||
# Binaries were created for OpenCV: 318cba4ce37319ba0b0870aab384d5dc066bb124
|
||||
ocv_update(FFMPEG_BINARIES_COMMIT "a66a24e9f410ae05da4baeeb8b451912664ce49c")
|
||||
ocv_update(FFMPEG_FILE_HASH_BIN32 "5de6044cad9398549e57bc46fc13908d")
|
||||
ocv_update(FFMPEG_FILE_HASH_BIN64 "55c0bc8ad27db00116fabf06508de196")
|
||||
ocv_update(FFMPEG_FILE_HASH_CMAKE "ad57c038ba34b868277ccbe6dd0f9602")
|
||||
|
||||
function(download_win_ffmpeg script_var)
|
||||
|
||||
Vendored
+11
-6
@@ -61,12 +61,17 @@ static const uint16_t kAcTable[128] = {
|
||||
|
||||
void VP8ParseQuant(VP8Decoder* const dec) {
|
||||
VP8BitReader* const br = &dec->br_;
|
||||
const int base_q0 = VP8GetValue(br, 7);
|
||||
const int dqy1_dc = VP8Get(br) ? VP8GetSignedValue(br, 4) : 0;
|
||||
const int dqy2_dc = VP8Get(br) ? VP8GetSignedValue(br, 4) : 0;
|
||||
const int dqy2_ac = VP8Get(br) ? VP8GetSignedValue(br, 4) : 0;
|
||||
const int dquv_dc = VP8Get(br) ? VP8GetSignedValue(br, 4) : 0;
|
||||
const int dquv_ac = VP8Get(br) ? VP8GetSignedValue(br, 4) : 0;
|
||||
const int base_q0 = VP8GetValue(br, 7, "global-header");
|
||||
const int dqy1_dc = VP8Get(br, "global-header") ?
|
||||
VP8GetSignedValue(br, 4, "global-header") : 0;
|
||||
const int dqy2_dc = VP8Get(br, "global-header") ?
|
||||
VP8GetSignedValue(br, 4, "global-header") : 0;
|
||||
const int dqy2_ac = VP8Get(br, "global-header") ?
|
||||
VP8GetSignedValue(br, 4, "global-header") : 0;
|
||||
const int dquv_dc = VP8Get(br, "global-header") ?
|
||||
VP8GetSignedValue(br, 4, "global-header") : 0;
|
||||
const int dquv_ac = VP8Get(br, "global-header") ?
|
||||
VP8GetSignedValue(br, 4, "global-header") : 0;
|
||||
|
||||
const VP8SegmentHeader* const hdr = &dec->segment_hdr_;
|
||||
int i;
|
||||
|
||||
Vendored
+31
-26
@@ -296,20 +296,21 @@ static void ParseIntraMode(VP8BitReader* const br,
|
||||
// to decode more than 1 keyframe.
|
||||
if (dec->segment_hdr_.update_map_) {
|
||||
// Hardcoded tree parsing
|
||||
block->segment_ = !VP8GetBit(br, dec->proba_.segments_[0])
|
||||
? VP8GetBit(br, dec->proba_.segments_[1])
|
||||
: 2 + VP8GetBit(br, dec->proba_.segments_[2]);
|
||||
block->segment_ = !VP8GetBit(br, dec->proba_.segments_[0], "segments")
|
||||
? VP8GetBit(br, dec->proba_.segments_[1], "segments")
|
||||
: VP8GetBit(br, dec->proba_.segments_[2], "segments") + 2;
|
||||
} else {
|
||||
block->segment_ = 0; // default for intra
|
||||
}
|
||||
if (dec->use_skip_proba_) block->skip_ = VP8GetBit(br, dec->skip_p_);
|
||||
if (dec->use_skip_proba_) block->skip_ = VP8GetBit(br, dec->skip_p_, "skip");
|
||||
|
||||
block->is_i4x4_ = !VP8GetBit(br, 145); // decide for B_PRED first
|
||||
block->is_i4x4_ = !VP8GetBit(br, 145, "block-size");
|
||||
if (!block->is_i4x4_) {
|
||||
// Hardcoded 16x16 intra-mode decision tree.
|
||||
const int ymode =
|
||||
VP8GetBit(br, 156) ? (VP8GetBit(br, 128) ? TM_PRED : H_PRED)
|
||||
: (VP8GetBit(br, 163) ? V_PRED : DC_PRED);
|
||||
VP8GetBit(br, 156, "pred-modes") ?
|
||||
(VP8GetBit(br, 128, "pred-modes") ? TM_PRED : H_PRED) :
|
||||
(VP8GetBit(br, 163, "pred-modes") ? V_PRED : DC_PRED);
|
||||
block->imodes_[0] = ymode;
|
||||
memset(top, ymode, 4 * sizeof(*top));
|
||||
memset(left, ymode, 4 * sizeof(*left));
|
||||
@@ -323,22 +324,25 @@ static void ParseIntraMode(VP8BitReader* const br,
|
||||
const uint8_t* const prob = kBModesProba[top[x]][ymode];
|
||||
#if (USE_GENERIC_TREE == 1)
|
||||
// Generic tree-parsing
|
||||
int i = kYModesIntra4[VP8GetBit(br, prob[0])];
|
||||
int i = kYModesIntra4[VP8GetBit(br, prob[0], "pred-modes")];
|
||||
while (i > 0) {
|
||||
i = kYModesIntra4[2 * i + VP8GetBit(br, prob[i])];
|
||||
i = kYModesIntra4[2 * i + VP8GetBit(br, prob[i], "pred-modes")];
|
||||
}
|
||||
ymode = -i;
|
||||
#else
|
||||
// Hardcoded tree parsing
|
||||
ymode = !VP8GetBit(br, prob[0]) ? B_DC_PRED :
|
||||
!VP8GetBit(br, prob[1]) ? B_TM_PRED :
|
||||
!VP8GetBit(br, prob[2]) ? B_VE_PRED :
|
||||
!VP8GetBit(br, prob[3]) ?
|
||||
(!VP8GetBit(br, prob[4]) ? B_HE_PRED :
|
||||
(!VP8GetBit(br, prob[5]) ? B_RD_PRED : B_VR_PRED)) :
|
||||
(!VP8GetBit(br, prob[6]) ? B_LD_PRED :
|
||||
(!VP8GetBit(br, prob[7]) ? B_VL_PRED :
|
||||
(!VP8GetBit(br, prob[8]) ? B_HD_PRED : B_HU_PRED)));
|
||||
ymode = !VP8GetBit(br, prob[0], "pred-modes") ? B_DC_PRED :
|
||||
!VP8GetBit(br, prob[1], "pred-modes") ? B_TM_PRED :
|
||||
!VP8GetBit(br, prob[2], "pred-modes") ? B_VE_PRED :
|
||||
!VP8GetBit(br, prob[3], "pred-modes") ?
|
||||
(!VP8GetBit(br, prob[4], "pred-modes") ? B_HE_PRED :
|
||||
(!VP8GetBit(br, prob[5], "pred-modes") ? B_RD_PRED
|
||||
: B_VR_PRED)) :
|
||||
(!VP8GetBit(br, prob[6], "pred-modes") ? B_LD_PRED :
|
||||
(!VP8GetBit(br, prob[7], "pred-modes") ? B_VL_PRED :
|
||||
(!VP8GetBit(br, prob[8], "pred-modes") ? B_HD_PRED
|
||||
: B_HU_PRED))
|
||||
);
|
||||
#endif // USE_GENERIC_TREE
|
||||
top[x] = ymode;
|
||||
}
|
||||
@@ -348,9 +352,9 @@ static void ParseIntraMode(VP8BitReader* const br,
|
||||
}
|
||||
}
|
||||
// Hardcoded UVMode decision tree
|
||||
block->uvmode_ = !VP8GetBit(br, 142) ? DC_PRED
|
||||
: !VP8GetBit(br, 114) ? V_PRED
|
||||
: VP8GetBit(br, 183) ? TM_PRED : H_PRED;
|
||||
block->uvmode_ = !VP8GetBit(br, 142, "pred-modes-uv") ? DC_PRED
|
||||
: !VP8GetBit(br, 114, "pred-modes-uv") ? V_PRED
|
||||
: VP8GetBit(br, 183, "pred-modes-uv") ? TM_PRED : H_PRED;
|
||||
}
|
||||
|
||||
int VP8ParseIntraModeRow(VP8BitReader* const br, VP8Decoder* const dec) {
|
||||
@@ -514,8 +518,10 @@ void VP8ParseProba(VP8BitReader* const br, VP8Decoder* const dec) {
|
||||
for (b = 0; b < NUM_BANDS; ++b) {
|
||||
for (c = 0; c < NUM_CTX; ++c) {
|
||||
for (p = 0; p < NUM_PROBAS; ++p) {
|
||||
const int v = VP8GetBit(br, CoeffsUpdateProba[t][b][c][p]) ?
|
||||
VP8GetValue(br, 8) : CoeffsProba0[t][b][c][p];
|
||||
const int v =
|
||||
VP8GetBit(br, CoeffsUpdateProba[t][b][c][p], "global-header") ?
|
||||
VP8GetValue(br, 8, "global-header") :
|
||||
CoeffsProba0[t][b][c][p];
|
||||
proba->bands_[t][b].probas_[c][p] = v;
|
||||
}
|
||||
}
|
||||
@@ -524,9 +530,8 @@ void VP8ParseProba(VP8BitReader* const br, VP8Decoder* const dec) {
|
||||
proba->bands_ptr_[t][b] = &proba->bands_[t][kBands[b]];
|
||||
}
|
||||
}
|
||||
dec->use_skip_proba_ = VP8Get(br);
|
||||
dec->use_skip_proba_ = VP8Get(br, "global-header");
|
||||
if (dec->use_skip_proba_) {
|
||||
dec->skip_p_ = VP8GetValue(br, 8);
|
||||
dec->skip_p_ = VP8GetValue(br, 8, "global-header");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Vendored
+42
-39
@@ -161,23 +161,26 @@ static int ParseSegmentHeader(VP8BitReader* br,
|
||||
VP8SegmentHeader* hdr, VP8Proba* proba) {
|
||||
assert(br != NULL);
|
||||
assert(hdr != NULL);
|
||||
hdr->use_segment_ = VP8Get(br);
|
||||
hdr->use_segment_ = VP8Get(br, "global-header");
|
||||
if (hdr->use_segment_) {
|
||||
hdr->update_map_ = VP8Get(br);
|
||||
if (VP8Get(br)) { // update data
|
||||
hdr->update_map_ = VP8Get(br, "global-header");
|
||||
if (VP8Get(br, "global-header")) { // update data
|
||||
int s;
|
||||
hdr->absolute_delta_ = VP8Get(br);
|
||||
hdr->absolute_delta_ = VP8Get(br, "global-header");
|
||||
for (s = 0; s < NUM_MB_SEGMENTS; ++s) {
|
||||
hdr->quantizer_[s] = VP8Get(br) ? VP8GetSignedValue(br, 7) : 0;
|
||||
hdr->quantizer_[s] = VP8Get(br, "global-header") ?
|
||||
VP8GetSignedValue(br, 7, "global-header") : 0;
|
||||
}
|
||||
for (s = 0; s < NUM_MB_SEGMENTS; ++s) {
|
||||
hdr->filter_strength_[s] = VP8Get(br) ? VP8GetSignedValue(br, 6) : 0;
|
||||
hdr->filter_strength_[s] = VP8Get(br, "global-header") ?
|
||||
VP8GetSignedValue(br, 6, "global-header") : 0;
|
||||
}
|
||||
}
|
||||
if (hdr->update_map_) {
|
||||
int s;
|
||||
for (s = 0; s < MB_FEATURE_TREE_PROBS; ++s) {
|
||||
proba->segments_[s] = VP8Get(br) ? VP8GetValue(br, 8) : 255u;
|
||||
proba->segments_[s] = VP8Get(br, "global-header") ?
|
||||
VP8GetValue(br, 8, "global-header") : 255u;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
@@ -205,7 +208,7 @@ static VP8StatusCode ParsePartitions(VP8Decoder* const dec,
|
||||
size_t last_part;
|
||||
size_t p;
|
||||
|
||||
dec->num_parts_minus_one_ = (1 << VP8GetValue(br, 2)) - 1;
|
||||
dec->num_parts_minus_one_ = (1 << VP8GetValue(br, 2, "global-header")) - 1;
|
||||
last_part = dec->num_parts_minus_one_;
|
||||
if (size < 3 * last_part) {
|
||||
// we can't even read the sizes with sz[]! That's a failure.
|
||||
@@ -229,21 +232,21 @@ static VP8StatusCode ParsePartitions(VP8Decoder* const dec,
|
||||
// Paragraph 9.4
|
||||
static int ParseFilterHeader(VP8BitReader* br, VP8Decoder* const dec) {
|
||||
VP8FilterHeader* const hdr = &dec->filter_hdr_;
|
||||
hdr->simple_ = VP8Get(br);
|
||||
hdr->level_ = VP8GetValue(br, 6);
|
||||
hdr->sharpness_ = VP8GetValue(br, 3);
|
||||
hdr->use_lf_delta_ = VP8Get(br);
|
||||
hdr->simple_ = VP8Get(br, "global-header");
|
||||
hdr->level_ = VP8GetValue(br, 6, "global-header");
|
||||
hdr->sharpness_ = VP8GetValue(br, 3, "global-header");
|
||||
hdr->use_lf_delta_ = VP8Get(br, "global-header");
|
||||
if (hdr->use_lf_delta_) {
|
||||
if (VP8Get(br)) { // update lf-delta?
|
||||
if (VP8Get(br, "global-header")) { // update lf-delta?
|
||||
int i;
|
||||
for (i = 0; i < NUM_REF_LF_DELTAS; ++i) {
|
||||
if (VP8Get(br)) {
|
||||
hdr->ref_lf_delta_[i] = VP8GetSignedValue(br, 6);
|
||||
if (VP8Get(br, "global-header")) {
|
||||
hdr->ref_lf_delta_[i] = VP8GetSignedValue(br, 6, "global-header");
|
||||
}
|
||||
}
|
||||
for (i = 0; i < NUM_MODE_LF_DELTAS; ++i) {
|
||||
if (VP8Get(br)) {
|
||||
hdr->mode_lf_delta_[i] = VP8GetSignedValue(br, 6);
|
||||
if (VP8Get(br, "global-header")) {
|
||||
hdr->mode_lf_delta_[i] = VP8GetSignedValue(br, 6, "global-header");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -352,8 +355,8 @@ int VP8GetHeaders(VP8Decoder* const dec, VP8Io* const io) {
|
||||
buf_size -= frm_hdr->partition_length_;
|
||||
|
||||
if (frm_hdr->key_frame_) {
|
||||
pic_hdr->colorspace_ = VP8Get(br);
|
||||
pic_hdr->clamp_type_ = VP8Get(br);
|
||||
pic_hdr->colorspace_ = VP8Get(br, "global-header");
|
||||
pic_hdr->clamp_type_ = VP8Get(br, "global-header");
|
||||
}
|
||||
if (!ParseSegmentHeader(br, &dec->segment_hdr_, &dec->proba_)) {
|
||||
return VP8SetError(dec, VP8_STATUS_BITSTREAM_ERROR,
|
||||
@@ -378,7 +381,7 @@ int VP8GetHeaders(VP8Decoder* const dec, VP8Io* const io) {
|
||||
"Not a key frame.");
|
||||
}
|
||||
|
||||
VP8Get(br); // ignore the value of update_proba_
|
||||
VP8Get(br, "global-header"); // ignore the value of update_proba_
|
||||
|
||||
VP8ParseProba(br, dec);
|
||||
|
||||
@@ -403,28 +406,28 @@ static const uint8_t kZigzag[16] = {
|
||||
// See section 13-2: http://tools.ietf.org/html/rfc6386#section-13.2
|
||||
static int GetLargeValue(VP8BitReader* const br, const uint8_t* const p) {
|
||||
int v;
|
||||
if (!VP8GetBit(br, p[3])) {
|
||||
if (!VP8GetBit(br, p[4])) {
|
||||
if (!VP8GetBit(br, p[3], "coeffs")) {
|
||||
if (!VP8GetBit(br, p[4], "coeffs")) {
|
||||
v = 2;
|
||||
} else {
|
||||
v = 3 + VP8GetBit(br, p[5]);
|
||||
v = 3 + VP8GetBit(br, p[5], "coeffs");
|
||||
}
|
||||
} else {
|
||||
if (!VP8GetBit(br, p[6])) {
|
||||
if (!VP8GetBit(br, p[7])) {
|
||||
v = 5 + VP8GetBit(br, 159);
|
||||
if (!VP8GetBit(br, p[6], "coeffs")) {
|
||||
if (!VP8GetBit(br, p[7], "coeffs")) {
|
||||
v = 5 + VP8GetBit(br, 159, "coeffs");
|
||||
} else {
|
||||
v = 7 + 2 * VP8GetBit(br, 165);
|
||||
v += VP8GetBit(br, 145);
|
||||
v = 7 + 2 * VP8GetBit(br, 165, "coeffs");
|
||||
v += VP8GetBit(br, 145, "coeffs");
|
||||
}
|
||||
} else {
|
||||
const uint8_t* tab;
|
||||
const int bit1 = VP8GetBit(br, p[8]);
|
||||
const int bit0 = VP8GetBit(br, p[9 + bit1]);
|
||||
const int bit1 = VP8GetBit(br, p[8], "coeffs");
|
||||
const int bit0 = VP8GetBit(br, p[9 + bit1], "coeffs");
|
||||
const int cat = 2 * bit1 + bit0;
|
||||
v = 0;
|
||||
for (tab = kCat3456[cat]; *tab; ++tab) {
|
||||
v += v + VP8GetBit(br, *tab);
|
||||
v += v + VP8GetBit(br, *tab, "coeffs");
|
||||
}
|
||||
v += 3 + (8 << cat);
|
||||
}
|
||||
@@ -438,24 +441,24 @@ static int GetCoeffsFast(VP8BitReader* const br,
|
||||
int ctx, const quant_t dq, int n, int16_t* out) {
|
||||
const uint8_t* p = prob[n]->probas_[ctx];
|
||||
for (; n < 16; ++n) {
|
||||
if (!VP8GetBit(br, p[0])) {
|
||||
if (!VP8GetBit(br, p[0], "coeffs")) {
|
||||
return n; // previous coeff was last non-zero coeff
|
||||
}
|
||||
while (!VP8GetBit(br, p[1])) { // sequence of zero coeffs
|
||||
while (!VP8GetBit(br, p[1], "coeffs")) { // sequence of zero coeffs
|
||||
p = prob[++n]->probas_[0];
|
||||
if (n == 16) return 16;
|
||||
}
|
||||
{ // non zero coeff
|
||||
const VP8ProbaArray* const p_ctx = &prob[n + 1]->probas_[0];
|
||||
int v;
|
||||
if (!VP8GetBit(br, p[2])) {
|
||||
if (!VP8GetBit(br, p[2], "coeffs")) {
|
||||
v = 1;
|
||||
p = p_ctx[1];
|
||||
} else {
|
||||
v = GetLargeValue(br, p);
|
||||
p = p_ctx[2];
|
||||
}
|
||||
out[kZigzag[n]] = VP8GetSigned(br, v) * dq[n > 0];
|
||||
out[kZigzag[n]] = VP8GetSigned(br, v, "coeffs") * dq[n > 0];
|
||||
}
|
||||
}
|
||||
return 16;
|
||||
@@ -468,24 +471,24 @@ static int GetCoeffsAlt(VP8BitReader* const br,
|
||||
int ctx, const quant_t dq, int n, int16_t* out) {
|
||||
const uint8_t* p = prob[n]->probas_[ctx];
|
||||
for (; n < 16; ++n) {
|
||||
if (!VP8GetBitAlt(br, p[0])) {
|
||||
if (!VP8GetBitAlt(br, p[0], "coeffs")) {
|
||||
return n; // previous coeff was last non-zero coeff
|
||||
}
|
||||
while (!VP8GetBitAlt(br, p[1])) { // sequence of zero coeffs
|
||||
while (!VP8GetBitAlt(br, p[1], "coeffs")) { // sequence of zero coeffs
|
||||
p = prob[++n]->probas_[0];
|
||||
if (n == 16) return 16;
|
||||
}
|
||||
{ // non zero coeff
|
||||
const VP8ProbaArray* const p_ctx = &prob[n + 1]->probas_[0];
|
||||
int v;
|
||||
if (!VP8GetBitAlt(br, p[2])) {
|
||||
if (!VP8GetBitAlt(br, p[2], "coeffs")) {
|
||||
v = 1;
|
||||
p = p_ctx[1];
|
||||
} else {
|
||||
v = GetLargeValue(br, p);
|
||||
p = p_ctx[2];
|
||||
}
|
||||
out[kZigzag[n]] = VP8GetSigned(br, v) * dq[n > 0];
|
||||
out[kZigzag[n]] = VP8GetSigned(br, v, "coeffs") * dq[n > 0];
|
||||
}
|
||||
}
|
||||
return 16;
|
||||
|
||||
Vendored
+1
-1
@@ -32,7 +32,7 @@ extern "C" {
|
||||
// version numbers
|
||||
#define DEC_MAJ_VERSION 1
|
||||
#define DEC_MIN_VERSION 0
|
||||
#define DEC_REV_VERSION 2
|
||||
#define DEC_REV_VERSION 3
|
||||
|
||||
// YUV-cache parameters. Cache is 32-bytes wide (= one cacheline).
|
||||
// Constraints are: We need to store one 16x16 block of luma samples (y),
|
||||
|
||||
Vendored
+61
-64
@@ -362,12 +362,8 @@ static int ReadHuffmanCodes(VP8LDecoder* const dec, int xsize, int ysize,
|
||||
VP8LMetadata* const hdr = &dec->hdr_;
|
||||
uint32_t* huffman_image = NULL;
|
||||
HTreeGroup* htree_groups = NULL;
|
||||
// When reading htrees, some might be unused, as the format allows it.
|
||||
// We will still read them but put them in this htree_group_bogus.
|
||||
HTreeGroup htree_group_bogus;
|
||||
HuffmanCode* huffman_tables = NULL;
|
||||
HuffmanCode* huffman_tables_bogus = NULL;
|
||||
HuffmanCode* next = NULL;
|
||||
HuffmanCode* huffman_table = NULL;
|
||||
int num_htree_groups = 1;
|
||||
int num_htree_groups_max = 1;
|
||||
int max_alphabet_size = 0;
|
||||
@@ -418,12 +414,6 @@ static int ReadHuffmanCodes(VP8LDecoder* const dec, int xsize, int ysize,
|
||||
if (*mapped_group == -1) *mapped_group = num_htree_groups++;
|
||||
huffman_image[i] = *mapped_group;
|
||||
}
|
||||
huffman_tables_bogus = (HuffmanCode*)WebPSafeMalloc(
|
||||
table_size, sizeof(*huffman_tables_bogus));
|
||||
if (huffman_tables_bogus == NULL) {
|
||||
dec->status_ = VP8_STATUS_OUT_OF_MEMORY;
|
||||
goto Error;
|
||||
}
|
||||
} else {
|
||||
num_htree_groups = num_htree_groups_max;
|
||||
}
|
||||
@@ -453,63 +443,71 @@ static int ReadHuffmanCodes(VP8LDecoder* const dec, int xsize, int ysize,
|
||||
goto Error;
|
||||
}
|
||||
|
||||
next = huffman_tables;
|
||||
huffman_table = huffman_tables;
|
||||
for (i = 0; i < num_htree_groups_max; ++i) {
|
||||
// If the index "i" is unused in the Huffman image, read the coefficients
|
||||
// but store them to a bogus htree_group.
|
||||
const int is_bogus = (mapping != NULL && mapping[i] == -1);
|
||||
HTreeGroup* const htree_group =
|
||||
is_bogus ? &htree_group_bogus :
|
||||
&htree_groups[(mapping == NULL) ? i : mapping[i]];
|
||||
HuffmanCode** const htrees = htree_group->htrees;
|
||||
HuffmanCode* huffman_tables_i = is_bogus ? huffman_tables_bogus : next;
|
||||
int size;
|
||||
int total_size = 0;
|
||||
int is_trivial_literal = 1;
|
||||
int max_bits = 0;
|
||||
for (j = 0; j < HUFFMAN_CODES_PER_META_CODE; ++j) {
|
||||
int alphabet_size = kAlphabetSize[j];
|
||||
htrees[j] = huffman_tables_i;
|
||||
if (j == 0 && color_cache_bits > 0) {
|
||||
alphabet_size += 1 << color_cache_bits;
|
||||
}
|
||||
size =
|
||||
ReadHuffmanCode(alphabet_size, dec, code_lengths, huffman_tables_i);
|
||||
if (size == 0) {
|
||||
goto Error;
|
||||
}
|
||||
if (is_trivial_literal && kLiteralMap[j] == 1) {
|
||||
is_trivial_literal = (huffman_tables_i->bits == 0);
|
||||
}
|
||||
total_size += huffman_tables_i->bits;
|
||||
huffman_tables_i += size;
|
||||
if (j <= ALPHA) {
|
||||
int local_max_bits = code_lengths[0];
|
||||
int k;
|
||||
for (k = 1; k < alphabet_size; ++k) {
|
||||
if (code_lengths[k] > local_max_bits) {
|
||||
local_max_bits = code_lengths[k];
|
||||
}
|
||||
// If the index "i" is unused in the Huffman image, just make sure the
|
||||
// coefficients are valid but do not store them.
|
||||
if (mapping != NULL && mapping[i] == -1) {
|
||||
for (j = 0; j < HUFFMAN_CODES_PER_META_CODE; ++j) {
|
||||
int alphabet_size = kAlphabetSize[j];
|
||||
if (j == 0 && color_cache_bits > 0) {
|
||||
alphabet_size += (1 << color_cache_bits);
|
||||
}
|
||||
// Passing in NULL so that nothing gets filled.
|
||||
if (!ReadHuffmanCode(alphabet_size, dec, code_lengths, NULL)) {
|
||||
goto Error;
|
||||
}
|
||||
max_bits += local_max_bits;
|
||||
}
|
||||
}
|
||||
if (!is_bogus) next = huffman_tables_i;
|
||||
htree_group->is_trivial_literal = is_trivial_literal;
|
||||
htree_group->is_trivial_code = 0;
|
||||
if (is_trivial_literal) {
|
||||
const int red = htrees[RED][0].value;
|
||||
const int blue = htrees[BLUE][0].value;
|
||||
const int alpha = htrees[ALPHA][0].value;
|
||||
htree_group->literal_arb = ((uint32_t)alpha << 24) | (red << 16) | blue;
|
||||
if (total_size == 0 && htrees[GREEN][0].value < NUM_LITERAL_CODES) {
|
||||
htree_group->is_trivial_code = 1;
|
||||
htree_group->literal_arb |= htrees[GREEN][0].value << 8;
|
||||
} else {
|
||||
HTreeGroup* const htree_group =
|
||||
&htree_groups[(mapping == NULL) ? i : mapping[i]];
|
||||
HuffmanCode** const htrees = htree_group->htrees;
|
||||
int size;
|
||||
int total_size = 0;
|
||||
int is_trivial_literal = 1;
|
||||
int max_bits = 0;
|
||||
for (j = 0; j < HUFFMAN_CODES_PER_META_CODE; ++j) {
|
||||
int alphabet_size = kAlphabetSize[j];
|
||||
htrees[j] = huffman_table;
|
||||
if (j == 0 && color_cache_bits > 0) {
|
||||
alphabet_size += (1 << color_cache_bits);
|
||||
}
|
||||
size = ReadHuffmanCode(alphabet_size, dec, code_lengths, huffman_table);
|
||||
if (size == 0) {
|
||||
goto Error;
|
||||
}
|
||||
if (is_trivial_literal && kLiteralMap[j] == 1) {
|
||||
is_trivial_literal = (huffman_table->bits == 0);
|
||||
}
|
||||
total_size += huffman_table->bits;
|
||||
huffman_table += size;
|
||||
if (j <= ALPHA) {
|
||||
int local_max_bits = code_lengths[0];
|
||||
int k;
|
||||
for (k = 1; k < alphabet_size; ++k) {
|
||||
if (code_lengths[k] > local_max_bits) {
|
||||
local_max_bits = code_lengths[k];
|
||||
}
|
||||
}
|
||||
max_bits += local_max_bits;
|
||||
}
|
||||
}
|
||||
htree_group->is_trivial_literal = is_trivial_literal;
|
||||
htree_group->is_trivial_code = 0;
|
||||
if (is_trivial_literal) {
|
||||
const int red = htrees[RED][0].value;
|
||||
const int blue = htrees[BLUE][0].value;
|
||||
const int alpha = htrees[ALPHA][0].value;
|
||||
htree_group->literal_arb = ((uint32_t)alpha << 24) | (red << 16) | blue;
|
||||
if (total_size == 0 && htrees[GREEN][0].value < NUM_LITERAL_CODES) {
|
||||
htree_group->is_trivial_code = 1;
|
||||
htree_group->literal_arb |= htrees[GREEN][0].value << 8;
|
||||
}
|
||||
}
|
||||
htree_group->use_packed_table =
|
||||
!htree_group->is_trivial_code && (max_bits < HUFFMAN_PACKED_BITS);
|
||||
if (htree_group->use_packed_table) BuildPackedTable(htree_group);
|
||||
}
|
||||
htree_group->use_packed_table =
|
||||
!htree_group->is_trivial_code && (max_bits < HUFFMAN_PACKED_BITS);
|
||||
if (htree_group->use_packed_table) BuildPackedTable(htree_group);
|
||||
}
|
||||
ok = 1;
|
||||
|
||||
@@ -521,7 +519,6 @@ static int ReadHuffmanCodes(VP8LDecoder* const dec, int xsize, int ysize,
|
||||
|
||||
Error:
|
||||
WebPSafeFree(code_lengths);
|
||||
WebPSafeFree(huffman_tables_bogus);
|
||||
WebPSafeFree(mapping);
|
||||
if (!ok) {
|
||||
WebPSafeFree(huffman_image);
|
||||
|
||||
Vendored
+1
-1
@@ -25,7 +25,7 @@
|
||||
|
||||
#define DMUX_MAJ_VERSION 1
|
||||
#define DMUX_MIN_VERSION 0
|
||||
#define DMUX_REV_VERSION 2
|
||||
#define DMUX_REV_VERSION 3
|
||||
|
||||
typedef struct {
|
||||
size_t start_; // start location of the data
|
||||
|
||||
+2
-2
@@ -214,7 +214,7 @@ static void ApplyAlphaMultiply_SSE2(uint8_t* rgba, int alpha_first,
|
||||
// Alpha detection
|
||||
|
||||
static int HasAlpha8b_SSE2(const uint8_t* src, int length) {
|
||||
const __m128i all_0xff = _mm_set1_epi8(0xff);
|
||||
const __m128i all_0xff = _mm_set1_epi8((char)0xff);
|
||||
int i = 0;
|
||||
for (; i + 16 <= length; i += 16) {
|
||||
const __m128i v = _mm_loadu_si128((const __m128i*)(src + i));
|
||||
@@ -228,7 +228,7 @@ static int HasAlpha8b_SSE2(const uint8_t* src, int length) {
|
||||
|
||||
static int HasAlpha32b_SSE2(const uint8_t* src, int length) {
|
||||
const __m128i alpha_mask = _mm_set1_epi32(0xff);
|
||||
const __m128i all_0xff = _mm_set1_epi8(0xff);
|
||||
const __m128i all_0xff = _mm_set1_epi8((char)0xff);
|
||||
int i = 0;
|
||||
// We don't know if we can access the last 3 bytes after the last alpha
|
||||
// value 'src[4 * length - 4]' (because we don't know if alpha is the first
|
||||
|
||||
Vendored
+2
-2
@@ -173,8 +173,8 @@ static int AndroidCPUInfo(CPUFeature feature) {
|
||||
const AndroidCpuFamily cpu_family = android_getCpuFamily();
|
||||
const uint64_t cpu_features = android_getCpuFeatures();
|
||||
if (feature == kNEON) {
|
||||
return (cpu_family == ANDROID_CPU_FAMILY_ARM &&
|
||||
0 != (cpu_features & ANDROID_CPU_ARM_FEATURE_NEON));
|
||||
return cpu_family == ANDROID_CPU_FAMILY_ARM &&
|
||||
(cpu_features & ANDROID_CPU_ARM_FEATURE_NEON) != 0;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
Vendored
+7
-7
@@ -326,7 +326,7 @@ static WEBP_INLINE void Update2Pixels_SSE2(__m128i* const pi, __m128i* const qi,
|
||||
const __m128i a1_lo = _mm_srai_epi16(*a0_lo, 7);
|
||||
const __m128i a1_hi = _mm_srai_epi16(*a0_hi, 7);
|
||||
const __m128i delta = _mm_packs_epi16(a1_lo, a1_hi);
|
||||
const __m128i sign_bit = _mm_set1_epi8(0x80);
|
||||
const __m128i sign_bit = _mm_set1_epi8((char)0x80);
|
||||
*pi = _mm_adds_epi8(*pi, delta);
|
||||
*qi = _mm_subs_epi8(*qi, delta);
|
||||
FLIP_SIGN_BIT2(*pi, *qi);
|
||||
@@ -338,9 +338,9 @@ static WEBP_INLINE void NeedsFilter_SSE2(const __m128i* const p1,
|
||||
const __m128i* const q0,
|
||||
const __m128i* const q1,
|
||||
int thresh, __m128i* const mask) {
|
||||
const __m128i m_thresh = _mm_set1_epi8(thresh);
|
||||
const __m128i m_thresh = _mm_set1_epi8((char)thresh);
|
||||
const __m128i t1 = MM_ABS(*p1, *q1); // abs(p1 - q1)
|
||||
const __m128i kFE = _mm_set1_epi8(0xFE);
|
||||
const __m128i kFE = _mm_set1_epi8((char)0xFE);
|
||||
const __m128i t2 = _mm_and_si128(t1, kFE); // set lsb of each byte to zero
|
||||
const __m128i t3 = _mm_srli_epi16(t2, 1); // abs(p1 - q1) / 2
|
||||
|
||||
@@ -360,7 +360,7 @@ static WEBP_INLINE void DoFilter2_SSE2(__m128i* const p1, __m128i* const p0,
|
||||
__m128i* const q0, __m128i* const q1,
|
||||
int thresh) {
|
||||
__m128i a, mask;
|
||||
const __m128i sign_bit = _mm_set1_epi8(0x80);
|
||||
const __m128i sign_bit = _mm_set1_epi8((char)0x80);
|
||||
// convert p1/q1 to int8_t (for GetBaseDelta_SSE2)
|
||||
const __m128i p1s = _mm_xor_si128(*p1, sign_bit);
|
||||
const __m128i q1s = _mm_xor_si128(*q1, sign_bit);
|
||||
@@ -380,7 +380,7 @@ static WEBP_INLINE void DoFilter4_SSE2(__m128i* const p1, __m128i* const p0,
|
||||
const __m128i* const mask,
|
||||
int hev_thresh) {
|
||||
const __m128i zero = _mm_setzero_si128();
|
||||
const __m128i sign_bit = _mm_set1_epi8(0x80);
|
||||
const __m128i sign_bit = _mm_set1_epi8((char)0x80);
|
||||
const __m128i k64 = _mm_set1_epi8(64);
|
||||
const __m128i k3 = _mm_set1_epi8(3);
|
||||
const __m128i k4 = _mm_set1_epi8(4);
|
||||
@@ -427,7 +427,7 @@ static WEBP_INLINE void DoFilter6_SSE2(__m128i* const p2, __m128i* const p1,
|
||||
const __m128i* const mask,
|
||||
int hev_thresh) {
|
||||
const __m128i zero = _mm_setzero_si128();
|
||||
const __m128i sign_bit = _mm_set1_epi8(0x80);
|
||||
const __m128i sign_bit = _mm_set1_epi8((char)0x80);
|
||||
__m128i a, not_hev;
|
||||
|
||||
// compute hev mask
|
||||
@@ -941,7 +941,7 @@ static void VR4_SSE2(uint8_t* dst) { // Vertical-Right
|
||||
const __m128i ABCD0 = _mm_srli_si128(XABCD, 1);
|
||||
const __m128i abcd = _mm_avg_epu8(XABCD, ABCD0);
|
||||
const __m128i _XABCD = _mm_slli_si128(XABCD, 1);
|
||||
const __m128i IXABCD = _mm_insert_epi16(_XABCD, I | (X << 8), 0);
|
||||
const __m128i IXABCD = _mm_insert_epi16(_XABCD, (short)(I | (X << 8)), 0);
|
||||
const __m128i avg1 = _mm_avg_epu8(IXABCD, ABCD0);
|
||||
const __m128i lsb = _mm_and_si128(_mm_xor_si128(IXABCD, ABCD0), one);
|
||||
const __m128i avg2 = _mm_subs_epu8(avg1, lsb);
|
||||
|
||||
Vendored
+1
-1
@@ -777,7 +777,7 @@ static WEBP_INLINE void VR4_SSE2(uint8_t* dst,
|
||||
const __m128i ABCD0 = _mm_srli_si128(XABCD, 1);
|
||||
const __m128i abcd = _mm_avg_epu8(XABCD, ABCD0);
|
||||
const __m128i _XABCD = _mm_slli_si128(XABCD, 1);
|
||||
const __m128i IXABCD = _mm_insert_epi16(_XABCD, I | (X << 8), 0);
|
||||
const __m128i IXABCD = _mm_insert_epi16(_XABCD, (short)(I | (X << 8)), 0);
|
||||
const __m128i avg1 = _mm_avg_epu8(IXABCD, ABCD0);
|
||||
const __m128i lsb = _mm_and_si128(_mm_xor_si128(IXABCD, ABCD0), one);
|
||||
const __m128i avg2 = _mm_subs_epu8(avg1, lsb);
|
||||
|
||||
Vendored
+6
-6
@@ -33,9 +33,9 @@ static WEBP_INLINE void PredictLine_C(const uint8_t* src, const uint8_t* pred,
|
||||
uint8_t* dst, int length, int inverse) {
|
||||
int i;
|
||||
if (inverse) {
|
||||
for (i = 0; i < length; ++i) dst[i] = src[i] + pred[i];
|
||||
for (i = 0; i < length; ++i) dst[i] = (uint8_t)(src[i] + pred[i]);
|
||||
} else {
|
||||
for (i = 0; i < length; ++i) dst[i] = src[i] - pred[i];
|
||||
for (i = 0; i < length; ++i) dst[i] = (uint8_t)(src[i] - pred[i]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -155,7 +155,7 @@ static WEBP_INLINE void DoGradientFilter_C(const uint8_t* in,
|
||||
const int pred = GradientPredictor_C(preds[w - 1],
|
||||
preds[w - stride],
|
||||
preds[w - stride - 1]);
|
||||
out[w] = in[w] + (inverse ? pred : -pred);
|
||||
out[w] = (uint8_t)(in[w] + (inverse ? pred : -pred));
|
||||
}
|
||||
++row;
|
||||
preds += stride;
|
||||
@@ -194,7 +194,7 @@ static void HorizontalUnfilter_C(const uint8_t* prev, const uint8_t* in,
|
||||
uint8_t pred = (prev == NULL) ? 0 : prev[0];
|
||||
int i;
|
||||
for (i = 0; i < width; ++i) {
|
||||
out[i] = pred + in[i];
|
||||
out[i] = (uint8_t)(pred + in[i]);
|
||||
pred = out[i];
|
||||
}
|
||||
}
|
||||
@@ -206,7 +206,7 @@ static void VerticalUnfilter_C(const uint8_t* prev, const uint8_t* in,
|
||||
HorizontalUnfilter_C(NULL, in, out, width);
|
||||
} else {
|
||||
int i;
|
||||
for (i = 0; i < width; ++i) out[i] = prev[i] + in[i];
|
||||
for (i = 0; i < width; ++i) out[i] = (uint8_t)(prev[i] + in[i]);
|
||||
}
|
||||
}
|
||||
#endif // !WEBP_NEON_OMIT_C_CODE
|
||||
@@ -220,7 +220,7 @@ static void GradientUnfilter_C(const uint8_t* prev, const uint8_t* in,
|
||||
int i;
|
||||
for (i = 0; i < width; ++i) {
|
||||
top = prev[i]; // need to read this first, in case prev==out
|
||||
left = in[i] + GradientPredictor_C(left, top, top_left);
|
||||
left = (uint8_t)(in[i] + GradientPredictor_C(left, top, top_left));
|
||||
top_left = top;
|
||||
out[i] = left;
|
||||
}
|
||||
|
||||
+9
-7
@@ -163,7 +163,8 @@ static void GradientPredictDirect_SSE2(const uint8_t* const row,
|
||||
_mm_storel_epi64((__m128i*)(out + i), H);
|
||||
}
|
||||
for (; i < length; ++i) {
|
||||
out[i] = row[i] - GradientPredictor_SSE2(row[i - 1], top[i], top[i - 1]);
|
||||
const int delta = GradientPredictor_SSE2(row[i - 1], top[i], top[i - 1]);
|
||||
out[i] = (uint8_t)(row[i] - delta);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -188,7 +189,7 @@ static WEBP_INLINE void DoGradientFilter_SSE2(const uint8_t* in,
|
||||
|
||||
// Filter line-by-line.
|
||||
while (row < last_row) {
|
||||
out[0] = in[0] - in[-stride];
|
||||
out[0] = (uint8_t)(in[0] - in[-stride]);
|
||||
GradientPredictDirect_SSE2(in + 1, in + 1 - stride, out + 1, width - 1);
|
||||
++row;
|
||||
in += stride;
|
||||
@@ -223,7 +224,7 @@ static void HorizontalUnfilter_SSE2(const uint8_t* prev, const uint8_t* in,
|
||||
uint8_t* out, int width) {
|
||||
int i;
|
||||
__m128i last;
|
||||
out[0] = in[0] + (prev == NULL ? 0 : prev[0]);
|
||||
out[0] = (uint8_t)(in[0] + (prev == NULL ? 0 : prev[0]));
|
||||
if (width <= 1) return;
|
||||
last = _mm_set_epi32(0, 0, 0, out[0]);
|
||||
for (i = 1; i + 8 <= width; i += 8) {
|
||||
@@ -238,7 +239,7 @@ static void HorizontalUnfilter_SSE2(const uint8_t* prev, const uint8_t* in,
|
||||
_mm_storel_epi64((__m128i*)(out + i), A7);
|
||||
last = _mm_srli_epi64(A7, 56);
|
||||
}
|
||||
for (; i < width; ++i) out[i] = in[i] + out[i - 1];
|
||||
for (; i < width; ++i) out[i] = (uint8_t)(in[i] + out[i - 1]);
|
||||
}
|
||||
|
||||
static void VerticalUnfilter_SSE2(const uint8_t* prev, const uint8_t* in,
|
||||
@@ -259,7 +260,7 @@ static void VerticalUnfilter_SSE2(const uint8_t* prev, const uint8_t* in,
|
||||
_mm_storeu_si128((__m128i*)&out[i + 0], C0);
|
||||
_mm_storeu_si128((__m128i*)&out[i + 16], C1);
|
||||
}
|
||||
for (; i < width; ++i) out[i] = in[i] + prev[i];
|
||||
for (; i < width; ++i) out[i] = (uint8_t)(in[i] + prev[i]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -296,7 +297,8 @@ static void GradientPredictInverse_SSE2(const uint8_t* const in,
|
||||
_mm_storel_epi64((__m128i*)&row[i], out);
|
||||
}
|
||||
for (; i < length; ++i) {
|
||||
row[i] = in[i] + GradientPredictor_SSE2(row[i - 1], top[i], top[i - 1]);
|
||||
const int delta = GradientPredictor_SSE2(row[i - 1], top[i], top[i - 1]);
|
||||
row[i] = (uint8_t)(in[i] + delta);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -306,7 +308,7 @@ static void GradientUnfilter_SSE2(const uint8_t* prev, const uint8_t* in,
|
||||
if (prev == NULL) {
|
||||
HorizontalUnfilter_SSE2(NULL, in, out, width);
|
||||
} else {
|
||||
out[0] = in[0] + prev[0]; // predict from above
|
||||
out[0] = (uint8_t)(in[0] + prev[0]); // predict from above
|
||||
GradientPredictInverse_SSE2(in + 1, prev + 1, out + 1, width - 1);
|
||||
}
|
||||
}
|
||||
|
||||
Vendored
+2
-2
@@ -270,14 +270,14 @@ void VP8LTransformColorInverse_C(const VP8LMultipliers* const m,
|
||||
int i;
|
||||
for (i = 0; i < num_pixels; ++i) {
|
||||
const uint32_t argb = src[i];
|
||||
const uint32_t green = argb >> 8;
|
||||
const int8_t green = (int8_t)(argb >> 8);
|
||||
const uint32_t red = argb >> 16;
|
||||
int new_red = red & 0xff;
|
||||
int new_blue = argb & 0xff;
|
||||
new_red += ColorTransformDelta(m->green_to_red_, green);
|
||||
new_red &= 0xff;
|
||||
new_blue += ColorTransformDelta(m->green_to_blue_, green);
|
||||
new_blue += ColorTransformDelta(m->red_to_blue_, new_red);
|
||||
new_blue += ColorTransformDelta(m->red_to_blue_, (int8_t)new_red);
|
||||
new_blue &= 0xff;
|
||||
dst[i] = (argb & 0xff00ff00u) | (new_red << 16) | (new_blue);
|
||||
}
|
||||
|
||||
+13
-8
@@ -515,13 +515,17 @@ static WEBP_INLINE int ColorTransformDelta(int8_t color_pred, int8_t color) {
|
||||
return ((int)color_pred * color) >> 5;
|
||||
}
|
||||
|
||||
static WEBP_INLINE int8_t U32ToS8(uint32_t v) {
|
||||
return (int8_t)(v & 0xff);
|
||||
}
|
||||
|
||||
void VP8LTransformColor_C(const VP8LMultipliers* const m, uint32_t* data,
|
||||
int num_pixels) {
|
||||
int i;
|
||||
for (i = 0; i < num_pixels; ++i) {
|
||||
const uint32_t argb = data[i];
|
||||
const uint32_t green = argb >> 8;
|
||||
const uint32_t red = argb >> 16;
|
||||
const int8_t green = U32ToS8(argb >> 8);
|
||||
const int8_t red = U32ToS8(argb >> 16);
|
||||
int new_red = red & 0xff;
|
||||
int new_blue = argb & 0xff;
|
||||
new_red -= ColorTransformDelta(m->green_to_red_, green);
|
||||
@@ -535,7 +539,7 @@ void VP8LTransformColor_C(const VP8LMultipliers* const m, uint32_t* data,
|
||||
|
||||
static WEBP_INLINE uint8_t TransformColorRed(uint8_t green_to_red,
|
||||
uint32_t argb) {
|
||||
const uint32_t green = argb >> 8;
|
||||
const int8_t green = U32ToS8(argb >> 8);
|
||||
int new_red = argb >> 16;
|
||||
new_red -= ColorTransformDelta(green_to_red, green);
|
||||
return (new_red & 0xff);
|
||||
@@ -544,9 +548,9 @@ static WEBP_INLINE uint8_t TransformColorRed(uint8_t green_to_red,
|
||||
static WEBP_INLINE uint8_t TransformColorBlue(uint8_t green_to_blue,
|
||||
uint8_t red_to_blue,
|
||||
uint32_t argb) {
|
||||
const uint32_t green = argb >> 8;
|
||||
const uint32_t red = argb >> 16;
|
||||
uint8_t new_blue = argb;
|
||||
const int8_t green = U32ToS8(argb >> 8);
|
||||
const int8_t red = U32ToS8(argb >> 16);
|
||||
uint8_t new_blue = argb & 0xff;
|
||||
new_blue -= ColorTransformDelta(green_to_blue, green);
|
||||
new_blue -= ColorTransformDelta(red_to_blue, red);
|
||||
return (new_blue & 0xff);
|
||||
@@ -558,7 +562,7 @@ void VP8LCollectColorRedTransforms_C(const uint32_t* argb, int stride,
|
||||
while (tile_height-- > 0) {
|
||||
int x;
|
||||
for (x = 0; x < tile_width; ++x) {
|
||||
++histo[TransformColorRed(green_to_red, argb[x])];
|
||||
++histo[TransformColorRed((uint8_t)green_to_red, argb[x])];
|
||||
}
|
||||
argb += stride;
|
||||
}
|
||||
@@ -571,7 +575,8 @@ void VP8LCollectColorBlueTransforms_C(const uint32_t* argb, int stride,
|
||||
while (tile_height-- > 0) {
|
||||
int x;
|
||||
for (x = 0; x < tile_width; ++x) {
|
||||
++histo[TransformColorBlue(green_to_blue, red_to_blue, argb[x])];
|
||||
++histo[TransformColorBlue((uint8_t)green_to_blue, (uint8_t)red_to_blue,
|
||||
argb[x])];
|
||||
}
|
||||
argb += stride;
|
||||
}
|
||||
|
||||
+2
-2
@@ -363,7 +363,7 @@ static void BundleColorMap_SSE2(const uint8_t* const row, int width, int xbits,
|
||||
assert(xbits <= 3);
|
||||
switch (xbits) {
|
||||
case 0: {
|
||||
const __m128i ff = _mm_set1_epi16(0xff00);
|
||||
const __m128i ff = _mm_set1_epi16((short)0xff00);
|
||||
const __m128i zero = _mm_setzero_si128();
|
||||
// Store 0xff000000 | (row[x] << 8).
|
||||
for (x = 0; x + 16 <= width; x += 16, dst += 16) {
|
||||
@@ -382,7 +382,7 @@ static void BundleColorMap_SSE2(const uint8_t* const row, int width, int xbits,
|
||||
break;
|
||||
}
|
||||
case 1: {
|
||||
const __m128i ff = _mm_set1_epi16(0xff00);
|
||||
const __m128i ff = _mm_set1_epi16((short)0xff00);
|
||||
const __m128i mul = _mm_set1_epi16(0x110);
|
||||
for (x = 0; x + 16 <= width; x += 16, dst += 8) {
|
||||
// 0a0b | (where a/b are 4 bits).
|
||||
|
||||
+3
-3
@@ -51,9 +51,9 @@ static void CollectColorBlueTransforms_SSE41(const uint32_t* argb, int stride,
|
||||
int histo[]) {
|
||||
const __m128i mults_r = _mm_set1_epi16(CST_5b(red_to_blue));
|
||||
const __m128i mults_g = _mm_set1_epi16(CST_5b(green_to_blue));
|
||||
const __m128i mask_g = _mm_set1_epi16(0xff00); // green mask
|
||||
const __m128i mask_gb = _mm_set1_epi32(0xffff); // green/blue mask
|
||||
const __m128i mask_b = _mm_set1_epi16(0x00ff); // blue mask
|
||||
const __m128i mask_g = _mm_set1_epi16((short)0xff00); // green mask
|
||||
const __m128i mask_gb = _mm_set1_epi32(0xffff); // green/blue mask
|
||||
const __m128i mask_b = _mm_set1_epi16(0x00ff); // blue mask
|
||||
const __m128i shuffler_lo = _mm_setr_epi8(-1, 2, -1, 6, -1, 10, -1, 14, -1,
|
||||
-1, -1, -1, -1, -1, -1, -1);
|
||||
const __m128i shuffler_hi = _mm_setr_epi8(-1, -1, -1, -1, -1, -1, -1, -1, -1,
|
||||
|
||||
Vendored
+15
@@ -10,6 +10,8 @@
|
||||
#ifndef WEBP_DSP_QUANT_H_
|
||||
#define WEBP_DSP_QUANT_H_
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "src/dsp/dsp.h"
|
||||
#include "src/webp/types.h"
|
||||
|
||||
@@ -67,4 +69,17 @@ static WEBP_INLINE int IsFlat(const int16_t* levels, int num_blocks,
|
||||
#endif // defined(WEBP_USE_NEON) && !defined(WEBP_ANDROID_NEON) &&
|
||||
// !defined(WEBP_HAVE_NEON_RTCD)
|
||||
|
||||
static WEBP_INLINE int IsFlatSource16(const uint8_t* src) {
|
||||
const uint32_t v = src[0] * 0x01010101u;
|
||||
int i;
|
||||
for (i = 0; i < 16; ++i) {
|
||||
if (memcmp(src + 0, &v, 4) || memcmp(src + 4, &v, 4) ||
|
||||
memcmp(src + 8, &v, 4) || memcmp(src + 12, &v, 4)) {
|
||||
return 0;
|
||||
}
|
||||
src += BPS;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
#endif // WEBP_DSP_QUANT_H_
|
||||
|
||||
Vendored
+6
-10
@@ -109,8 +109,7 @@ void WebPRescalerExportRowExpand_C(WebPRescaler* const wrk) {
|
||||
for (x_out = 0; x_out < x_out_max; ++x_out) {
|
||||
const uint32_t J = frow[x_out];
|
||||
const int v = (int)MULT_FIX(J, wrk->fy_scale);
|
||||
assert(v >= 0 && v <= 255);
|
||||
dst[x_out] = v;
|
||||
dst[x_out] = (v > 255) ? 255u : (uint8_t)v;
|
||||
}
|
||||
} else {
|
||||
const uint32_t B = WEBP_RESCALER_FRAC(-wrk->y_accum, wrk->y_sub);
|
||||
@@ -120,8 +119,7 @@ void WebPRescalerExportRowExpand_C(WebPRescaler* const wrk) {
|
||||
+ (uint64_t)B * irow[x_out];
|
||||
const uint32_t J = (uint32_t)((I + ROUNDER) >> WEBP_RESCALER_RFIX);
|
||||
const int v = (int)MULT_FIX(J, wrk->fy_scale);
|
||||
assert(v >= 0 && v <= 255);
|
||||
dst[x_out] = v;
|
||||
dst[x_out] = (v > 255) ? 255u : (uint8_t)v;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -138,17 +136,15 @@ void WebPRescalerExportRowShrink_C(WebPRescaler* const wrk) {
|
||||
assert(!wrk->y_expand);
|
||||
if (yscale) {
|
||||
for (x_out = 0; x_out < x_out_max; ++x_out) {
|
||||
const uint32_t frac = (uint32_t)MULT_FIX(frow[x_out], yscale);
|
||||
const int v = (int)MULT_FIX_FLOOR(irow[x_out] - frac, wrk->fxy_scale);
|
||||
assert(v >= 0 && v <= 255);
|
||||
dst[x_out] = v;
|
||||
const uint32_t frac = (uint32_t)MULT_FIX_FLOOR(frow[x_out], yscale);
|
||||
const int v = (int)MULT_FIX(irow[x_out] - frac, wrk->fxy_scale);
|
||||
dst[x_out] = (v > 255) ? 255u : (uint8_t)v;
|
||||
irow[x_out] = frac; // new fractional start
|
||||
}
|
||||
} else {
|
||||
for (x_out = 0; x_out < x_out_max; ++x_out) {
|
||||
const int v = (int)MULT_FIX(irow[x_out], wrk->fxy_scale);
|
||||
assert(v >= 0 && v <= 255);
|
||||
dst[x_out] = v;
|
||||
dst[x_out] = (v > 255) ? 255u : (uint8_t)v;
|
||||
irow[x_out] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
+6
-10
@@ -107,10 +107,9 @@ static void ExportRowShrink_MIPSdspR2(WebPRescaler* const wrk) {
|
||||
);
|
||||
}
|
||||
for (i = 0; i < (x_out_max & 0x3); ++i) {
|
||||
const uint32_t frac = (uint32_t)MULT_FIX(*frow++, yscale);
|
||||
const int v = (int)MULT_FIX_FLOOR(*irow - frac, wrk->fxy_scale);
|
||||
assert(v >= 0 && v <= 255);
|
||||
*dst++ = v;
|
||||
const uint32_t frac = (uint32_t)MULT_FIX_FLOOR(*frow++, yscale);
|
||||
const int v = (int)MULT_FIX(*irow - frac, wrk->fxy_scale);
|
||||
*dst++ = (v > 255) ? 255u : (uint8_t)v;
|
||||
*irow++ = frac; // new fractional start
|
||||
}
|
||||
} else {
|
||||
@@ -157,8 +156,7 @@ static void ExportRowShrink_MIPSdspR2(WebPRescaler* const wrk) {
|
||||
}
|
||||
for (i = 0; i < (x_out_max & 0x3); ++i) {
|
||||
const int v = (int)MULT_FIX_FLOOR(*irow, wrk->fxy_scale);
|
||||
assert(v >= 0 && v <= 255);
|
||||
*dst++ = v;
|
||||
*dst++ = (v > 255) ? 255u : (uint8_t)v;
|
||||
*irow++ = 0;
|
||||
}
|
||||
}
|
||||
@@ -219,8 +217,7 @@ static void ExportRowExpand_MIPSdspR2(WebPRescaler* const wrk) {
|
||||
for (i = 0; i < (x_out_max & 0x3); ++i) {
|
||||
const uint32_t J = *frow++;
|
||||
const int v = (int)MULT_FIX(J, wrk->fy_scale);
|
||||
assert(v >= 0 && v <= 255);
|
||||
*dst++ = v;
|
||||
*dst++ = (v > 255) ? 255u : (uint8_t)v;
|
||||
}
|
||||
} else {
|
||||
const uint32_t B = WEBP_RESCALER_FRAC(-wrk->y_accum, wrk->y_sub);
|
||||
@@ -291,8 +288,7 @@ static void ExportRowExpand_MIPSdspR2(WebPRescaler* const wrk) {
|
||||
+ (uint64_t)B * *irow++;
|
||||
const uint32_t J = (uint32_t)((I + ROUNDER) >> WEBP_RESCALER_RFIX);
|
||||
const int v = (int)MULT_FIX(J, wrk->fy_scale);
|
||||
assert(v >= 0 && v <= 255);
|
||||
*dst++ = v;
|
||||
*dst++ = (v > 255) ? 255u : (uint8_t)v;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+6
-10
@@ -166,8 +166,7 @@ static WEBP_INLINE void ExportRowExpand_0(const uint32_t* frow, uint8_t* dst,
|
||||
for (x_out = 0; x_out < length; ++x_out) {
|
||||
const uint32_t J = frow[x_out];
|
||||
const int v = (int)MULT_FIX(J, wrk->fy_scale);
|
||||
assert(v >= 0 && v <= 255);
|
||||
dst[x_out] = v;
|
||||
dst[x_out] = (v > 255) ? 255u : (uint8_t)v;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -241,8 +240,7 @@ static WEBP_INLINE void ExportRowExpand_1(const uint32_t* frow, uint32_t* irow,
|
||||
+ (uint64_t)B * irow[x_out];
|
||||
const uint32_t J = (uint32_t)((I + ROUNDER) >> WEBP_RESCALER_RFIX);
|
||||
const int v = (int)MULT_FIX(J, wrk->fy_scale);
|
||||
assert(v >= 0 && v <= 255);
|
||||
dst[x_out] = v;
|
||||
dst[x_out] = (v > 255) ? 255u : (uint8_t)v;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -342,10 +340,9 @@ static WEBP_INLINE void ExportRowShrink_0(const uint32_t* frow, uint32_t* irow,
|
||||
length -= 4;
|
||||
}
|
||||
for (x_out = 0; x_out < length; ++x_out) {
|
||||
const uint32_t frac = (uint32_t)MULT_FIX(frow[x_out], yscale);
|
||||
const int v = (int)MULT_FIX_FLOOR(irow[x_out] - frac, wrk->fxy_scale);
|
||||
assert(v >= 0 && v <= 255);
|
||||
dst[x_out] = v;
|
||||
const uint32_t frac = (uint32_t)MULT_FIX_FLOOR(frow[x_out], yscale);
|
||||
const int v = (int)MULT_FIX(irow[x_out] - frac, wrk->fxy_scale);
|
||||
dst[x_out] = (v > 255) ? 255u : (uint8_t)v;
|
||||
irow[x_out] = frac;
|
||||
}
|
||||
}
|
||||
@@ -406,8 +403,7 @@ static WEBP_INLINE void ExportRowShrink_1(uint32_t* irow, uint8_t* dst,
|
||||
}
|
||||
for (x_out = 0; x_out < length; ++x_out) {
|
||||
const int v = (int)MULT_FIX(irow[x_out], wrk->fxy_scale);
|
||||
assert(v >= 0 && v <= 255);
|
||||
dst[x_out] = v;
|
||||
dst[x_out] = (v > 255) ? 255u : (uint8_t)v;
|
||||
irow[x_out] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
+14
-18
@@ -81,14 +81,13 @@ static void RescalerExportRowExpand_NEON(WebPRescaler* const wrk) {
|
||||
const uint32x4_t B1 = MULT_FIX(A1, fy_scale_half);
|
||||
const uint16x4_t C0 = vmovn_u32(B0);
|
||||
const uint16x4_t C1 = vmovn_u32(B1);
|
||||
const uint8x8_t D = vmovn_u16(vcombine_u16(C0, C1));
|
||||
const uint8x8_t D = vqmovn_u16(vcombine_u16(C0, C1));
|
||||
vst1_u8(dst + x_out, D);
|
||||
}
|
||||
for (; x_out < x_out_max; ++x_out) {
|
||||
const uint32_t J = frow[x_out];
|
||||
const int v = (int)MULT_FIX_C(J, fy_scale);
|
||||
assert(v >= 0 && v <= 255);
|
||||
dst[x_out] = v;
|
||||
dst[x_out] = (v > 255) ? 255u : (uint8_t)v;
|
||||
}
|
||||
} else {
|
||||
const uint32_t B = WEBP_RESCALER_FRAC(-wrk->y_accum, wrk->y_sub);
|
||||
@@ -102,7 +101,7 @@ static void RescalerExportRowExpand_NEON(WebPRescaler* const wrk) {
|
||||
const uint32x4_t D1 = MULT_FIX(C1, fy_scale_half);
|
||||
const uint16x4_t E0 = vmovn_u32(D0);
|
||||
const uint16x4_t E1 = vmovn_u32(D1);
|
||||
const uint8x8_t F = vmovn_u16(vcombine_u16(E0, E1));
|
||||
const uint8x8_t F = vqmovn_u16(vcombine_u16(E0, E1));
|
||||
vst1_u8(dst + x_out, F);
|
||||
}
|
||||
for (; x_out < x_out_max; ++x_out) {
|
||||
@@ -110,8 +109,7 @@ static void RescalerExportRowExpand_NEON(WebPRescaler* const wrk) {
|
||||
+ (uint64_t)B * irow[x_out];
|
||||
const uint32_t J = (uint32_t)((I + ROUNDER) >> WEBP_RESCALER_RFIX);
|
||||
const int v = (int)MULT_FIX_C(J, fy_scale);
|
||||
assert(v >= 0 && v <= 255);
|
||||
dst[x_out] = v;
|
||||
dst[x_out] = (v > 255) ? 255u : (uint8_t)v;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -135,23 +133,22 @@ static void RescalerExportRowShrink_NEON(WebPRescaler* const wrk) {
|
||||
for (x_out = 0; x_out < max_span; x_out += 8) {
|
||||
LOAD_32x8(frow + x_out, in0, in1);
|
||||
LOAD_32x8(irow + x_out, in2, in3);
|
||||
const uint32x4_t A0 = MULT_FIX(in0, yscale_half);
|
||||
const uint32x4_t A1 = MULT_FIX(in1, yscale_half);
|
||||
const uint32x4_t A0 = MULT_FIX_FLOOR(in0, yscale_half);
|
||||
const uint32x4_t A1 = MULT_FIX_FLOOR(in1, yscale_half);
|
||||
const uint32x4_t B0 = vqsubq_u32(in2, A0);
|
||||
const uint32x4_t B1 = vqsubq_u32(in3, A1);
|
||||
const uint32x4_t C0 = MULT_FIX_FLOOR(B0, fxy_scale_half);
|
||||
const uint32x4_t C1 = MULT_FIX_FLOOR(B1, fxy_scale_half);
|
||||
const uint32x4_t C0 = MULT_FIX(B0, fxy_scale_half);
|
||||
const uint32x4_t C1 = MULT_FIX(B1, fxy_scale_half);
|
||||
const uint16x4_t D0 = vmovn_u32(C0);
|
||||
const uint16x4_t D1 = vmovn_u32(C1);
|
||||
const uint8x8_t E = vmovn_u16(vcombine_u16(D0, D1));
|
||||
const uint8x8_t E = vqmovn_u16(vcombine_u16(D0, D1));
|
||||
vst1_u8(dst + x_out, E);
|
||||
STORE_32x8(A0, A1, irow + x_out);
|
||||
}
|
||||
for (; x_out < x_out_max; ++x_out) {
|
||||
const uint32_t frac = (uint32_t)MULT_FIX_C(frow[x_out], yscale);
|
||||
const int v = (int)MULT_FIX_FLOOR_C(irow[x_out] - frac, fxy_scale);
|
||||
assert(v >= 0 && v <= 255);
|
||||
dst[x_out] = v;
|
||||
const uint32_t frac = (uint32_t)MULT_FIX_FLOOR_C(frow[x_out], yscale);
|
||||
const int v = (int)MULT_FIX_C(irow[x_out] - frac, fxy_scale);
|
||||
dst[x_out] = (v > 255) ? 255u : (uint8_t)v;
|
||||
irow[x_out] = frac; // new fractional start
|
||||
}
|
||||
} else {
|
||||
@@ -161,14 +158,13 @@ static void RescalerExportRowShrink_NEON(WebPRescaler* const wrk) {
|
||||
const uint32x4_t A1 = MULT_FIX(in1, fxy_scale_half);
|
||||
const uint16x4_t B0 = vmovn_u32(A0);
|
||||
const uint16x4_t B1 = vmovn_u32(A1);
|
||||
const uint8x8_t C = vmovn_u16(vcombine_u16(B0, B1));
|
||||
const uint8x8_t C = vqmovn_u16(vcombine_u16(B0, B1));
|
||||
vst1_u8(dst + x_out, C);
|
||||
STORE_32x8(zero, zero, irow + x_out);
|
||||
}
|
||||
for (; x_out < x_out_max; ++x_out) {
|
||||
const int v = (int)MULT_FIX_C(irow[x_out], fxy_scale);
|
||||
assert(v >= 0 && v <= 255);
|
||||
dst[x_out] = v;
|
||||
dst[x_out] = (v > 255) ? 255u : (uint8_t)v;
|
||||
irow[x_out] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
+11
-49
@@ -225,35 +225,6 @@ static WEBP_INLINE void ProcessRow_SSE2(const __m128i* const A0,
|
||||
_mm_storel_epi64((__m128i*)dst, G);
|
||||
}
|
||||
|
||||
static WEBP_INLINE void ProcessRow_Floor_SSE2(const __m128i* const A0,
|
||||
const __m128i* const A1,
|
||||
const __m128i* const A2,
|
||||
const __m128i* const A3,
|
||||
const __m128i* const mult,
|
||||
uint8_t* const dst) {
|
||||
const __m128i mask = _mm_set_epi32(0xffffffffu, 0, 0xffffffffu, 0);
|
||||
const __m128i B0 = _mm_mul_epu32(*A0, *mult);
|
||||
const __m128i B1 = _mm_mul_epu32(*A1, *mult);
|
||||
const __m128i B2 = _mm_mul_epu32(*A2, *mult);
|
||||
const __m128i B3 = _mm_mul_epu32(*A3, *mult);
|
||||
const __m128i D0 = _mm_srli_epi64(B0, WEBP_RESCALER_RFIX);
|
||||
const __m128i D1 = _mm_srli_epi64(B1, WEBP_RESCALER_RFIX);
|
||||
#if (WEBP_RESCALER_RFIX < 32)
|
||||
const __m128i D2 =
|
||||
_mm_and_si128(_mm_slli_epi64(B2, 32 - WEBP_RESCALER_RFIX), mask);
|
||||
const __m128i D3 =
|
||||
_mm_and_si128(_mm_slli_epi64(B3, 32 - WEBP_RESCALER_RFIX), mask);
|
||||
#else
|
||||
const __m128i D2 = _mm_and_si128(B2, mask);
|
||||
const __m128i D3 = _mm_and_si128(B3, mask);
|
||||
#endif
|
||||
const __m128i E0 = _mm_or_si128(D0, D2);
|
||||
const __m128i E1 = _mm_or_si128(D1, D3);
|
||||
const __m128i F = _mm_packs_epi32(E0, E1);
|
||||
const __m128i G = _mm_packus_epi16(F, F);
|
||||
_mm_storel_epi64((__m128i*)dst, G);
|
||||
}
|
||||
|
||||
static void RescalerExportRowExpand_SSE2(WebPRescaler* const wrk) {
|
||||
int x_out;
|
||||
uint8_t* const dst = wrk->dst;
|
||||
@@ -274,8 +245,7 @@ static void RescalerExportRowExpand_SSE2(WebPRescaler* const wrk) {
|
||||
for (; x_out < x_out_max; ++x_out) {
|
||||
const uint32_t J = frow[x_out];
|
||||
const int v = (int)MULT_FIX(J, wrk->fy_scale);
|
||||
assert(v >= 0 && v <= 255);
|
||||
dst[x_out] = v;
|
||||
dst[x_out] = (v > 255) ? 255u : (uint8_t)v;
|
||||
}
|
||||
} else {
|
||||
const uint32_t B = WEBP_RESCALER_FRAC(-wrk->y_accum, wrk->y_sub);
|
||||
@@ -308,8 +278,7 @@ static void RescalerExportRowExpand_SSE2(WebPRescaler* const wrk) {
|
||||
+ (uint64_t)B * irow[x_out];
|
||||
const uint32_t J = (uint32_t)((I + ROUNDER) >> WEBP_RESCALER_RFIX);
|
||||
const int v = (int)MULT_FIX(J, wrk->fy_scale);
|
||||
assert(v >= 0 && v <= 255);
|
||||
dst[x_out] = v;
|
||||
dst[x_out] = (v > 255) ? 255u : (uint8_t)v;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -328,20 +297,15 @@ static void RescalerExportRowShrink_SSE2(WebPRescaler* const wrk) {
|
||||
const int scale_xy = wrk->fxy_scale;
|
||||
const __m128i mult_xy = _mm_set_epi32(0, scale_xy, 0, scale_xy);
|
||||
const __m128i mult_y = _mm_set_epi32(0, yscale, 0, yscale);
|
||||
const __m128i rounder = _mm_set_epi32(0, ROUNDER, 0, ROUNDER);
|
||||
for (x_out = 0; x_out + 8 <= x_out_max; x_out += 8) {
|
||||
__m128i A0, A1, A2, A3, B0, B1, B2, B3;
|
||||
LoadDispatchAndMult_SSE2(irow + x_out, NULL, &A0, &A1, &A2, &A3);
|
||||
LoadDispatchAndMult_SSE2(frow + x_out, &mult_y, &B0, &B1, &B2, &B3);
|
||||
{
|
||||
const __m128i C0 = _mm_add_epi64(B0, rounder);
|
||||
const __m128i C1 = _mm_add_epi64(B1, rounder);
|
||||
const __m128i C2 = _mm_add_epi64(B2, rounder);
|
||||
const __m128i C3 = _mm_add_epi64(B3, rounder);
|
||||
const __m128i D0 = _mm_srli_epi64(C0, WEBP_RESCALER_RFIX); // = frac
|
||||
const __m128i D1 = _mm_srli_epi64(C1, WEBP_RESCALER_RFIX);
|
||||
const __m128i D2 = _mm_srli_epi64(C2, WEBP_RESCALER_RFIX);
|
||||
const __m128i D3 = _mm_srli_epi64(C3, WEBP_RESCALER_RFIX);
|
||||
const __m128i D0 = _mm_srli_epi64(B0, WEBP_RESCALER_RFIX); // = frac
|
||||
const __m128i D1 = _mm_srli_epi64(B1, WEBP_RESCALER_RFIX);
|
||||
const __m128i D2 = _mm_srli_epi64(B2, WEBP_RESCALER_RFIX);
|
||||
const __m128i D3 = _mm_srli_epi64(B3, WEBP_RESCALER_RFIX);
|
||||
const __m128i E0 = _mm_sub_epi64(A0, D0); // irow[x] - frac
|
||||
const __m128i E1 = _mm_sub_epi64(A1, D1);
|
||||
const __m128i E2 = _mm_sub_epi64(A2, D2);
|
||||
@@ -352,14 +316,13 @@ static void RescalerExportRowShrink_SSE2(WebPRescaler* const wrk) {
|
||||
const __m128i G1 = _mm_or_si128(D1, F3);
|
||||
_mm_storeu_si128((__m128i*)(irow + x_out + 0), G0);
|
||||
_mm_storeu_si128((__m128i*)(irow + x_out + 4), G1);
|
||||
ProcessRow_Floor_SSE2(&E0, &E1, &E2, &E3, &mult_xy, dst + x_out);
|
||||
ProcessRow_SSE2(&E0, &E1, &E2, &E3, &mult_xy, dst + x_out);
|
||||
}
|
||||
}
|
||||
for (; x_out < x_out_max; ++x_out) {
|
||||
const uint32_t frac = (int)MULT_FIX(frow[x_out], yscale);
|
||||
const int v = (int)MULT_FIX_FLOOR(irow[x_out] - frac, wrk->fxy_scale);
|
||||
assert(v >= 0 && v <= 255);
|
||||
dst[x_out] = v;
|
||||
const uint32_t frac = (int)MULT_FIX_FLOOR(frow[x_out], yscale);
|
||||
const int v = (int)MULT_FIX(irow[x_out] - frac, wrk->fxy_scale);
|
||||
dst[x_out] = (v > 255) ? 255u : (uint8_t)v;
|
||||
irow[x_out] = frac; // new fractional start
|
||||
}
|
||||
} else {
|
||||
@@ -375,8 +338,7 @@ static void RescalerExportRowShrink_SSE2(WebPRescaler* const wrk) {
|
||||
}
|
||||
for (; x_out < x_out_max; ++x_out) {
|
||||
const int v = (int)MULT_FIX(irow[x_out], scale);
|
||||
assert(v >= 0 && v <= 255);
|
||||
dst[x_out] = v;
|
||||
dst[x_out] = (v > 255) ? 255u : (uint8_t)v;
|
||||
irow[x_out] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
+6
-5
@@ -191,13 +191,14 @@ void VP8LHashChainClear(VP8LHashChain* const p) {
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
#define HASH_MULTIPLIER_HI (0xc6a4a793ULL)
|
||||
#define HASH_MULTIPLIER_LO (0x5bd1e996ULL)
|
||||
static const uint32_t kHashMultiplierHi = 0xc6a4a793u;
|
||||
static const uint32_t kHashMultiplierLo = 0x5bd1e996u;
|
||||
|
||||
static WEBP_INLINE uint32_t GetPixPairHash64(const uint32_t* const argb) {
|
||||
static WEBP_UBSAN_IGNORE_UNSIGNED_OVERFLOW WEBP_INLINE
|
||||
uint32_t GetPixPairHash64(const uint32_t* const argb) {
|
||||
uint32_t key;
|
||||
key = (argb[1] * HASH_MULTIPLIER_HI) & 0xffffffffu;
|
||||
key += (argb[0] * HASH_MULTIPLIER_LO) & 0xffffffffu;
|
||||
key = argb[1] * kHashMultiplierHi;
|
||||
key += argb[0] * kHashMultiplierLo;
|
||||
key = key >> (32 - HASH_BITS);
|
||||
return key;
|
||||
}
|
||||
|
||||
+2
-3
@@ -929,9 +929,8 @@ static int HistogramCombineStochastic(VP8LHistogramSet* const image_histo,
|
||||
}
|
||||
|
||||
mappings = (int*) WebPSafeMalloc(*num_used, sizeof(*mappings));
|
||||
if (mappings == NULL || !HistoQueueInit(&histo_queue, kHistoQueueSize)) {
|
||||
goto End;
|
||||
}
|
||||
if (mappings == NULL) return 0;
|
||||
if (!HistoQueueInit(&histo_queue, kHistoQueueSize)) goto End;
|
||||
// Fill the initial mapping.
|
||||
for (j = 0, iter = 0; iter < image_histo->size; ++iter) {
|
||||
if (histograms[iter] == NULL) continue;
|
||||
|
||||
+7
-7
@@ -202,7 +202,7 @@ static uint32_t NearLossless(uint32_t value, uint32_t predict,
|
||||
}
|
||||
if ((value >> 24) == 0 || (value >> 24) == 0xff) {
|
||||
// Preserve transparency of fully transparent or fully opaque pixels.
|
||||
a = NearLosslessDiff(value >> 24, predict >> 24);
|
||||
a = NearLosslessDiff((value >> 24) & 0xff, (predict >> 24) & 0xff);
|
||||
} else {
|
||||
a = NearLosslessComponent(value >> 24, predict >> 24, 0xff, quantization);
|
||||
}
|
||||
@@ -215,12 +215,12 @@ static uint32_t NearLossless(uint32_t value, uint32_t predict,
|
||||
// The amount by which green has been adjusted during quantization. It is
|
||||
// subtracted from red and blue for compensation, to avoid accumulating two
|
||||
// quantization errors in them.
|
||||
green_diff = NearLosslessDiff(new_green, value >> 8);
|
||||
green_diff = NearLosslessDiff(new_green, (value >> 8) & 0xff);
|
||||
}
|
||||
r = NearLosslessComponent(NearLosslessDiff(value >> 16, green_diff),
|
||||
r = NearLosslessComponent(NearLosslessDiff((value >> 16) & 0xff, green_diff),
|
||||
(predict >> 16) & 0xff, 0xff - new_green,
|
||||
quantization);
|
||||
b = NearLosslessComponent(NearLosslessDiff(value, green_diff),
|
||||
b = NearLosslessComponent(NearLosslessDiff(value & 0xff, green_diff),
|
||||
predict & 0xff, 0xff - new_green, quantization);
|
||||
return ((uint32_t)a << 24) | ((uint32_t)r << 16) | ((uint32_t)g << 8) | b;
|
||||
}
|
||||
@@ -587,7 +587,7 @@ static void GetBestGreenToRed(
|
||||
}
|
||||
}
|
||||
}
|
||||
best_tx->green_to_red_ = green_to_red_best;
|
||||
best_tx->green_to_red_ = (green_to_red_best & 0xff);
|
||||
}
|
||||
|
||||
static float GetPredictionCostCrossColorBlue(
|
||||
@@ -666,8 +666,8 @@ static void GetBestGreenRedToBlue(
|
||||
break; // out of iter-loop.
|
||||
}
|
||||
}
|
||||
best_tx->green_to_blue_ = green_to_blue_best;
|
||||
best_tx->red_to_blue_ = red_to_blue_best;
|
||||
best_tx->green_to_blue_ = green_to_blue_best & 0xff;
|
||||
best_tx->red_to_blue_ = red_to_blue_best & 0xff;
|
||||
}
|
||||
#undef kGreenRedToBlueMaxIters
|
||||
#undef kGreenRedToBlueNumAxis
|
||||
|
||||
Vendored
+20
-6
@@ -33,7 +33,7 @@
|
||||
|
||||
// number of non-zero coeffs below which we consider the block very flat
|
||||
// (and apply a penalty to complex predictions)
|
||||
#define FLATNESS_LIMIT_I16 10 // I16 mode
|
||||
#define FLATNESS_LIMIT_I16 0 // I16 mode (special case)
|
||||
#define FLATNESS_LIMIT_I4 3 // I4 mode
|
||||
#define FLATNESS_LIMIT_UV 2 // UV mode
|
||||
#define FLATNESS_PENALTY 140 // roughly ~1bit per block
|
||||
@@ -988,6 +988,7 @@ static void PickBestIntra16(VP8EncIterator* const it, VP8ModeScore* rd) {
|
||||
VP8ModeScore* rd_cur = &rd_tmp;
|
||||
VP8ModeScore* rd_best = rd;
|
||||
int mode;
|
||||
int is_flat = IsFlatSource16(it->yuv_in_ + Y_OFF_ENC);
|
||||
|
||||
rd->mode_i16 = -1;
|
||||
for (mode = 0; mode < NUM_PRED_MODES; ++mode) {
|
||||
@@ -1003,10 +1004,14 @@ static void PickBestIntra16(VP8EncIterator* const it, VP8ModeScore* rd) {
|
||||
tlambda ? MULT_8B(tlambda, VP8TDisto16x16(src, tmp_dst, kWeightY)) : 0;
|
||||
rd_cur->H = VP8FixedCostsI16[mode];
|
||||
rd_cur->R = VP8GetCostLuma16(it, rd_cur);
|
||||
if (mode > 0 &&
|
||||
IsFlat(rd_cur->y_ac_levels[0], kNumBlocks, FLATNESS_LIMIT_I16)) {
|
||||
// penalty to avoid flat area to be mispredicted by complex mode
|
||||
rd_cur->R += FLATNESS_PENALTY * kNumBlocks;
|
||||
if (is_flat) {
|
||||
// refine the first impression (which was in pixel space)
|
||||
is_flat = IsFlat(rd_cur->y_ac_levels[0], kNumBlocks, FLATNESS_LIMIT_I16);
|
||||
if (is_flat) {
|
||||
// Block is very flat. We put emphasis on the distortion being very low!
|
||||
rd_cur->D *= 2;
|
||||
rd_cur->SD *= 2;
|
||||
}
|
||||
}
|
||||
|
||||
// Since we always examine Intra16 first, we can overwrite *rd directly.
|
||||
@@ -1087,7 +1092,8 @@ static int PickBestIntra4(VP8EncIterator* const it, VP8ModeScore* const rd) {
|
||||
: 0;
|
||||
rd_tmp.H = mode_costs[mode];
|
||||
|
||||
// Add flatness penalty
|
||||
// Add flatness penalty, to avoid flat area to be mispredicted
|
||||
// by a complex mode.
|
||||
if (mode > 0 && IsFlat(tmp_levels, kNumBlocks, FLATNESS_LIMIT_I4)) {
|
||||
rd_tmp.R = FLATNESS_PENALTY * kNumBlocks;
|
||||
} else {
|
||||
@@ -1242,11 +1248,19 @@ static void RefineUsingDistortion(VP8EncIterator* const it,
|
||||
if (mode > 0 && VP8FixedCostsI16[mode] > bit_limit) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (score < best_score) {
|
||||
best_mode = mode;
|
||||
best_score = score;
|
||||
}
|
||||
}
|
||||
if (it->x_ == 0 || it->y_ == 0) {
|
||||
// avoid starting a checkerboard resonance from the border. See bug #432.
|
||||
if (IsFlatSource16(src)) {
|
||||
best_mode = (it->x_ == 0) ? 0 : 2;
|
||||
try_both_modes = 0; // stick to i16
|
||||
}
|
||||
}
|
||||
VP8SetIntra16Mode(it, best_mode);
|
||||
// we'll reconstruct later, if i16 mode actually gets selected
|
||||
}
|
||||
|
||||
Vendored
+1
-1
@@ -32,7 +32,7 @@ extern "C" {
|
||||
// version numbers
|
||||
#define ENC_MAJ_VERSION 1
|
||||
#define ENC_MIN_VERSION 0
|
||||
#define ENC_REV_VERSION 2
|
||||
#define ENC_REV_VERSION 3
|
||||
|
||||
enum { MAX_LF_LEVELS = 64, // Maximum loop filter level
|
||||
MAX_VARIABLE_LEVEL = 67, // last (inclusive) level with variable cost
|
||||
|
||||
Vendored
+1
-1
@@ -29,7 +29,7 @@ extern "C" {
|
||||
|
||||
#define MUX_MAJ_VERSION 1
|
||||
#define MUX_MIN_VERSION 0
|
||||
#define MUX_REV_VERSION 2
|
||||
#define MUX_REV_VERSION 3
|
||||
|
||||
// Chunk object.
|
||||
typedef struct WebPChunk WebPChunk;
|
||||
|
||||
+8
-3
@@ -104,7 +104,8 @@ void VP8LoadNewBytes(VP8BitReader* const br) {
|
||||
}
|
||||
|
||||
// Read a bit with proba 'prob'. Speed-critical function!
|
||||
static WEBP_INLINE int VP8GetBit(VP8BitReader* const br, int prob) {
|
||||
static WEBP_INLINE int VP8GetBit(VP8BitReader* const br,
|
||||
int prob, const char label[]) {
|
||||
// Don't move this declaration! It makes a big speed difference to store
|
||||
// 'range' *before* calling VP8LoadNewBytes(), even if this function doesn't
|
||||
// alter br->range_ value.
|
||||
@@ -129,13 +130,14 @@ static WEBP_INLINE int VP8GetBit(VP8BitReader* const br, int prob) {
|
||||
br->bits_ -= shift;
|
||||
}
|
||||
br->range_ = range - 1;
|
||||
BT_TRACK(br);
|
||||
return bit;
|
||||
}
|
||||
}
|
||||
|
||||
// simplified version of VP8GetBit() for prob=0x80 (note shift is always 1 here)
|
||||
static WEBP_UBSAN_IGNORE_UNSIGNED_OVERFLOW WEBP_INLINE
|
||||
int VP8GetSigned(VP8BitReader* const br, int v) {
|
||||
int VP8GetSigned(VP8BitReader* const br, int v, const char label[]) {
|
||||
if (br->bits_ < 0) {
|
||||
VP8LoadNewBytes(br);
|
||||
}
|
||||
@@ -148,11 +150,13 @@ int VP8GetSigned(VP8BitReader* const br, int v) {
|
||||
br->range_ += mask;
|
||||
br->range_ |= 1;
|
||||
br->value_ -= (bit_t)((split + 1) & mask) << pos;
|
||||
BT_TRACK(br);
|
||||
return (v ^ mask) - mask;
|
||||
}
|
||||
}
|
||||
|
||||
static WEBP_INLINE int VP8GetBitAlt(VP8BitReader* const br, int prob) {
|
||||
static WEBP_INLINE int VP8GetBitAlt(VP8BitReader* const br,
|
||||
int prob, const char label[]) {
|
||||
// Don't move this declaration! It makes a big speed difference to store
|
||||
// 'range' *before* calling VP8LoadNewBytes(), even if this function doesn't
|
||||
// alter br->range_ value.
|
||||
@@ -179,6 +183,7 @@ static WEBP_INLINE int VP8GetBitAlt(VP8BitReader* const br, int prob) {
|
||||
br->bits_ -= shift;
|
||||
}
|
||||
br->range_ = range;
|
||||
BT_TRACK(br);
|
||||
return bit;
|
||||
}
|
||||
}
|
||||
|
||||
+81
-5
@@ -102,17 +102,18 @@ void VP8LoadFinalBytes(VP8BitReader* const br) {
|
||||
//------------------------------------------------------------------------------
|
||||
// Higher-level calls
|
||||
|
||||
uint32_t VP8GetValue(VP8BitReader* const br, int bits) {
|
||||
uint32_t VP8GetValue(VP8BitReader* const br, int bits, const char label[]) {
|
||||
uint32_t v = 0;
|
||||
while (bits-- > 0) {
|
||||
v |= VP8GetBit(br, 0x80) << bits;
|
||||
v |= VP8GetBit(br, 0x80, label) << bits;
|
||||
}
|
||||
return v;
|
||||
}
|
||||
|
||||
int32_t VP8GetSignedValue(VP8BitReader* const br, int bits) {
|
||||
const int value = VP8GetValue(br, bits);
|
||||
return VP8Get(br) ? -value : value;
|
||||
int32_t VP8GetSignedValue(VP8BitReader* const br, int bits,
|
||||
const char label[]) {
|
||||
const int value = VP8GetValue(br, bits, label);
|
||||
return VP8Get(br, label) ? -value : value;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
@@ -220,3 +221,78 @@ uint32_t VP8LReadBits(VP8LBitReader* const br, int n_bits) {
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Bit-tracing tool
|
||||
|
||||
#if (BITTRACE > 0)
|
||||
|
||||
#include <stdlib.h> // for atexit()
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#define MAX_NUM_LABELS 32
|
||||
static struct {
|
||||
const char* label;
|
||||
int size;
|
||||
int count;
|
||||
} kLabels[MAX_NUM_LABELS];
|
||||
|
||||
static int last_label = 0;
|
||||
static int last_pos = 0;
|
||||
static const uint8_t* buf_start = NULL;
|
||||
static int init_done = 0;
|
||||
|
||||
static void PrintBitTraces(void) {
|
||||
int i;
|
||||
int scale = 1;
|
||||
int total = 0;
|
||||
const char* units = "bits";
|
||||
#if (BITTRACE == 2)
|
||||
scale = 8;
|
||||
units = "bytes";
|
||||
#endif
|
||||
for (i = 0; i < last_label; ++i) total += kLabels[i].size;
|
||||
if (total < 1) total = 1; // avoid rounding errors
|
||||
printf("=== Bit traces ===\n");
|
||||
for (i = 0; i < last_label; ++i) {
|
||||
const int skip = 16 - (int)strlen(kLabels[i].label);
|
||||
const int value = (kLabels[i].size + scale - 1) / scale;
|
||||
assert(skip > 0);
|
||||
printf("%s \%*s: %6d %s \t[%5.2f%%] [count: %7d]\n",
|
||||
kLabels[i].label, skip, "", value, units,
|
||||
100.f * kLabels[i].size / total,
|
||||
kLabels[i].count);
|
||||
}
|
||||
total = (total + scale - 1) / scale;
|
||||
printf("Total: %d %s\n", total, units);
|
||||
}
|
||||
|
||||
void BitTrace(const struct VP8BitReader* const br, const char label[]) {
|
||||
int i, pos;
|
||||
if (!init_done) {
|
||||
memset(kLabels, 0, sizeof(kLabels));
|
||||
atexit(PrintBitTraces);
|
||||
buf_start = br->buf_;
|
||||
init_done = 1;
|
||||
}
|
||||
pos = (int)(br->buf_ - buf_start) * 8 - br->bits_;
|
||||
// if there's a too large jump, we've changed partition -> reset counter
|
||||
if (abs(pos - last_pos) > 32) {
|
||||
buf_start = br->buf_;
|
||||
pos = 0;
|
||||
last_pos = 0;
|
||||
}
|
||||
if (br->range_ >= 0x7f) pos += kVP8Log2Range[br->range_ - 0x7f];
|
||||
for (i = 0; i < last_label; ++i) {
|
||||
if (!strcmp(label, kLabels[i].label)) break;
|
||||
}
|
||||
if (i == MAX_NUM_LABELS) abort(); // overflow!
|
||||
kLabels[i].label = label;
|
||||
kLabels[i].size += pos - last_pos;
|
||||
kLabels[i].count += 1;
|
||||
if (i == last_label) ++last_label;
|
||||
last_pos = pos;
|
||||
}
|
||||
|
||||
#endif // BITTRACE > 0
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
+26
-7
@@ -21,6 +21,27 @@
|
||||
#endif
|
||||
#include "src/webp/types.h"
|
||||
|
||||
// Warning! This macro triggers quite some MACRO wizardry around func signature!
|
||||
#if !defined(BITTRACE)
|
||||
#define BITTRACE 0 // 0 = off, 1 = print bits, 2 = print bytes
|
||||
#endif
|
||||
|
||||
#if (BITTRACE > 0)
|
||||
struct VP8BitReader;
|
||||
extern void BitTrace(const struct VP8BitReader* const br, const char label[]);
|
||||
#define BT_TRACK(br) BitTrace(br, label)
|
||||
#define VP8Get(BR, L) VP8GetValue(BR, 1, L)
|
||||
#else
|
||||
#define BT_TRACK(br)
|
||||
// We'll REMOVE the 'const char label[]' from all signatures and calls (!!):
|
||||
#define VP8GetValue(BR, N, L) VP8GetValue(BR, N)
|
||||
#define VP8Get(BR, L) VP8GetValue(BR, 1, L)
|
||||
#define VP8GetSignedValue(BR, N, L) VP8GetSignedValue(BR, N)
|
||||
#define VP8GetBit(BR, P, L) VP8GetBit(BR, P)
|
||||
#define VP8GetBitAlt(BR, P, L) VP8GetBitAlt(BR, P)
|
||||
#define VP8GetSigned(BR, V, L) VP8GetSigned(BR, V)
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
@@ -92,17 +113,15 @@ void VP8BitReaderSetBuffer(VP8BitReader* const br,
|
||||
void VP8RemapBitReader(VP8BitReader* const br, ptrdiff_t offset);
|
||||
|
||||
// return the next value made of 'num_bits' bits
|
||||
uint32_t VP8GetValue(VP8BitReader* const br, int num_bits);
|
||||
static WEBP_INLINE uint32_t VP8Get(VP8BitReader* const br) {
|
||||
return VP8GetValue(br, 1);
|
||||
}
|
||||
uint32_t VP8GetValue(VP8BitReader* const br, int num_bits, const char label[]);
|
||||
|
||||
// return the next value with sign-extension.
|
||||
int32_t VP8GetSignedValue(VP8BitReader* const br, int num_bits);
|
||||
int32_t VP8GetSignedValue(VP8BitReader* const br, int num_bits,
|
||||
const char label[]);
|
||||
|
||||
// bit_reader_inl.h will implement the following methods:
|
||||
// static WEBP_INLINE int VP8GetBit(VP8BitReader* const br, int prob)
|
||||
// static WEBP_INLINE int VP8GetSigned(VP8BitReader* const br, int v)
|
||||
// static WEBP_INLINE int VP8GetBit(VP8BitReader* const br, int prob, ...)
|
||||
// static WEBP_INLINE int VP8GetSigned(VP8BitReader* const br, int v, ...)
|
||||
// and should be included by the .c files that actually need them.
|
||||
// This is to avoid recompiling the whole library whenever this file is touched,
|
||||
// and also allowing platform-specific ad-hoc hacks.
|
||||
|
||||
+1
-1
@@ -70,7 +70,7 @@ static void Flush(VP8BitWriter* const bw) {
|
||||
const int value = (bits & 0x100) ? 0x00 : 0xff;
|
||||
for (; bw->run_ > 0; --bw->run_) bw->buf_[pos++] = value;
|
||||
}
|
||||
bw->buf_[pos++] = bits;
|
||||
bw->buf_[pos++] = bits & 0xff;
|
||||
bw->pos_ = pos;
|
||||
} else {
|
||||
bw->run_++; // delay writing of bytes 0xff, pending eventual carry.
|
||||
|
||||
+5
-3
@@ -17,6 +17,7 @@
|
||||
|
||||
#include <assert.h>
|
||||
|
||||
#include "src/dsp/dsp.h"
|
||||
#include "src/webp/types.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
@@ -30,10 +31,11 @@ typedef struct {
|
||||
int hash_bits_;
|
||||
} VP8LColorCache;
|
||||
|
||||
static const uint64_t kHashMul = 0x1e35a7bdull;
|
||||
static const uint32_t kHashMul = 0x1e35a7bdu;
|
||||
|
||||
static WEBP_INLINE int VP8LHashPix(uint32_t argb, int shift) {
|
||||
return (int)(((argb * kHashMul) & 0xffffffffu) >> shift);
|
||||
static WEBP_UBSAN_IGNORE_UNSIGNED_OVERFLOW WEBP_INLINE
|
||||
int VP8LHashPix(uint32_t argb, int shift) {
|
||||
return (int)((argb * kHashMul) >> shift);
|
||||
}
|
||||
|
||||
static WEBP_INLINE uint32_t VP8LColorCacheLookup(
|
||||
|
||||
+19
-7
@@ -91,7 +91,8 @@ static int BuildHuffmanTable(HuffmanCode* const root_table, int root_bits,
|
||||
|
||||
assert(code_lengths_size != 0);
|
||||
assert(code_lengths != NULL);
|
||||
assert(root_table != NULL);
|
||||
assert((root_table != NULL && sorted != NULL) ||
|
||||
(root_table == NULL && sorted == NULL));
|
||||
assert(root_bits > 0);
|
||||
|
||||
// Build histogram of code lengths.
|
||||
@@ -120,16 +121,22 @@ static int BuildHuffmanTable(HuffmanCode* const root_table, int root_bits,
|
||||
for (symbol = 0; symbol < code_lengths_size; ++symbol) {
|
||||
const int symbol_code_length = code_lengths[symbol];
|
||||
if (code_lengths[symbol] > 0) {
|
||||
sorted[offset[symbol_code_length]++] = symbol;
|
||||
if (sorted != NULL) {
|
||||
sorted[offset[symbol_code_length]++] = symbol;
|
||||
} else {
|
||||
offset[symbol_code_length]++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Special case code with only one value.
|
||||
if (offset[MAX_ALLOWED_CODE_LENGTH] == 1) {
|
||||
HuffmanCode code;
|
||||
code.bits = 0;
|
||||
code.value = (uint16_t)sorted[0];
|
||||
ReplicateValue(table, 1, total_size, code);
|
||||
if (sorted != NULL) {
|
||||
HuffmanCode code;
|
||||
code.bits = 0;
|
||||
code.value = (uint16_t)sorted[0];
|
||||
ReplicateValue(table, 1, total_size, code);
|
||||
}
|
||||
return total_size;
|
||||
}
|
||||
|
||||
@@ -151,6 +158,7 @@ static int BuildHuffmanTable(HuffmanCode* const root_table, int root_bits,
|
||||
if (num_open < 0) {
|
||||
return 0;
|
||||
}
|
||||
if (root_table == NULL) continue;
|
||||
for (; count[len] > 0; --count[len]) {
|
||||
HuffmanCode code;
|
||||
code.bits = (uint8_t)len;
|
||||
@@ -169,6 +177,7 @@ static int BuildHuffmanTable(HuffmanCode* const root_table, int root_bits,
|
||||
if (num_open < 0) {
|
||||
return 0;
|
||||
}
|
||||
if (root_table == NULL) continue;
|
||||
for (; count[len] > 0; --count[len]) {
|
||||
HuffmanCode code;
|
||||
if ((key & mask) != low) {
|
||||
@@ -206,7 +215,10 @@ int VP8LBuildHuffmanTable(HuffmanCode* const root_table, int root_bits,
|
||||
const int code_lengths[], int code_lengths_size) {
|
||||
int total_size;
|
||||
assert(code_lengths_size <= MAX_CODE_LENGTHS_SIZE);
|
||||
if (code_lengths_size <= SORTED_SIZE_CUTOFF) {
|
||||
if (root_table == NULL) {
|
||||
total_size = BuildHuffmanTable(NULL, root_bits,
|
||||
code_lengths, code_lengths_size, NULL);
|
||||
} else if (code_lengths_size <= SORTED_SIZE_CUTOFF) {
|
||||
// use local stack-allocated array.
|
||||
uint16_t sorted[SORTED_SIZE_CUTOFF];
|
||||
total_size = BuildHuffmanTable(root_table, root_bits,
|
||||
|
||||
+2
@@ -78,6 +78,8 @@ void VP8LHtreeGroupsFree(HTreeGroup* const htree_groups);
|
||||
// the huffman table.
|
||||
// Returns built table size or 0 in case of error (invalid tree or
|
||||
// memory error).
|
||||
// If root_table is NULL, it returns 0 if a lookup cannot be built, something
|
||||
// > 0 otherwise (but not the table size).
|
||||
int VP8LBuildHuffmanTable(HuffmanCode* const root_table, int root_bits,
|
||||
const int code_lengths[], int code_lengths_size);
|
||||
|
||||
|
||||
+4
-4
@@ -84,14 +84,14 @@ int WebPRescalerGetScaledDimensions(int src_width, int src_height,
|
||||
int height = *scaled_height;
|
||||
|
||||
// if width is unspecified, scale original proportionally to height ratio.
|
||||
if (width == 0) {
|
||||
if (width == 0 && src_height > 0) {
|
||||
width =
|
||||
(int)(((uint64_t)src_width * height + src_height / 2) / src_height);
|
||||
(int)(((uint64_t)src_width * height + src_height - 1) / src_height);
|
||||
}
|
||||
// if height is unspecified, scale original proportionally to width ratio.
|
||||
if (height == 0) {
|
||||
if (height == 0 && src_width > 0) {
|
||||
height =
|
||||
(int)(((uint64_t)src_height * width + src_width / 2) / src_width);
|
||||
(int)(((uint64_t)src_height * width + src_width - 1) / src_width);
|
||||
}
|
||||
// Check if the overall dimensions still make sense.
|
||||
if (width <= 0 || height <= 0) {
|
||||
|
||||
+11
-1
@@ -217,8 +217,12 @@ static THREADFN ThreadLoop(void* ptr) {
|
||||
done = 1;
|
||||
}
|
||||
// signal to the main thread that we're done (for Sync())
|
||||
pthread_cond_signal(&impl->condition_);
|
||||
// Note the associated mutex does not need to be held when signaling the
|
||||
// condition. Unlocking the mutex first may improve performance in some
|
||||
// implementations, avoiding the case where the waiting thread can't
|
||||
// reacquire the mutex when woken.
|
||||
pthread_mutex_unlock(&impl->mutex_);
|
||||
pthread_cond_signal(&impl->condition_);
|
||||
}
|
||||
return THREAD_RETURN(NULL); // Thread is finished
|
||||
}
|
||||
@@ -240,7 +244,13 @@ static void ChangeState(WebPWorker* const worker, WebPWorkerStatus new_status) {
|
||||
// assign new status and release the working thread if needed
|
||||
if (new_status != OK) {
|
||||
worker->status_ = new_status;
|
||||
// Note the associated mutex does not need to be held when signaling the
|
||||
// condition. Unlocking the mutex first may improve performance in some
|
||||
// implementations, avoiding the case where the waiting thread can't
|
||||
// reacquire the mutex when woken.
|
||||
pthread_mutex_unlock(&impl->mutex_);
|
||||
pthread_cond_signal(&impl->condition_);
|
||||
return;
|
||||
}
|
||||
}
|
||||
pthread_mutex_unlock(&impl->mutex_);
|
||||
|
||||
Vendored
+3
-3
@@ -92,14 +92,14 @@ static WEBP_INLINE uint32_t GetLE32(const uint8_t* const data) {
|
||||
// Store 16, 24 or 32 bits in little-endian order.
|
||||
static WEBP_INLINE void PutLE16(uint8_t* const data, int val) {
|
||||
assert(val < (1 << 16));
|
||||
data[0] = (val >> 0);
|
||||
data[1] = (val >> 8);
|
||||
data[0] = (val >> 0) & 0xff;
|
||||
data[1] = (val >> 8) & 0xff;
|
||||
}
|
||||
|
||||
static WEBP_INLINE void PutLE24(uint8_t* const data, int val) {
|
||||
assert(val < (1 << 24));
|
||||
PutLE16(data, val & 0xffff);
|
||||
data[2] = (val >> 16);
|
||||
data[2] = (val >> 16) & 0xff;
|
||||
}
|
||||
|
||||
static WEBP_INLINE void PutLE32(uint8_t* const data, uint32_t val) {
|
||||
|
||||
Vendored
+4
@@ -62,6 +62,10 @@ WEBP_EXTERN size_t WebPEncodeBGRA(const uint8_t* bgra,
|
||||
// These functions are the equivalent of the above, but compressing in a
|
||||
// lossless manner. Files are usually larger than lossy format, but will
|
||||
// not suffer any compression loss.
|
||||
// Note these functions, like the lossy versions, use the library's default
|
||||
// settings. For lossless this means 'exact' is disabled. RGB values in
|
||||
// transparent areas will be modified to improve compression. To avoid this,
|
||||
// use WebPEncode() and set WebPConfig::exact to 1.
|
||||
WEBP_EXTERN size_t WebPEncodeLosslessRGB(const uint8_t* rgb,
|
||||
int width, int height, int stride,
|
||||
uint8_t** output);
|
||||
|
||||
Vendored
+1
-1
@@ -45,7 +45,7 @@
|
||||
|
||||
|
||||
// GCC and Visual Studio SSE2 compiler flags
|
||||
#if defined __SSE2__ || (_MSC_VER >= 1300 && !_M_CEE_PURE)
|
||||
#if defined __SSE2__ || (defined(_M_X64) || _M_IX86_FP == 2)
|
||||
#define IMF_HAVE_SSE2 1
|
||||
#endif
|
||||
|
||||
|
||||
Vendored
+1
-1
@@ -5,7 +5,7 @@ target_include_directories(openvx_hal PUBLIC
|
||||
${CMAKE_SOURCE_DIR}/modules/core/include
|
||||
${CMAKE_SOURCE_DIR}/modules/imgproc/include
|
||||
${OPENVX_INCLUDE_DIR})
|
||||
target_link_libraries(openvx_hal LINK_PUBLIC ${OPENVX_LIBRARIES})
|
||||
target_link_libraries(openvx_hal PUBLIC ${OPENVX_LIBRARIES})
|
||||
set_target_properties(openvx_hal PROPERTIES ARCHIVE_OUTPUT_DIRECTORY ${3P_LIBRARY_OUTPUT_PATH})
|
||||
if(NOT BUILD_SHARED_LIBS)
|
||||
ocv_install_target(openvx_hal EXPORT OpenCVModules ARCHIVE DESTINATION ${OPENCV_3P_LIB_INSTALL_PATH} COMPONENT dev)
|
||||
|
||||
Vendored
+1
@@ -22,6 +22,7 @@ else()
|
||||
-Wenum-compare-switch
|
||||
-Wsuggest-override -Winconsistent-missing-override
|
||||
-Wimplicit-fallthrough
|
||||
-Warray-bounds # GCC 9+
|
||||
)
|
||||
endif()
|
||||
if(CV_ICC)
|
||||
|
||||
Vendored
+6
-3
@@ -5,10 +5,11 @@ if (WIN32 AND NOT ARM)
|
||||
message(FATAL_ERROR "BUILD_TBB option supports Windows on ARM only!\nUse regular official TBB build instead of the BUILD_TBB option!")
|
||||
endif()
|
||||
|
||||
ocv_update(OPENCV_TBB_RELEASE "2019_U8")
|
||||
ocv_update(OPENCV_TBB_RELEASE_MD5 "7c371d0f62726154d2c568a85697a0ad")
|
||||
ocv_update(OPENCV_TBB_RELEASE "v2020.0")
|
||||
ocv_update(OPENCV_TBB_RELEASE_MD5 "5858dd01ec007c139d5d178b21e06dae")
|
||||
ocv_update(OPENCV_TBB_FILENAME "${OPENCV_TBB_RELEASE}.tar.gz")
|
||||
ocv_update(OPENCV_TBB_SUBDIR "tbb-${OPENCV_TBB_RELEASE}")
|
||||
string(REGEX REPLACE "^v" "" OPENCV_TBB_RELEASE_ "${OPENCV_TBB_RELEASE}")
|
||||
ocv_update(OPENCV_TBB_SUBDIR "tbb-${OPENCV_TBB_RELEASE_}")
|
||||
|
||||
set(tbb_src_dir "${OpenCV_BINARY_DIR}/3rdparty/tbb")
|
||||
ocv_download(FILENAME ${OPENCV_TBB_FILENAME}
|
||||
@@ -34,10 +35,12 @@ ocv_include_directories("${tbb_src_dir}/include"
|
||||
file(GLOB lib_srcs "${tbb_src_dir}/src/tbb/*.cpp")
|
||||
file(GLOB lib_hdrs "${tbb_src_dir}/src/tbb/*.h")
|
||||
list(APPEND lib_srcs "${tbb_src_dir}/src/rml/client/rml_tbb.cpp")
|
||||
ocv_list_filterout(lib_srcs "${tbb_src_dir}/src/tbb/tbbbind.cpp") # hwloc.h requirement
|
||||
|
||||
if (WIN32)
|
||||
add_definitions(/D__TBB_DYNAMIC_LOAD_ENABLED=0
|
||||
/D__TBB_BUILD=1
|
||||
/DTBB_SUPPRESS_DEPRECATED_MESSAGES=1
|
||||
/DTBB_NO_LEGACY=1
|
||||
/D_UNICODE
|
||||
/DUNICODE
|
||||
|
||||
+30
-2
@@ -275,6 +275,9 @@ OCV_OPTION(WITH_VULKAN "Include Vulkan support" OFF
|
||||
OCV_OPTION(WITH_INF_ENGINE "Include Intel Inference Engine support" OFF
|
||||
VISIBLE_IF TRUE
|
||||
VERIFY INF_ENGINE_TARGET)
|
||||
OCV_OPTION(WITH_NGRAPH "Include nGraph support" WITH_INF_ENGINE
|
||||
VISIBLE_IF TRUE
|
||||
VERIFY TARGET ngraph::ngraph)
|
||||
OCV_OPTION(WITH_JASPER "Include JPEG2K support" ON
|
||||
VISIBLE_IF NOT IOS
|
||||
VERIFY HAVE_JASPER)
|
||||
@@ -1423,12 +1426,37 @@ if(WITH_INF_ENGINE OR INF_ENGINE_TARGET)
|
||||
)
|
||||
get_target_property(_inc ${ie_target} INTERFACE_INCLUDE_DIRECTORIES)
|
||||
status(" Inference Engine:" "${__msg}")
|
||||
status(" libs:" "${_lib}")
|
||||
status(" includes:" "${_inc}")
|
||||
status(" * libs:" "${_lib}")
|
||||
status(" * includes:" "${_inc}")
|
||||
else()
|
||||
status(" Inference Engine:" "NO")
|
||||
endif()
|
||||
endif()
|
||||
if(WITH_NGRAPH OR HAVE_NGRAPH)
|
||||
if(HAVE_NGRAPH)
|
||||
set(__target ngraph::ngraph)
|
||||
set(__msg "YES (${ngraph_VERSION})")
|
||||
get_target_property(_lib ${__target} IMPORTED_LOCATION)
|
||||
get_target_property(_lib_imp_rel ${__target} IMPORTED_IMPLIB_RELEASE)
|
||||
get_target_property(_lib_imp_dbg ${__target} IMPORTED_IMPLIB_DEBUG)
|
||||
get_target_property(_lib_rel ${__target} IMPORTED_LOCATION_RELEASE)
|
||||
get_target_property(_lib_dbg ${__target} IMPORTED_LOCATION_DEBUG)
|
||||
ocv_build_features_string(_lib
|
||||
IF _lib THEN "${_lib}"
|
||||
IF _lib_imp_rel AND _lib_imp_dbg THEN "${_lib_imp_rel} / ${_lib_imp_dbg}"
|
||||
IF _lib_rel AND _lib_dbg THEN "${_lib_rel} / ${_lib_dbg}"
|
||||
IF _lib_rel THEN "${_lib_rel}"
|
||||
IF _lib_dbg THEN "${_lib_dbg}"
|
||||
ELSE "unknown"
|
||||
)
|
||||
get_target_property(_inc ${__target} INTERFACE_INCLUDE_DIRECTORIES)
|
||||
status(" nGraph:" "${__msg}")
|
||||
status(" * libs:" "${_lib}")
|
||||
status(" * includes:" "${_inc}")
|
||||
else()
|
||||
status(" nGraph:" "NO")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(WITH_EIGEN OR HAVE_EIGEN)
|
||||
status(" Eigen:" HAVE_EIGEN THEN "YES (ver ${EIGEN_WORLD_VERSION}.${EIGEN_MAJOR_VERSION}.${EIGEN_MINOR_VERSION})" ELSE NO)
|
||||
|
||||
@@ -13,6 +13,7 @@ Copyright (C) 2009-2016, NVIDIA Corporation, all rights reserved.
|
||||
Copyright (C) 2010-2013, Advanced Micro Devices, Inc., all rights reserved.
|
||||
Copyright (C) 2015-2016, OpenCV Foundation, all rights reserved.
|
||||
Copyright (C) 2015-2016, Itseez Inc., all rights reserved.
|
||||
Copyright (C) 2019, Xperience AI, all rights reserved.
|
||||
Third party copyrights are property of their respective owners.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without modification,
|
||||
|
||||
+74
@@ -0,0 +1,74 @@
|
||||
# Security Policy
|
||||
|
||||
## Reporting a Vulnerability
|
||||
|
||||
If you have information about a security issue or vulnerability in OpenCV, please send an e-mail to security@opencv.org.
|
||||
|
||||
Please provide as much information as possible:
|
||||
|
||||
- A detailed description of the vulnerability we can use to reproduce your findings.
|
||||
|
||||
- Who can exploit this vulnerability and what would they gain. An attack scenario.
|
||||
|
||||
- Information about known exploits if any.
|
||||
|
||||
A member of the Security Team will review your e-mail and contact you to collaborate on resolving the issue.
|
||||
|
||||
## PGP Key
|
||||
|
||||
If a security vulnerability report has extremely sensitive information you may encrypt it using our PGP public key:
|
||||
|
||||
```
|
||||
-----BEGIN PGP PUBLIC KEY BLOCK-----
|
||||
|
||||
mQINBF3D4kMBEADMujKXgYtH1RFA25OTz+AFVZJlsQj8vC9WqKxvx7lluU33J8H0
|
||||
QUTwqysupVKmeLLdhWLrhMdN22qV33rn/Ba4VaosIkjBs6TSJg752J9qK07C+ps5
|
||||
4a8DDiqUV5B5aOO1ZI82rcw0EB4uTyJdsynY2zs1ylfNA3UothN30nSmv8HK22wO
|
||||
rt7R0/W29AoREihDWiGsTm6PxPA/jGN9WawqzVZF4Iv5kC6jFSojoyj1vVDn/55l
|
||||
JcjjRD88tSvjLeJE4vVgoN5LzvaNnwHOv8z+wDakt7cc/BLO0GdUOZvo5iBqv9sF
|
||||
y/6G4SULEY+LD1ByTEjajZCkfzURq7qVdRehseplOO5qPLDYU1PumzFYMqhkGRcu
|
||||
yH0q1bnqzKilARYCDUjlfPinQR6ko1CZ6aFCVPXHjs6btJ3kiP7SohjO8maO8IXD
|
||||
DvHS/qzxQ4NiqunSd8/Nh54WhckV3t+u1UUY7sesN+BDFHcpPOwvjubSpmoBT1TC
|
||||
lSduVUfR754uxiZTkNB19+J8omWCg6lJsXysZjRwypGv5cUUFiv+HM+4m2Th4Hpx
|
||||
hVvTwzwkhWDq91DkuHwtfpyikhSaCqMrvZ+cJ6QCUSssYwR3unPbhJ7E7HVUA114
|
||||
KgBHBcx9ZBJXcuYeztC4sRPOcvPc7s9ODzT1zSJpyqxJ1XMwjH4tir311QARAQAB
|
||||
tCVPcGVuQ1YgU2VjdXJpdHkgPHNlY3VyaXR5QG9wZW5jdi5vcmc+iQJOBBMBCgA4
|
||||
FiEEWsPgWpOWlzgrzgzn8YQhBPGCecsFAl3D4kMCGwMFCwkIBwIGFQoJCAsCBBYC
|
||||
AwECHgECF4AACgkQ8YQhBPGCecsweBAAl2dWcAJuLvLFDrQsTfYy8SgUvr88SZai
|
||||
uxOkAfdmYeuttE6pONb9/u4zngZM9cVkDAKP4M8C852QaO4TryKT4q5LGwYtAIjF
|
||||
OBFVUebAuf+huL/e6YCI9FDWy3hj4aeMdWg8lphNe7niNfICYJ5nTSIvly3rWFUm
|
||||
aWTV9kVJVT0oH9Cw96+XLahOdAWsFDurBy0prLcqjYuNd6bO/j3hqm+GCrMZTbKG
|
||||
21oNYoKO4+6T12Fy9ZCxnQWcguNdIpeDJ93O18R3A92zwxQlYchcyzqcSAXzQZGs
|
||||
nctYrxqTOcrOV0HoYRTQSct5hoUPK6v9gOP9CJ1rzHf5l88/mD0cqT12IbpYPXYg
|
||||
/AldZBUljTatjDy74v2GX4wktkuDhT2NN0sJJ6deFQuqoBbG8ODo8XemOKENc/kA
|
||||
VvUeroI7Jc6P1eIfTM9rOCR6w/wCl9YM+2v3pV0eQ/yn2SLEYH6uT/fCzCEYY+Kh
|
||||
G/uw5e4BjKjMgqIyDO/QiFaQIcTgpxgt8hze6U2lHTEoNvVbd7Xw99x+aN9BYeNz
|
||||
DGNao+gwDAo+jZrcg99+nwW/+ecFRAmHZ0iCDRJISR55j4CKLMaGKvnNF4P43FqU
|
||||
Ug++9lv7gPWFU96o+p58omfkamVT6lp+LgZHr+YTBgCOMjD1ljaIES4rvVolCNLv
|
||||
3pXe63k8J5y5Ag0EXcPiQwEQALkUn+px7kTTKdHY8HRTakgoj1FD0sRS8O1r8Y1b
|
||||
UJ2lZCkHlJc1zsyR0EnZascsW3IMlzeU1mcogR9HPogFkotd0pmkwheXKPWfwhfJ
|
||||
EQ48p8oYwtTzwcd6+Fd6nYs9Uj1fSlnkQ7aAxSOhxZsUwYuAUMZPTK4caFENneGO
|
||||
8lsjrC8aS3/DE1QMdafxd2mYDqigMarVU7UfZi5db5rb6NwUOhE65qmXC7cCrGRw
|
||||
uC7AMr/xybND0pWgWMWFPNOdKKS0Vsx6Wm3uJANXHz/28d4ozhn+midht+BBHFLa
|
||||
kzs0eA2dygVNoVqthjWZoT7fDQJ1t9BGH6oQQVJIhPNkM0LZt0Egpq3K025F9GW7
|
||||
JMlUG78ptKogroaVqQcFBGOxMS6J2axHXyd7NFulzucH48y0HCkJi2CyavNabHAF
|
||||
Nr5d+1M1az4AzFHoeMD1OcUSO2klnZqSR5X4JJm4gBeNAAPOJ9nNyo7lQOgG50Kq
|
||||
buwwEY1rBFDfi55pF+RzvqbKUxPyvaz5LoK7ZJWWlYmf7r61c7IeE+vmXGLcqbRh
|
||||
IViJMCh+l22EdjYyX/A4QW6fuYDPgJR9CD97mMK5SsTZnxGAKO+qOqD/D7SWh7sR
|
||||
MV3dJ0M+230kykA0Rdb3COOFmrw4oae9yUVFUN90hklB7ulK0uBXWfrvHmporD/z
|
||||
/AK3ABEBAAGJAjYEGAEKACAWIQRaw+Bak5aXOCvODOfxhCEE8YJ5ywUCXcPiQwIb
|
||||
DAAKCRDxhCEE8YJ5y0AqD/97VKV3aLihstSfZtnDo1kiafEmmoRX4uFRh2pqcuVn
|
||||
Ex8bqKTljy0jHCZfS4W+/imB2o6mubfygdjB/sKCUAknehyuuPTTFGIMUTvx5WJe
|
||||
S3c3Gdg9r3zCIge3lXw2lhGIn+obo2Y069/8CBVKSEoFl9mtoyXIVHe/E6tqzkb4
|
||||
3xPnjXbUt0jsWGqDJf5qPN4kjcwBpYXs6ayZy8LCm1i4IwMj1PtbkKPmXAGe6wJi
|
||||
5aGBxtMFiNcEH3+bW8vE8RIe/7dy3G/1n/VfhEBL05HCwxmM6I6S6tgZ1vE0k101
|
||||
3jL5a/9RDEYk9abHb1eXEwWUk2FVrzC87qH76OU6TXU8pV8Xp8dLv4U3meA5aNqm
|
||||
vkAv1If6/TR1yTqMDVD1mRkBL+dawRAUWtSed4QZeJf83AF+zFPGRn5TMB98AvTg
|
||||
QE3FDdxdkygOb9jx5reJ4SO1N0pMy8Enb8Xoe/dHIYmTncceQkMlQWMaBvYEzuVh
|
||||
mZd5GRHeogRx14tGxlRSc95wW9o0UN4fXxFBDDJDjrJVQQ3DqzK2Ca7Y53DzGODK
|
||||
d++Epc3Ef7lPex4eIIbqyup8X0SNiQelV91j3IyUEi3NZxSX9LQzSzG705gohpCz
|
||||
9va7NjxWCjwrE3x9PJD6KYdgcaNlbPaeQMLt2/fJKEBraJDHTU8buBaJJaC4psIj
|
||||
7Q==
|
||||
=Mdna
|
||||
-----END PGP PUBLIC KEY BLOCK-----
|
||||
```
|
||||
@@ -288,7 +288,7 @@ if(X86 OR X86_64)
|
||||
ocv_update(CPU_AVX2_FLAGS_ON "/arch:AVX2")
|
||||
ocv_update(CPU_AVX_FLAGS_ON "/arch:AVX")
|
||||
ocv_update(CPU_FP16_FLAGS_ON "/arch:AVX")
|
||||
if(NOT MSVC64)
|
||||
if(NOT X86_64)
|
||||
# 64-bit MSVC compiler uses SSE/SSE2 by default
|
||||
ocv_update(CPU_SSE_FLAGS_ON "/arch:SSE")
|
||||
ocv_update(CPU_SSE_SUPPORTED ON)
|
||||
@@ -346,7 +346,7 @@ elseif(MIPS)
|
||||
ocv_update(CPU_MSA_TEST_FILE "${OpenCV_SOURCE_DIR}/cmake/checks/cpu_msa.cpp")
|
||||
ocv_update(CPU_KNOWN_OPTIMIZATIONS "MSA")
|
||||
ocv_update(CPU_MSA_FLAGS_ON "-mmsa")
|
||||
set(CPU_BASELINE "MSA" CACHE STRING "${HELP_CPU_BASELINE}")
|
||||
set(CPU_BASELINE "DETECT" CACHE STRING "${HELP_CPU_BASELINE}")
|
||||
elseif(PPC64LE)
|
||||
ocv_update(CPU_KNOWN_OPTIMIZATIONS "VSX;VSX3")
|
||||
ocv_update(CPU_VSX_TEST_FILE "${OpenCV_SOURCE_DIR}/cmake/checks/cpu_vsx.cpp")
|
||||
@@ -714,7 +714,10 @@ macro(ocv_compiler_optimization_process_sources SOURCES_VAR_NAME LIBS_VAR_NAME T
|
||||
foreach(OPT ${CPU_DISPATCH_FINAL})
|
||||
if(__result_${OPT})
|
||||
#message("${OPT}: ${__result_${OPT}}")
|
||||
if(CMAKE_GENERATOR MATCHES "^Visual")
|
||||
if(CMAKE_GENERATOR MATCHES "^Visual"
|
||||
OR OPENCV_CMAKE_CPU_OPTIMIZATIONS_FORCE_TARGETS
|
||||
)
|
||||
# MSVS generator is not able to properly order compilation flags:
|
||||
# extra flags are added before common flags, so switching between optimizations doesn't work correctly
|
||||
# Also CMAKE_CXX_FLAGS doesn't work (it is directory-based, so add_subdirectory is required)
|
||||
add_library(${TARGET_BASE_NAME}_${OPT} OBJECT ${__result_${OPT}})
|
||||
|
||||
@@ -385,6 +385,19 @@ if(MSVC)
|
||||
add_definitions(-D_VARIADIC_MAX=10)
|
||||
endif()
|
||||
|
||||
if(CMAKE_SYSTEM_NAME STREQUAL "Windows")
|
||||
get_directory_property(__DIRECTORY_COMPILE_DEFINITIONS COMPILE_DEFINITIONS)
|
||||
if((NOT " ${CMAKE_CXX_FLAGS} ${CMAKE_CXX_FLAGS_RELEASE} ${OPENCV_EXTRA_CXX_FLAGS} ${OPENCV_EXTRA_FLAGS_RELEASE} ${__DIRECTORY_COMPILE_DEFINITIONS}" MATCHES "_WIN32_WINNT"
|
||||
AND NOT OPENCV_CMAKE_SKIP_MACRO_WIN32_WINNT)
|
||||
OR OPENCV_CMAKE_FORCE_MACRO_WIN32_WINNT
|
||||
)
|
||||
# https://docs.microsoft.com/en-us/cpp/porting/modifying-winver-and-win32-winnt
|
||||
# Target Windows 7 API
|
||||
set(OPENCV_CMAKE_MACRO_WIN32_WINNT "0x0601" CACHE STRING "Value of _WIN32_WINNT macro")
|
||||
add_definitions(-D_WIN32_WINNT=${OPENCV_CMAKE_MACRO_WIN32_WINNT})
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# Enable compiler options for OpenCV modules/apps/samples only (ignore 3rdparty)
|
||||
macro(ocv_add_modules_compiler_options)
|
||||
if(MSVC AND NOT OPENCV_SKIP_MSVC_W4_OPTION)
|
||||
|
||||
@@ -12,7 +12,7 @@ endif()
|
||||
if(((NOT CMAKE_VERSION VERSION_LESS "3.9.0") # requires https://gitlab.kitware.com/cmake/cmake/merge_requests/663
|
||||
OR OPENCV_CUDA_FORCE_EXTERNAL_CMAKE_MODULE)
|
||||
AND NOT OPENCV_CUDA_FORCE_BUILTIN_CMAKE_MODULE)
|
||||
ocv_update(CUDA_LINK_LIBRARIES_KEYWORD "LINK_PRIVATE")
|
||||
ocv_update(CUDA_LINK_LIBRARIES_KEYWORD "PRIVATE")
|
||||
find_host_package(CUDA "${MIN_VER_CUDA}" QUIET)
|
||||
else()
|
||||
# Use OpenCV's patched "FindCUDA" module
|
||||
|
||||
@@ -3,15 +3,14 @@
|
||||
# - CV_CLANG - Clang-compatible compiler (CMAKE_CXX_COMPILER_ID MATCHES "Clang" - Clang or AppleClang, see CMP0025)
|
||||
# - CV_ICC - Intel compiler
|
||||
# - MSVC - Microsoft Visual Compiler (CMake variable)
|
||||
# - MSVC64 - additional flag, 64-bit
|
||||
# - MINGW / CYGWIN / CMAKE_COMPILER_IS_MINGW / CMAKE_COMPILER_IS_CYGWIN (CMake original variables)
|
||||
# - MINGW64 - 64-bit
|
||||
#
|
||||
# CPU Platforms:
|
||||
# - X86 / X86_64
|
||||
# - ARM - ARM CPU, not defined for AArch64
|
||||
# - AARCH64 - ARMv8+ (64-bit)
|
||||
# - PPC64 / PPC64LE - PowerPC
|
||||
# - MIPS
|
||||
#
|
||||
# OS:
|
||||
# - WIN32 - Windows | MINGW
|
||||
@@ -21,9 +20,8 @@
|
||||
# - APPLE - MacOSX | iOS
|
||||
# ----------------------------------------------------------------------------
|
||||
|
||||
if(CMAKE_CL_64)
|
||||
set(MSVC64 1)
|
||||
endif()
|
||||
ocv_declare_removed_variables(MINGW64 MSVC64)
|
||||
# do not use (CMake variables): CMAKE_CL_64
|
||||
|
||||
if(NOT DEFINED CV_GCC AND CMAKE_CXX_COMPILER_ID MATCHES "GNU")
|
||||
set(CV_GCC 1)
|
||||
@@ -51,7 +49,7 @@ variable_watch(CMAKE_COMPILER_IS_CLANGCC access_CMAKE_COMPILER_IS_CLANGCXX)
|
||||
# Detect Intel ICC compiler
|
||||
# ----------------------------------------------------------------------------
|
||||
if(UNIX)
|
||||
if (__ICL)
|
||||
if(__ICL)
|
||||
set(CV_ICC __ICL)
|
||||
elseif(__ICC)
|
||||
set(CV_ICC __ICC)
|
||||
@@ -70,53 +68,65 @@ if(MSVC AND CMAKE_C_COMPILER MATCHES "icc|icl")
|
||||
set(CV_ICC __INTEL_COMPILER_FOR_WINDOWS)
|
||||
endif()
|
||||
|
||||
if(NOT DEFINED CMAKE_CXX_COMPILER_VERSION)
|
||||
message(WARNING "Compiler version is not available: CMAKE_CXX_COMPILER_VERSION is not set")
|
||||
if(NOT DEFINED CMAKE_CXX_COMPILER_VERSION
|
||||
AND NOT OPENCV_SUPPRESS_MESSAGE_MISSING_COMPILER_VERSION)
|
||||
message(WARNING "OpenCV: Compiler version is not available: CMAKE_CXX_COMPILER_VERSION is not set")
|
||||
endif()
|
||||
|
||||
if(WIN32 AND CV_GCC)
|
||||
execute_process(COMMAND ${CMAKE_CXX_COMPILER} -dumpmachine
|
||||
OUTPUT_VARIABLE OPENCV_GCC_TARGET_MACHINE
|
||||
OUTPUT_STRIP_TRAILING_WHITESPACE)
|
||||
if(OPENCV_GCC_TARGET_MACHINE MATCHES "amd64|x86_64|AMD64")
|
||||
set(MINGW64 1)
|
||||
endif()
|
||||
if((NOT DEFINED CMAKE_SYSTEM_PROCESSOR OR CMAKE_SYSTEM_PROCESSOR STREQUAL "")
|
||||
AND NOT OPENCV_SUPPRESS_MESSAGE_MISSING_CMAKE_SYSTEM_PROCESSOR)
|
||||
message(WARNING "OpenCV: CMAKE_SYSTEM_PROCESSOR is not defined. Perhaps CMake toolchain is broken")
|
||||
endif()
|
||||
if(NOT DEFINED CMAKE_SIZEOF_VOID_P
|
||||
AND NOT OPENCV_SUPPRESS_MESSAGE_MISSING_CMAKE_SIZEOF_VOID_P)
|
||||
message(WARNING "OpenCV: CMAKE_SIZEOF_VOID_P is not defined. Perhaps CMake toolchain is broken")
|
||||
endif()
|
||||
|
||||
message(STATUS "Detected processor: ${CMAKE_SYSTEM_PROCESSOR}")
|
||||
if(MSVC64 OR MINGW64)
|
||||
set(X86_64 1)
|
||||
elseif(MINGW OR (MSVC AND NOT CMAKE_CROSSCOMPILING))
|
||||
set(X86 1)
|
||||
if(OPENCV_SKIP_SYSTEM_PROCESSOR_DETECTION)
|
||||
# custom setup: required variables are passed through cache / CMake's command-line
|
||||
elseif(CMAKE_SYSTEM_PROCESSOR MATCHES "amd64.*|x86_64.*|AMD64.*")
|
||||
set(X86_64 1)
|
||||
elseif(CMAKE_SYSTEM_PROCESSOR MATCHES "i686.*|i386.*|x86.*|amd64.*|AMD64.*")
|
||||
elseif(CMAKE_SYSTEM_PROCESSOR MATCHES "i686.*|i386.*|x86.*")
|
||||
set(X86 1)
|
||||
elseif(CMAKE_SYSTEM_PROCESSOR MATCHES "^(aarch64.*|AARCH64.*|arm64.*|ARM64.*)")
|
||||
set(AARCH64 1)
|
||||
elseif(CMAKE_SYSTEM_PROCESSOR MATCHES "^(arm.*|ARM.*)")
|
||||
set(ARM 1)
|
||||
elseif(CMAKE_SYSTEM_PROCESSOR MATCHES "^(aarch64.*|AARCH64.*)")
|
||||
set(AARCH64 1)
|
||||
elseif(CMAKE_SYSTEM_PROCESSOR MATCHES "^(powerpc|ppc)64le")
|
||||
set(PPC64LE 1)
|
||||
elseif(CMAKE_SYSTEM_PROCESSOR MATCHES "^(powerpc|ppc)64")
|
||||
set(PPC64 1)
|
||||
elseif(CMAKE_SYSTEM_PROCESSOR MATCHES "^(mips.*|MIPS.*)")
|
||||
set(MIPS 1)
|
||||
else()
|
||||
if(NOT OPENCV_SUPPRESS_MESSAGE_UNRECOGNIZED_SYSTEM_PROCESSOR)
|
||||
message(WARNING "OpenCV: unrecognized target processor configuration")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# Workaround for 32-bit operating systems on x86_64/aarch64 processor
|
||||
if(CMAKE_SIZEOF_VOID_P EQUAL 4 AND NOT FORCE_X86_64)
|
||||
# Workaround for 32-bit operating systems on x86_64
|
||||
if(CMAKE_SIZEOF_VOID_P EQUAL 4 AND X86_64
|
||||
AND NOT FORCE_X86_64 # deprecated (2019-12)
|
||||
AND NOT OPENCV_FORCE_X86_64
|
||||
)
|
||||
message(STATUS "sizeof(void) = 4 on 64 bit processor. Assume 32-bit compilation mode")
|
||||
if (X86_64)
|
||||
if(X86_64)
|
||||
unset(X86_64)
|
||||
set(X86 1)
|
||||
endif()
|
||||
if (AARCH64)
|
||||
endif()
|
||||
# Workaround for 32-bit operating systems on aarch64 processor
|
||||
if(CMAKE_SIZEOF_VOID_P EQUAL 4 AND AARCH64
|
||||
AND NOT OPENCV_FORCE_AARCH64
|
||||
)
|
||||
message(STATUS "sizeof(void) = 4 on 64 bit processor. Assume 32-bit compilation mode")
|
||||
if(AARCH64)
|
||||
unset(AARCH64)
|
||||
set(ARM 1)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
|
||||
# Similar code exists in OpenCVConfig.cmake
|
||||
if(NOT DEFINED OpenCV_STATIC)
|
||||
# look for global setting
|
||||
@@ -130,14 +140,19 @@ endif()
|
||||
if(DEFINED OpenCV_ARCH AND DEFINED OpenCV_RUNTIME)
|
||||
# custom overridden values
|
||||
elseif(MSVC)
|
||||
if(CMAKE_CL_64)
|
||||
set(OpenCV_ARCH x64)
|
||||
elseif((CMAKE_GENERATOR MATCHES "ARM") OR ("${arch_hint}" STREQUAL "ARM") OR (CMAKE_VS_EFFECTIVE_PLATFORMS MATCHES "ARM|arm"))
|
||||
# see Modules/CmakeGenericSystem.cmake
|
||||
set(OpenCV_ARCH ARM)
|
||||
# see Modules/CMakeGenericSystem.cmake
|
||||
if("${CMAKE_GENERATOR}" MATCHES "(Win64|IA64)")
|
||||
set(OpenCV_ARCH "x64")
|
||||
elseif("${CMAKE_GENERATOR_PLATFORM}" MATCHES "ARM64")
|
||||
set(OpenCV_ARCH "ARM64")
|
||||
elseif("${CMAKE_GENERATOR}" MATCHES "ARM")
|
||||
set(OpenCV_ARCH "ARM")
|
||||
elseif("${CMAKE_SIZEOF_VOID_P}" STREQUAL "8")
|
||||
set(OpenCV_ARCH "x64")
|
||||
else()
|
||||
set(OpenCV_ARCH x86)
|
||||
endif()
|
||||
|
||||
if(MSVC_VERSION EQUAL 1400)
|
||||
set(OpenCV_RUNTIME vc8)
|
||||
elseif(MSVC_VERSION EQUAL 1500)
|
||||
@@ -160,7 +175,7 @@ elseif(MSVC)
|
||||
elseif(MINGW)
|
||||
set(OpenCV_RUNTIME mingw)
|
||||
|
||||
if(MINGW64)
|
||||
if(CMAKE_SYSTEM_PROCESSOR MATCHES "amd64.*|x86_64.*|AMD64.*")
|
||||
set(OpenCV_ARCH x64)
|
||||
else()
|
||||
set(OpenCV_ARCH x86)
|
||||
|
||||
@@ -28,6 +28,15 @@ function(add_custom_ie_build _inc _lib _lib_rel _lib_dbg _msg)
|
||||
IMPORTED_IMPLIB_DEBUG "${_lib_dbg}"
|
||||
INTERFACE_INCLUDE_DIRECTORIES "${_inc}"
|
||||
)
|
||||
|
||||
find_library(ie_builder_custom_lib "inference_engine_nn_builder" PATHS "${INF_ENGINE_LIB_DIRS}" NO_DEFAULT_PATH)
|
||||
if(EXISTS "${ie_builder_custom_lib}")
|
||||
add_library(inference_engine_nn_builder UNKNOWN IMPORTED)
|
||||
set_target_properties(inference_engine_nn_builder PROPERTIES
|
||||
IMPORTED_LOCATION "${ie_builder_custom_lib}"
|
||||
)
|
||||
endif()
|
||||
|
||||
if(NOT INF_ENGINE_RELEASE VERSION_GREATER "2018050000")
|
||||
find_library(INF_ENGINE_OMP_LIBRARY iomp5 PATHS "${INF_ENGINE_OMP_DIR}" NO_DEFAULT_PATH)
|
||||
if(NOT INF_ENGINE_OMP_LIBRARY)
|
||||
@@ -37,7 +46,12 @@ function(add_custom_ie_build _inc _lib _lib_rel _lib_dbg _msg)
|
||||
endif()
|
||||
endif()
|
||||
set(INF_ENGINE_VERSION "Unknown" CACHE STRING "")
|
||||
set(INF_ENGINE_TARGET inference_engine PARENT_SCOPE)
|
||||
set(INF_ENGINE_TARGET inference_engine)
|
||||
if(TARGET inference_engine_nn_builder)
|
||||
list(APPEND INF_ENGINE_TARGET inference_engine_nn_builder)
|
||||
set(_msg "${_msg}, with IE NN Builder API")
|
||||
endif()
|
||||
set(INF_ENGINE_TARGET "${INF_ENGINE_TARGET}" PARENT_SCOPE)
|
||||
message(STATUS "Detected InferenceEngine: ${_msg}")
|
||||
endfunction()
|
||||
|
||||
@@ -47,7 +61,7 @@ find_package(InferenceEngine QUIET)
|
||||
if(InferenceEngine_FOUND)
|
||||
set(INF_ENGINE_TARGET ${InferenceEngine_LIBRARIES})
|
||||
set(INF_ENGINE_VERSION "${InferenceEngine_VERSION}" CACHE STRING "")
|
||||
message(STATUS "Detected InferenceEngine: cmake package")
|
||||
message(STATUS "Detected InferenceEngine: cmake package (${InferenceEngine_VERSION})")
|
||||
endif()
|
||||
|
||||
if(NOT INF_ENGINE_TARGET AND INF_ENGINE_LIB_DIRS AND INF_ENGINE_INCLUDE_DIRS)
|
||||
@@ -87,3 +101,15 @@ if(INF_ENGINE_TARGET)
|
||||
INTERFACE_COMPILE_DEFINITIONS "HAVE_INF_ENGINE=1;INF_ENGINE_RELEASE=${INF_ENGINE_RELEASE}"
|
||||
)
|
||||
endif()
|
||||
|
||||
if(WITH_NGRAPH)
|
||||
find_package(ngraph QUIET)
|
||||
if(ngraph_FOUND)
|
||||
ocv_assert(TARGET ngraph::ngraph)
|
||||
if(INF_ENGINE_RELEASE VERSION_LESS "2019039999")
|
||||
message(WARNING "nGraph is not tested with current InferenceEngine version: INF_ENGINE_RELEASE=${INF_ENGINE_RELEASE}")
|
||||
endif()
|
||||
message(STATUS "Detected ngraph: cmake package (${ngraph_VERSION})")
|
||||
set(HAVE_NGRAPH ON)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
@@ -40,11 +40,8 @@ if (X86 AND UNIX AND NOT APPLE AND NOT ANDROID AND BUILD_SHARED_LIBS)
|
||||
endif()
|
||||
|
||||
set(IPP_X64 0)
|
||||
if(CMAKE_CXX_SIZEOF_DATA_PTR EQUAL 8)
|
||||
set(IPP_X64 1)
|
||||
endif()
|
||||
if(CMAKE_CL_64)
|
||||
set(IPP_X64 1)
|
||||
if(X86_64)
|
||||
set(IPP_X64 1)
|
||||
endif()
|
||||
|
||||
# This function detects Intel IPP version by analyzing .h file
|
||||
|
||||
@@ -133,7 +133,7 @@ message(STATUS "Found MKL ${MKL_VERSION_STR} at: ${MKL_ROOT_DIR}")
|
||||
set(HAVE_MKL ON)
|
||||
set(MKL_ROOT_DIR "${MKL_ROOT_DIR}" CACHE PATH "Path to MKL directory")
|
||||
set(MKL_INCLUDE_DIRS "${MKL_INCLUDE_DIRS}" CACHE PATH "Path to MKL include directory")
|
||||
set(MKL_LIBRARIES "${MKL_LIBRARIES}" CACHE STRING "MKL libarries")
|
||||
set(MKL_LIBRARIES "${MKL_LIBRARIES}" CACHE STRING "MKL libraries")
|
||||
if(UNIX AND NOT MKL_LIBRARIES_DONT_HACK)
|
||||
#it's ugly but helps to avoid cyclic lib problem
|
||||
set(MKL_LIBRARIES ${MKL_LIBRARIES} ${MKL_LIBRARIES} ${MKL_LIBRARIES} "-lpthread" "-lm" "-ldl")
|
||||
|
||||
@@ -15,60 +15,94 @@ file(TO_CMAKE_PATH "$ENV{ProgramFiles}" ProgramFiles_ENV_PATH)
|
||||
|
||||
if(WIN32)
|
||||
SET(OPENEXR_ROOT "C:/Deploy" CACHE STRING "Path to the OpenEXR \"Deploy\" folder")
|
||||
if(CMAKE_CL_64)
|
||||
if(X86_64)
|
||||
SET(OPENEXR_LIBSEARCH_SUFFIXES x64/Release x64 x64/Debug)
|
||||
elseif(MSVC)
|
||||
SET(OPENEXR_LIBSEARCH_SUFFIXES Win32/Release Win32 Win32/Debug)
|
||||
endif()
|
||||
else()
|
||||
set(OPENEXR_ROOT "")
|
||||
endif()
|
||||
|
||||
SET(LIBRARY_PATHS
|
||||
/usr/lib
|
||||
/usr/local/lib
|
||||
/sw/lib
|
||||
/opt/local/lib
|
||||
"${ProgramFiles_ENV_PATH}/OpenEXR/lib/static"
|
||||
"${OPENEXR_ROOT}/lib")
|
||||
SET(SEARCH_PATHS
|
||||
"${OPENEXR_ROOT}"
|
||||
/usr
|
||||
/usr/local
|
||||
/sw
|
||||
/opt
|
||||
"${ProgramFiles_ENV_PATH}/OpenEXR")
|
||||
|
||||
FIND_PATH(OPENEXR_INCLUDE_PATH ImfRgbaFile.h
|
||||
PATH_SUFFIXES OpenEXR
|
||||
PATHS
|
||||
/usr/include
|
||||
/usr/local/include
|
||||
/sw/include
|
||||
/opt/local/include
|
||||
"${ProgramFiles_ENV_PATH}/OpenEXR/include"
|
||||
"${OPENEXR_ROOT}/include")
|
||||
MACRO(FIND_OPENEXR_LIBRARY LIBRARY_NAME LIBRARY_SUFFIX)
|
||||
string(TOUPPER "${LIBRARY_NAME}" LIBRARY_NAME_UPPER)
|
||||
FIND_LIBRARY(OPENEXR_${LIBRARY_NAME_UPPER}_LIBRARY
|
||||
NAMES ${LIBRARY_NAME}${LIBRARY_SUFFIX}
|
||||
PATH_SUFFIXES ${OPENEXR_LIBSEARCH_SUFFIXES}
|
||||
NO_DEFAULT_PATH
|
||||
PATHS "${SEARCH_PATH}/lib" "${SEARCH_PATH}/lib/static")
|
||||
ENDMACRO()
|
||||
|
||||
FIND_LIBRARY(OPENEXR_HALF_LIBRARY
|
||||
NAMES Half
|
||||
PATH_SUFFIXES ${OPENEXR_LIBSEARCH_SUFFIXES}
|
||||
PATHS ${LIBRARY_PATHS})
|
||||
FOREACH(SEARCH_PATH ${SEARCH_PATHS})
|
||||
FIND_PATH(OPENEXR_INCLUDE_PATH ImfRgbaFile.h
|
||||
PATH_SUFFIXES OpenEXR
|
||||
NO_DEFAULT_PATH
|
||||
PATHS
|
||||
"${SEARCH_PATH}/include")
|
||||
|
||||
FIND_LIBRARY(OPENEXR_IEX_LIBRARY
|
||||
NAMES Iex
|
||||
PATH_SUFFIXES ${OPENEXR_LIBSEARCH_SUFFIXES}
|
||||
PATHS ${LIBRARY_PATHS})
|
||||
IF (OPENEXR_INCLUDE_PATH)
|
||||
SET(OPENEXR_VERSION_FILE "${OPENEXR_INCLUDE_PATH}/OpenEXRConfig.h")
|
||||
IF (EXISTS ${OPENEXR_VERSION_FILE})
|
||||
FILE (STRINGS ${OPENEXR_VERSION_FILE} contents REGEX "#define OPENEXR_VERSION_MAJOR ")
|
||||
IF (${contents} MATCHES "#define OPENEXR_VERSION_MAJOR ([0-9]+)")
|
||||
SET(OPENEXR_VERSION_MAJOR "${CMAKE_MATCH_1}")
|
||||
ENDIF ()
|
||||
FILE (STRINGS ${OPENEXR_VERSION_FILE} contents REGEX "#define OPENEXR_VERSION_MINOR ")
|
||||
IF (${contents} MATCHES "#define OPENEXR_VERSION_MINOR ([0-9]+)")
|
||||
SET(OPENEXR_VERSION_MINOR "${CMAKE_MATCH_1}")
|
||||
ENDIF ()
|
||||
ENDIF ()
|
||||
ENDIF ()
|
||||
|
||||
FIND_LIBRARY(OPENEXR_IMATH_LIBRARY
|
||||
NAMES Imath
|
||||
PATH_SUFFIXES ${OPENEXR_LIBSEARCH_SUFFIXES}
|
||||
PATHS ${LIBRARY_PATHS})
|
||||
IF (OPENEXR_VERSION_MAJOR AND OPENEXR_VERSION_MINOR)
|
||||
set(OPENEXR_VERSION "${OPENEXR_VERSION_MAJOR}_${OPENEXR_VERSION_MINOR}")
|
||||
ENDIF ()
|
||||
|
||||
FIND_LIBRARY(OPENEXR_ILMIMF_LIBRARY
|
||||
NAMES IlmImf
|
||||
PATH_SUFFIXES ${OPENEXR_LIBSEARCH_SUFFIXES}
|
||||
PATHS ${LIBRARY_PATHS})
|
||||
SET(LIBRARY_SUFFIXES
|
||||
"-${OPENEXR_VERSION}"
|
||||
"-${OPENEXR_VERSION}_s"
|
||||
"-${OPENEXR_VERSION}_d"
|
||||
"-${OPEXEXR_VERSION}_s_d"
|
||||
""
|
||||
"_s"
|
||||
"_d"
|
||||
"_s_d")
|
||||
|
||||
FIND_LIBRARY(OPENEXR_ILMTHREAD_LIBRARY
|
||||
NAMES IlmThread
|
||||
PATH_SUFFIXES ${OPENEXR_LIBSEARCH_SUFFIXES}
|
||||
PATHS ${LIBRARY_PATHS})
|
||||
FOREACH(LIBRARY_SUFFIX ${LIBRARY_SUFFIXES})
|
||||
FIND_OPENEXR_LIBRARY("Half" ${LIBRARY_SUFFIX})
|
||||
FIND_OPENEXR_LIBRARY("Iex" ${LIBRARY_SUFFIX})
|
||||
FIND_OPENEXR_LIBRARY("Imath" ${LIBRARY_SUFFIX})
|
||||
FIND_OPENEXR_LIBRARY("IlmImf" ${LIBRARY_SUFFIX})
|
||||
FIND_OPENEXR_LIBRARY("IlmThread" ${LIBRARY_SUFFIX})
|
||||
IF (OPENEXR_INCLUDE_PATH AND OPENEXR_IMATH_LIBRARY AND OPENEXR_ILMIMF_LIBRARY AND OPENEXR_IEX_LIBRARY AND OPENEXR_HALF_LIBRARY)
|
||||
SET(OPENEXR_FOUND TRUE)
|
||||
BREAK()
|
||||
ENDIF()
|
||||
UNSET(OPENEXR_IMATH_LIBRARY)
|
||||
UNSET(OPENEXR_ILMIMF_LIBRARY)
|
||||
UNSET(OPENEXR_IEX_LIBRARY)
|
||||
UNSET(OPENEXR_ILMTHREAD_LIBRARY)
|
||||
UNSET(OPENEXR_HALF_LIBRARY)
|
||||
ENDFOREACH()
|
||||
|
||||
IF (OPENEXR_INCLUDE_PATH AND OPENEXR_IMATH_LIBRARY AND OPENEXR_ILMIMF_LIBRARY AND OPENEXR_IEX_LIBRARY AND OPENEXR_HALF_LIBRARY)
|
||||
SET(OPENEXR_FOUND TRUE)
|
||||
IF (OPENEXR_FOUND)
|
||||
BREAK()
|
||||
ENDIF()
|
||||
|
||||
UNSET(OPENEXR_INCLUDE_PATH)
|
||||
UNSET(OPENEXR_VERSION_FILE)
|
||||
UNSET(OPENEXR_VERSION_MAJOR)
|
||||
UNSET(OPENEXR_VERSION_MINOR)
|
||||
UNSET(OPENEXR_VERSION)
|
||||
ENDFOREACH()
|
||||
|
||||
IF (OPENEXR_FOUND)
|
||||
SET(OPENEXR_INCLUDE_PATHS ${OPENEXR_INCLUDE_PATH} CACHE PATH "The include paths needed to use OpenEXR")
|
||||
SET(OPENEXR_LIBRARIES ${OPENEXR_IMATH_LIBRARY} ${OPENEXR_ILMIMF_LIBRARY} ${OPENEXR_IEX_LIBRARY} ${OPENEXR_HALF_LIBRARY} ${OPENEXR_ILMTHREAD_LIBRARY} CACHE STRING "The libraries needed to use OpenEXR" FORCE)
|
||||
ENDIF ()
|
||||
|
||||
@@ -2,7 +2,7 @@ if(NOT DEFINED MIN_VER_CMAKE)
|
||||
set(MIN_VER_CMAKE 3.5.1)
|
||||
endif()
|
||||
set(MIN_VER_CUDA 6.5)
|
||||
set(MIN_VER_CUDNN 6)
|
||||
set(MIN_VER_CUDNN 7.5)
|
||||
set(MIN_VER_PYTHON2 2.7)
|
||||
set(MIN_VER_PYTHON3 3.2)
|
||||
set(MIN_VER_ZLIB 1.2.3)
|
||||
|
||||
@@ -63,7 +63,6 @@ foreach(mod ${OPENCV_MODULES_BUILD} ${OPENCV_MODULES_DISABLED_USER} ${OPENCV_MOD
|
||||
unset(OPENCV_MODULE_${mod}_PRIVATE_OPT_DEPS CACHE)
|
||||
unset(OPENCV_MODULE_${mod}_LINK_DEPS CACHE)
|
||||
unset(OPENCV_MODULE_${mod}_WRAPPERS CACHE)
|
||||
unset(OPENCV_DEPENDANT_TARGETS_${mod} CACHE)
|
||||
endforeach()
|
||||
|
||||
# clean modules info which needs to be recalculated
|
||||
@@ -937,11 +936,15 @@ macro(_ocv_create_module)
|
||||
set_source_files_properties(${OPENCV_MODULE_${the_module}_HEADERS} ${OPENCV_MODULE_${the_module}_SOURCES} ${${the_module}_pch}
|
||||
PROPERTIES LABELS "${OPENCV_MODULE_${the_module}_LABEL};Module")
|
||||
|
||||
ocv_target_link_libraries(${the_module} LINK_PUBLIC ${OPENCV_MODULE_${the_module}_DEPS_TO_LINK})
|
||||
ocv_target_link_libraries(${the_module} LINK_PUBLIC ${OPENCV_MODULE_${the_module}_DEPS_EXT})
|
||||
ocv_target_link_libraries(${the_module} LINK_PRIVATE ${OPENCV_LINKER_LIBS} ${OPENCV_HAL_LINKER_LIBS} ${IPP_LIBS} ${ARGN})
|
||||
ocv_target_link_libraries(${the_module} PUBLIC ${OPENCV_MODULE_${the_module}_DEPS_TO_LINK}
|
||||
INTERFACE ${OPENCV_MODULE_${the_module}_DEPS_TO_LINK}
|
||||
)
|
||||
ocv_target_link_libraries(${the_module} PUBLIC ${OPENCV_MODULE_${the_module}_DEPS_EXT}
|
||||
INTERFACE ${OPENCV_MODULE_${the_module}_DEPS_EXT}
|
||||
)
|
||||
ocv_target_link_libraries(${the_module} PRIVATE ${OPENCV_LINKER_LIBS} ${OPENCV_HAL_LINKER_LIBS} ${IPP_LIBS} ${ARGN})
|
||||
if (HAVE_CUDA)
|
||||
ocv_target_link_libraries(${the_module} LINK_PRIVATE ${CUDA_LIBRARIES} ${CUDA_npp_LIBRARY})
|
||||
ocv_target_link_libraries(${the_module} PRIVATE ${CUDA_LIBRARIES} ${CUDA_npp_LIBRARY})
|
||||
endif()
|
||||
|
||||
if(OPENCV_MODULE_${the_module}_COMPILE_DEFINITIONS)
|
||||
@@ -1150,7 +1153,7 @@ function(ocv_add_perf_tests)
|
||||
source_group("Src" FILES "${${the_target}_pch}")
|
||||
ocv_add_executable(${the_target} ${OPENCV_PERF_${the_module}_SOURCES} ${${the_target}_pch})
|
||||
ocv_target_include_modules(${the_target} ${perf_deps})
|
||||
ocv_target_link_libraries(${the_target} LINK_PRIVATE ${perf_deps} ${OPENCV_MODULE_${the_module}_DEPS} ${OPENCV_LINKER_LIBS} ${OPENCV_PERF_${the_module}_DEPS})
|
||||
ocv_target_link_libraries(${the_target} PRIVATE ${perf_deps} ${OPENCV_MODULE_${the_module}_DEPS} ${OPENCV_LINKER_LIBS} ${OPENCV_PERF_${the_module}_DEPS})
|
||||
add_dependencies(opencv_perf_tests ${the_target})
|
||||
|
||||
if(TARGET opencv_videoio_plugins)
|
||||
@@ -1240,7 +1243,7 @@ function(ocv_add_accuracy_tests)
|
||||
if(EXISTS "${CMAKE_CURRENT_BINARY_DIR}/test")
|
||||
ocv_target_include_directories(${the_target} "${CMAKE_CURRENT_BINARY_DIR}/test")
|
||||
endif()
|
||||
ocv_target_link_libraries(${the_target} LINK_PRIVATE ${test_deps} ${OPENCV_MODULE_${the_module}_DEPS} ${OPENCV_LINKER_LIBS} ${OPENCV_TEST_${the_module}_DEPS})
|
||||
ocv_target_link_libraries(${the_target} PRIVATE ${test_deps} ${OPENCV_MODULE_${the_module}_DEPS} ${OPENCV_LINKER_LIBS} ${OPENCV_TEST_${the_module}_DEPS})
|
||||
add_dependencies(opencv_tests ${the_target})
|
||||
|
||||
if(TARGET opencv_videoio_plugins)
|
||||
@@ -1310,7 +1313,7 @@ function(ocv_add_samples)
|
||||
|
||||
ocv_add_executable(${the_target} "${source}")
|
||||
ocv_target_include_modules(${the_target} ${samples_deps})
|
||||
ocv_target_link_libraries(${the_target} LINK_PRIVATE ${samples_deps})
|
||||
ocv_target_link_libraries(${the_target} PRIVATE ${samples_deps})
|
||||
|
||||
set_target_properties(${the_target} PROPERTIES
|
||||
PROJECT_LABEL "(sample) ${name}"
|
||||
|
||||
@@ -125,11 +125,11 @@ MACRO(_PCH_GET_COMPILE_COMMAND out_command _input _output)
|
||||
STRING(REGEX REPLACE "^ +" "" pchsupport_compiler_cxx_arg1 ${CMAKE_CXX_COMPILER_ARG1})
|
||||
|
||||
SET(${out_command}
|
||||
${CMAKE_CXX_COMPILER} ${pchsupport_compiler_cxx_arg1} ${_compile_FLAGS} -x c++-header -o ${_output} ${_input}
|
||||
${CMAKE_CXX_COMPILER} ${pchsupport_compiler_cxx_arg1} ${_compile_FLAGS} -x c++-header -o ${_output} -c ${_input}
|
||||
)
|
||||
ELSE(CMAKE_CXX_COMPILER_ARG1)
|
||||
SET(${out_command}
|
||||
${CMAKE_CXX_COMPILER} ${_compile_FLAGS} -x c++-header -o ${_output} ${_input}
|
||||
${CMAKE_CXX_COMPILER} ${_compile_FLAGS} -x c++-header -o ${_output} -c ${_input}
|
||||
)
|
||||
ENDIF(CMAKE_CXX_COMPILER_ARG1)
|
||||
ELSE()
|
||||
|
||||
+46
-3
@@ -100,6 +100,30 @@ macro(ocv_update VAR)
|
||||
endif()
|
||||
endmacro()
|
||||
|
||||
function(_ocv_access_removed_variable VAR ACCESS)
|
||||
if(ACCESS STREQUAL "MODIFIED_ACCESS")
|
||||
set(OPENCV_SUPPRESS_MESSAGE_REMOVED_VARIABLE_${VAR} 1 PARENT_SCOPE)
|
||||
return()
|
||||
endif()
|
||||
if(ACCESS MATCHES "UNKNOWN_.*"
|
||||
AND NOT OPENCV_SUPPRESS_MESSAGE_REMOVED_VARIABLE
|
||||
AND NOT OPENCV_SUPPRESS_MESSAGE_REMOVED_VARIABLE_${VAR}
|
||||
)
|
||||
message(WARNING "OpenCV: Variable has been removed from CMake scripts: ${VAR}")
|
||||
set(OPENCV_SUPPRESS_MESSAGE_REMOVED_VARIABLE_${VAR} 1 PARENT_SCOPE) # suppress similar messages
|
||||
endif()
|
||||
endfunction()
|
||||
macro(ocv_declare_removed_variable VAR)
|
||||
if(NOT DEFINED ${VAR}) # don't hit external variables
|
||||
variable_watch(${VAR} _ocv_access_removed_variable)
|
||||
endif()
|
||||
endmacro()
|
||||
macro(ocv_declare_removed_variables)
|
||||
foreach(_var ${ARGN})
|
||||
ocv_declare_removed_variable(${_var})
|
||||
endforeach()
|
||||
endmacro()
|
||||
|
||||
# Search packages for the host system instead of packages for the target system
|
||||
# in case of cross compilation these macros should be defined by the toolchain file
|
||||
if(NOT COMMAND find_host_package)
|
||||
@@ -288,9 +312,22 @@ function(ocv_append_target_property target prop)
|
||||
endif()
|
||||
endfunction()
|
||||
|
||||
if(DEFINED OPENCV_DEPENDANT_TARGETS_LIST)
|
||||
foreach(v ${OPENCV_DEPENDANT_TARGETS_LIST})
|
||||
unset(${v} CACHE)
|
||||
endforeach()
|
||||
unset(OPENCV_DEPENDANT_TARGETS_LIST CACHE)
|
||||
endif()
|
||||
|
||||
function(ocv_append_dependant_targets target)
|
||||
#ocv_debug_message("ocv_append_dependant_targets(${target} ${ARGN})")
|
||||
_ocv_fix_target(target)
|
||||
list(FIND OPENCV_DEPENDANT_TARGETS_LIST "OPENCV_DEPENDANT_TARGETS_${target}" __id)
|
||||
if(__id EQUAL -1)
|
||||
list(APPEND OPENCV_DEPENDANT_TARGETS_LIST "OPENCV_DEPENDANT_TARGETS_${target}")
|
||||
list(SORT OPENCV_DEPENDANT_TARGETS_LIST)
|
||||
set(OPENCV_DEPENDANT_TARGETS_LIST "${OPENCV_DEPENDANT_TARGETS_LIST}" CACHE INTERNAL "")
|
||||
endif()
|
||||
set(OPENCV_DEPENDANT_TARGETS_${target} "${OPENCV_DEPENDANT_TARGETS_${target}};${ARGN}" CACHE INTERNAL "" FORCE)
|
||||
endfunction()
|
||||
|
||||
@@ -364,6 +401,7 @@ macro(ocv_clear_vars)
|
||||
endmacro()
|
||||
|
||||
set(OCV_COMPILER_FAIL_REGEX
|
||||
"argument '.*' is not valid" # GCC 9+
|
||||
"command line option .* is valid for .* but not for C\\+\\+" # GNU
|
||||
"command line option .* is valid for .* but not for C" # GNU
|
||||
"unrecognized .*option" # GNU
|
||||
@@ -1393,12 +1431,14 @@ endmacro()
|
||||
function(ocv_target_link_libraries target)
|
||||
set(LINK_DEPS ${ARGN})
|
||||
_ocv_fix_target(target)
|
||||
set(LINK_MODE "LINK_PRIVATE")
|
||||
set(LINK_MODE "PRIVATE")
|
||||
set(LINK_PENDING "")
|
||||
foreach(dep ${LINK_DEPS})
|
||||
if(" ${dep}" STREQUAL " ${target}")
|
||||
# prevent "link to itself" warning (world problem)
|
||||
elseif(" ${dep}" STREQUAL " LINK_PRIVATE" OR " ${dep}" STREQUAL "LINK_PUBLIC")
|
||||
elseif(" ${dep}" STREQUAL " LINK_PRIVATE" OR " ${dep}" STREQUAL " LINK_PUBLIC" # deprecated
|
||||
OR " ${dep}" STREQUAL " PRIVATE" OR " ${dep}" STREQUAL " PUBLIC" OR " ${dep}" STREQUAL " INTERFACE"
|
||||
)
|
||||
if(NOT LINK_PENDING STREQUAL "")
|
||||
__ocv_push_target_link_libraries(${LINK_MODE} ${LINK_PENDING})
|
||||
set(LINK_PENDING "")
|
||||
@@ -1537,7 +1577,10 @@ macro(ocv_get_all_libs _modules _extra _3rdparty)
|
||||
endif()
|
||||
if (TARGET ${dep})
|
||||
get_target_property(_type ${dep} TYPE)
|
||||
if(_type STREQUAL "STATIC_LIBRARY" AND BUILD_SHARED_LIBS OR _type STREQUAL "INTERFACE_LIBRARY")
|
||||
if((_type STREQUAL "STATIC_LIBRARY" AND BUILD_SHARED_LIBS)
|
||||
OR _type STREQUAL "INTERFACE_LIBRARY"
|
||||
OR DEFINED OPENCV_MODULE_${dep}_LOCATION # OpenCV modules
|
||||
)
|
||||
# nothing
|
||||
else()
|
||||
get_target_property(_output ${dep} IMPORTED_LOCATION)
|
||||
|
||||
@@ -194,7 +194,7 @@ macro(add_android_project target path)
|
||||
add_library(${JNI_LIB_NAME} SHARED ${android_proj_jni_files})
|
||||
ocv_target_include_modules_recurse(${JNI_LIB_NAME} ${android_proj_NATIVE_DEPS})
|
||||
ocv_target_include_directories(${JNI_LIB_NAME} "${path}/jni")
|
||||
ocv_target_link_libraries(${JNI_LIB_NAME} LINK_PRIVATE ${OPENCV_LINKER_LIBS} ${android_proj_NATIVE_DEPS})
|
||||
ocv_target_link_libraries(${JNI_LIB_NAME} PRIVATE ${OPENCV_LINKER_LIBS} ${android_proj_NATIVE_DEPS})
|
||||
|
||||
set_target_properties(${JNI_LIB_NAME} PROPERTIES
|
||||
OUTPUT_NAME "${JNI_LIB_NAME}"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#include <stdio.h>
|
||||
|
||||
#if defined _WIN32 && defined(_M_ARM)
|
||||
#if defined _WIN32 && (defined(_M_ARM) || defined(_M_ARM64))
|
||||
# include <Intrin.h>
|
||||
# include <arm_neon.h>
|
||||
# define CV_NEON 1
|
||||
@@ -9,6 +9,10 @@
|
||||
# define CV_NEON 1
|
||||
#endif
|
||||
|
||||
// MSVC 2019 bug. Details: https://github.com/opencv/opencv/pull/16027
|
||||
void test_aliased_type(const uint8x16_t& a) { }
|
||||
void test_aliased_type(const int8x16_t& a) { }
|
||||
|
||||
#if defined CV_NEON
|
||||
int test()
|
||||
{
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
include("${CMAKE_CURRENT_LIST_DIR}/OpenCV_WinRT.cmake")
|
||||
include("${CMAKE_CURRENT_LIST_DIR}/OpenCV-WinRT.cmake")
|
||||
|
||||
# Adding additional using directory for WindowsPhone 8.0 to get Windows.winmd properly
|
||||
if(WINRT_8_0)
|
||||
|
||||
@@ -1 +1 @@
|
||||
include("${CMAKE_CURRENT_LIST_DIR}/OpenCV_WinRT.cmake")
|
||||
include("${CMAKE_CURRENT_LIST_DIR}/OpenCV-WinRT.cmake")
|
||||
|
||||
@@ -84,17 +84,31 @@ endfunction()
|
||||
|
||||
get_filename_component(OpenCV_CONFIG_PATH "${CMAKE_CURRENT_LIST_FILE}" DIRECTORY)
|
||||
|
||||
if((NOT DEFINED CMAKE_SYSTEM_PROCESSOR OR CMAKE_SYSTEM_PROCESSOR STREQUAL "")
|
||||
AND NOT OPENCV_SUPPRESS_MESSAGE_MISSING_CMAKE_SYSTEM_PROCESSOR)
|
||||
message(WARNING "OpenCV: CMAKE_SYSTEM_PROCESSOR is not defined. Perhaps CMake toolchain is broken")
|
||||
endif()
|
||||
if(NOT DEFINED CMAKE_SIZEOF_VOID_P
|
||||
AND NOT OPENCV_SUPPRESS_MESSAGE_MISSING_CMAKE_SIZEOF_VOID_P)
|
||||
message(WARNING "OpenCV: CMAKE_SIZEOF_VOID_P is not defined. Perhaps CMake toolchain is broken")
|
||||
endif()
|
||||
|
||||
if(DEFINED OpenCV_ARCH AND DEFINED OpenCV_RUNTIME)
|
||||
# custom overridden values
|
||||
elseif(MSVC)
|
||||
if(CMAKE_CL_64)
|
||||
set(OpenCV_ARCH x64)
|
||||
elseif((CMAKE_GENERATOR MATCHES "ARM") OR ("${arch_hint}" STREQUAL "ARM") OR (CMAKE_VS_EFFECTIVE_PLATFORMS MATCHES "ARM|arm"))
|
||||
# see Modules/CmakeGenericSystem.cmake
|
||||
set(OpenCV_ARCH ARM)
|
||||
# see Modules/CMakeGenericSystem.cmake
|
||||
if("${CMAKE_GENERATOR}" MATCHES "(Win64|IA64)")
|
||||
set(OpenCV_ARCH "x64")
|
||||
elseif("${CMAKE_GENERATOR_PLATFORM}" MATCHES "ARM64")
|
||||
set(OpenCV_ARCH "ARM64")
|
||||
elseif("${CMAKE_GENERATOR}" MATCHES "ARM")
|
||||
set(OpenCV_ARCH "ARM")
|
||||
elseif("${CMAKE_SIZEOF_VOID_P}" STREQUAL "8")
|
||||
set(OpenCV_ARCH "x64")
|
||||
else()
|
||||
set(OpenCV_ARCH x86)
|
||||
endif()
|
||||
|
||||
if(MSVC_VERSION EQUAL 1400)
|
||||
set(OpenCV_RUNTIME vc8)
|
||||
elseif(MSVC_VERSION EQUAL 1500)
|
||||
@@ -127,11 +141,7 @@ elseif(MSVC)
|
||||
elseif(MINGW)
|
||||
set(OpenCV_RUNTIME mingw)
|
||||
|
||||
execute_process(COMMAND ${CMAKE_CXX_COMPILER} -dumpmachine
|
||||
OUTPUT_VARIABLE OPENCV_GCC_TARGET_MACHINE
|
||||
OUTPUT_STRIP_TRAILING_WHITESPACE)
|
||||
if(OPENCV_GCC_TARGET_MACHINE MATCHES "amd64|x86_64|AMD64")
|
||||
set(MINGW64 1)
|
||||
if(CMAKE_SYSTEM_PROCESSOR MATCHES "amd64.*|x86_64.*|AMD64.*")
|
||||
set(OpenCV_ARCH x64)
|
||||
else()
|
||||
set(OpenCV_ARCH x86)
|
||||
|
||||
@@ -22,6 +22,8 @@
|
||||
|
||||
<skip_headers>
|
||||
opencv2/core/hal/intrin*
|
||||
opencv2/core/hal/*macros.*
|
||||
opencv2/core/hal/*.impl.*
|
||||
opencv2/core/cuda*
|
||||
opencv2/core/opencl*
|
||||
opencv2/core/private*
|
||||
|
||||
+20
-2
@@ -172,9 +172,27 @@ if(DOXYGEN_FOUND)
|
||||
list(APPEND CMAKE_DOXYGEN_HTML_FILES "${CMAKE_CURRENT_SOURCE_DIR}/tutorial-utils.js")
|
||||
string(REPLACE ";" " \\\n" CMAKE_DOXYGEN_HTML_FILES "${CMAKE_DOXYGEN_HTML_FILES}")
|
||||
|
||||
set(OPENCV_DOCS_DOT_PATH "" CACHE PATH "Doxygen/DOT_PATH value")
|
||||
set(CMAKECONFIG_DOT_PATH "${OPENCV_DOCS_DOT_PATH}")
|
||||
|
||||
set(OPENCV_DOCS_HAVE_DOT "NO" CACHE BOOL "Doxygen: build extra diagrams")
|
||||
set(CMAKECONFIG_HAVE_DOT "${OPENCV_DOCS_HAVE_DOT}")
|
||||
|
||||
# 'png' is good enough for compatibility (but requires +50% storage space)
|
||||
set(OPENCV_DOCS_DOT_IMAGE_FORMAT "svg" CACHE STRING "Doxygen/DOT_IMAGE_FORMAT value")
|
||||
set(CMAKECONFIG_DOT_IMAGE_FORMAT "${OPENCV_DOCS_DOT_IMAGE_FORMAT}")
|
||||
|
||||
# Doxygen 1.8.16 fix: https://github.com/doxygen/doxygen/pull/6870
|
||||
# NO is needed here: https://github.com/opencv/opencv/pull/16039
|
||||
set(OPENCV_DOCS_INTERACTIVE_SVG "NO" CACHE BOOL "Doxygen/INTERACTIVE_SVG value")
|
||||
set(CMAKECONFIG_INTERACTIVE_SVG "${OPENCV_DOCS_INTERACTIVE_SVG}")
|
||||
|
||||
set(OPENCV_DOCS_DOXYFILE_IN "Doxyfile.in" CACHE PATH "Doxygen configuration template file (Doxyfile.in)")
|
||||
set(OPENCV_DOCS_DOXYGEN_LAYOUT "DoxygenLayout.xml" CACHE PATH "Doxygen layout file (.xml)")
|
||||
|
||||
# writing file
|
||||
configure_file(DoxygenLayout.xml DoxygenLayout.xml @ONLY)
|
||||
configure_file(Doxyfile.in ${doxyfile} @ONLY)
|
||||
configure_file("${OPENCV_DOCS_DOXYGEN_LAYOUT}" DoxygenLayout.xml @ONLY)
|
||||
configure_file("${OPENCV_DOCS_DOXYFILE_IN}" ${doxyfile} @ONLY)
|
||||
configure_file(root.markdown.in ${rootfile} @ONLY)
|
||||
|
||||
# js tutorial assets
|
||||
|
||||
+4
-4
@@ -269,7 +269,7 @@ EXTERNAL_PAGES = YES
|
||||
CLASS_DIAGRAMS = YES
|
||||
DIA_PATH =
|
||||
HIDE_UNDOC_RELATIONS = NO
|
||||
HAVE_DOT = NO
|
||||
HAVE_DOT = @CMAKECONFIG_HAVE_DOT@
|
||||
DOT_NUM_THREADS = 0
|
||||
DOT_FONTNAME = Helvetica
|
||||
DOT_FONTSIZE = 10
|
||||
@@ -286,9 +286,9 @@ CALL_GRAPH = YES
|
||||
CALLER_GRAPH = NO
|
||||
GRAPHICAL_HIERARCHY = YES
|
||||
DIRECTORY_GRAPH = YES
|
||||
DOT_IMAGE_FORMAT = svg
|
||||
INTERACTIVE_SVG = YES
|
||||
DOT_PATH =
|
||||
DOT_IMAGE_FORMAT = @CMAKECONFIG_DOT_IMAGE_FORMAT@
|
||||
INTERACTIVE_SVG = @CMAKECONFIG_INTERACTIVE_SVG@
|
||||
DOT_PATH = @CMAKECONFIG_DOT_PATH@
|
||||
DOTFILE_DIRS =
|
||||
MSCFILE_DIRS =
|
||||
DIAFILE_DIRS =
|
||||
|
||||
@@ -27,7 +27,7 @@ src1.delete(); src2.delete(); dst.delete(); mask.delete();
|
||||
Image Subtraction
|
||||
--------------
|
||||
|
||||
You can subtract two images by OpenCV function, cv.subtract(). res = img1 - img2. Both images should be of same depth and type.
|
||||
You can subtract two images by OpenCV function, cv.subtract(). res = img1 - img2. Both images should be of same depth and type. Note that when used with RGBA images, the alpha channel is also subtracted.
|
||||
|
||||
For example, consider below sample:
|
||||
@code{.js}
|
||||
@@ -59,4 +59,4 @@ Try it
|
||||
<iframe src="../../js_image_arithmetics_bitwise.html" width="100%"
|
||||
onload="this.style.height=this.contentDocument.body.scrollHeight +'px';">
|
||||
</iframe>
|
||||
\endhtmlonly
|
||||
\endhtmlonly
|
||||
|
||||
@@ -36,7 +36,7 @@ let x = document.getElementById('myRange');
|
||||
@endcode
|
||||
|
||||
As a trackbar, the range element need a trackbar name, the default value, minimum value, maximum value,
|
||||
step and the callback function which is executed everytime trackbar value changes. The callback function
|
||||
step and the callback function which is executed every time trackbar value changes. The callback function
|
||||
always has a default argument, which is the trackbar position. Additionally, a text element to display the
|
||||
trackbar value is fine. In our case, we can create the trackbar as below:
|
||||
@code{.html}
|
||||
|
||||
@@ -0,0 +1,345 @@
|
||||
Using OpenCV.js In Node.js {#tutorial_js_nodejs}
|
||||
==========================
|
||||
|
||||
Goals
|
||||
-----
|
||||
|
||||
In this tutorial, you will learn:
|
||||
|
||||
- Use OpenCV.js in a [Node.js](https://nodejs.org) application.
|
||||
- Load images with [jimp](https://www.npmjs.com/package/jimp) in order to use them with OpenCV.js.
|
||||
- Using [jsdom](https://www.npmjs.com/package/canvas) and [node-canvas](https://www.npmjs.com/package/canvas) to support `cv.imread()`, `cv.imshow()`
|
||||
- The basics of [emscripten](https://emscripten.org/) APIs, like [Module](https://emscripten.org/docs/api_reference/module.html) and [File System](https://emscripten.org/docs/api_reference/Filesystem-API.html) on which OpenCV.js is based.
|
||||
- Learn Node.js basics. Although this tutorial assumes the user knows JavaScript, experience with Node.js is not required.
|
||||
|
||||
@note Besides giving instructions to run OpenCV.js in Node.js, another objective of this tutorial is to introduce users to the basics of [emscripten](https://emscripten.org/) APIs, like [Module](https://emscripten.org/docs/api_reference/module.html) and [File System](https://emscripten.org/docs/api_reference/Filesystem-API.html) and also Node.js.
|
||||
|
||||
|
||||
Minimal example
|
||||
-----
|
||||
|
||||
Create a file `example1.js` with the following content:
|
||||
|
||||
@code{.js}
|
||||
// Define a global variable 'Module' with a method 'onRuntimeInitialized':
|
||||
Module = {
|
||||
onRuntimeInitialized() {
|
||||
// this is our application:
|
||||
console.log(cv.getBuildInformation())
|
||||
}
|
||||
}
|
||||
// Load 'opencv.js' assigning the value to the global variable 'cv'
|
||||
cv = require('./opencv.js')
|
||||
@endcode
|
||||
|
||||
### Execute it ###
|
||||
|
||||
- Save the file as `example1.js`.
|
||||
- Make sure the file `opencv.js` is in the same folder.
|
||||
- Make sure [Node.js](https://nodejs.org) is installed on your system.
|
||||
|
||||
The following command should print OpenCV build information:
|
||||
|
||||
@code{.bash}
|
||||
node example1.js
|
||||
@endcode
|
||||
|
||||
### What just happened? ###
|
||||
|
||||
* **In the first statement**:, by defining a global variable named 'Module', emscripten will call `Module.onRuntimeInitialized()` when the library is ready to use. Our program is in that method and uses the global variable `cv` just like in the browser.
|
||||
* The statement **"cv = require('./opencv.js')"** requires the file `opencv.js` and assign the return value to the global variable `cv`.
|
||||
`require()` which is a Node.js API, is used to load modules and files.
|
||||
In this case we load the file `opencv.js` form the current folder, and, as said previously emscripten will call `Module.onRuntimeInitialized()` when its ready.
|
||||
* See [emscripten Module API](https://emscripten.org/docs/api_reference/module.html) for more details.
|
||||
|
||||
|
||||
Working with images
|
||||
-----
|
||||
|
||||
OpenCV.js doesn't support image formats so we can't load png or jpeg images directly. In the browser it uses the HTML DOM (like HTMLCanvasElement and HTMLImageElement to decode and decode images). In node.js we will need to use a library for this.
|
||||
|
||||
In this example we use [jimp](https://www.npmjs.com/package/jimp), which supports common image formats and is pretty easy to use.
|
||||
|
||||
### Example setup ###
|
||||
|
||||
Execute the following commands to create a new node.js package and install [jimp](https://www.npmjs.com/package/jimp) dependency:
|
||||
|
||||
@code{.bash}
|
||||
mkdir project1
|
||||
cd project1
|
||||
npm init -y
|
||||
npm install jimp
|
||||
@endcode
|
||||
|
||||
### The example ###
|
||||
|
||||
@code{.js}
|
||||
const Jimp = require('jimp');
|
||||
|
||||
async function onRuntimeInitialized(){
|
||||
|
||||
// load local image file with jimp. It supports jpg, png, bmp, tiff and gif:
|
||||
var jimpSrc = await Jimp.read('./lena.jpg');
|
||||
|
||||
// `jimpImage.bitmap` property has the decoded ImageData that we can use to create a cv:Mat
|
||||
var src = cv.matFromImageData(jimpSrc.bitmap);
|
||||
|
||||
// following lines is copy&paste of opencv.js dilate tutorial:
|
||||
let dst = new cv.Mat();
|
||||
let M = cv.Mat.ones(5, 5, cv.CV_8U);
|
||||
let anchor = new cv.Point(-1, -1);
|
||||
cv.dilate(src, dst, M, anchor, 1, cv.BORDER_CONSTANT, cv.morphologyDefaultBorderValue());
|
||||
|
||||
// Now that we are finish, we want to write `dst` to file `output.png`. For this we create a `Jimp`
|
||||
// image which accepts the image data as a [`Buffer`](https://nodejs.org/docs/latest-v10.x/api/buffer.html).
|
||||
// `write('output.png')` will write it to disk and Jimp infers the output format from given file name:
|
||||
new Jimp({
|
||||
width: dst.cols,
|
||||
height: dst.rows,
|
||||
data: Buffer.from(dst.data)
|
||||
})
|
||||
.write('output.png');
|
||||
|
||||
src.delete();
|
||||
dst.delete();
|
||||
}
|
||||
|
||||
// Finally, load the open.js as before. The function `onRuntimeInitialized` contains our program.
|
||||
Module = {
|
||||
onRuntimeInitialized
|
||||
};
|
||||
cv = require('./opencv.js');
|
||||
@endcode
|
||||
|
||||
### Execute it ###
|
||||
|
||||
- Save the file as `exampleNodeJimp.js`.
|
||||
- Make sure a sample image `lena.jpg` exists in the current directory.
|
||||
|
||||
The following command should generate the file `output.png`:
|
||||
|
||||
@code{.bash}
|
||||
node exampleNodeJimp.js
|
||||
@endcode
|
||||
|
||||
|
||||
Emulating HTML DOM and canvas
|
||||
-----
|
||||
|
||||
As you might already seen, the rest of the examples use functions like `cv.imread()`, `cv.imshow()` to read and write images. Unfortunately as mentioned they won't work on Node.js since there is no HTML DOM.
|
||||
|
||||
In this section, you will learn how to use [jsdom](https://www.npmjs.com/package/canvas) and [node-canvas](https://www.npmjs.com/package/canvas) to emulate the HTML DOM on Node.js so those functions work.
|
||||
|
||||
### Example setup ###
|
||||
|
||||
As before, we create a Node.js project and install the dependencies we need:
|
||||
|
||||
@code{.bash}
|
||||
mkdir project2
|
||||
cd project2
|
||||
npm init -y
|
||||
npm install canvas jsdom
|
||||
@endcode
|
||||
|
||||
### The example ###
|
||||
|
||||
@code{.js}
|
||||
const { Canvas, createCanvas, Image, ImageData, loadImage } = require('canvas');
|
||||
const { JSDOM } = require('jsdom');
|
||||
const { writeFileSync } = require('fs');
|
||||
|
||||
// This is our program. This time we use JavaScript async / await and promises to handle asynchronicity.
|
||||
(async () => {
|
||||
|
||||
// before loading opencv.js we emulate a minimal HTML DOM. See the function declaration below.
|
||||
installDOM();
|
||||
|
||||
await loadOpenCV();
|
||||
|
||||
// using node-canvas, we an image file to an object compatible with HTML DOM Image and therefore with cv.imread()
|
||||
const image = await loadImage('./lena.jpg');
|
||||
|
||||
const src = cv.imread(image);
|
||||
const dst = new cv.Mat();
|
||||
const M = cv.Mat.ones(5, 5, cv.CV_8U);
|
||||
const anchor = new cv.Point(-1, -1);
|
||||
cv.dilate(src, dst, M, anchor, 1, cv.BORDER_CONSTANT, cv.morphologyDefaultBorderValue());
|
||||
|
||||
// we create an object compatible HTMLCanvasElement
|
||||
const canvas = createCanvas(300, 300);
|
||||
cv.imshow(canvas, dst);
|
||||
writeFileSync('output.jpg', canvas.toBuffer('image/jpeg'));
|
||||
src.delete();
|
||||
dst.delete();
|
||||
})();
|
||||
|
||||
// Load opencv.js just like before but using Promise instead of callbacks:
|
||||
function loadOpenCV() {
|
||||
return new Promise(resolve => {
|
||||
global.Module = {
|
||||
onRuntimeInitialized: resolve
|
||||
};
|
||||
global.cv = require('./opencv.js');
|
||||
});
|
||||
}
|
||||
|
||||
// Using jsdom and node-canvas we define some global variables to emulate HTML DOM.
|
||||
// Although a complete emulation can be archived, here we only define those globals used
|
||||
// by cv.imread() and cv.imshow().
|
||||
function installDOM() {
|
||||
const dom = new JSDOM();
|
||||
global.document = dom.window.document;
|
||||
|
||||
// The rest enables DOM image and canvas and is provided by node-canvas
|
||||
global.Image = Image;
|
||||
global.HTMLCanvasElement = Canvas;
|
||||
global.ImageData = ImageData;
|
||||
global.HTMLImageElement = Image;
|
||||
}
|
||||
@endcode
|
||||
|
||||
### Execute it ###
|
||||
|
||||
- Save the file as `exampleNodeCanvas.js`.
|
||||
- Make sure a sample image `lena.jpg` exists in the current directory.
|
||||
|
||||
The following command should generate the file `output.jpg`:
|
||||
|
||||
@code{.bash}
|
||||
node exampleNodeCanvas.js
|
||||
@endcode
|
||||
|
||||
|
||||
Dealing with files
|
||||
-----
|
||||
|
||||
In this tutorial you will learn how to configure emscripten so it uses the local filesystem for file operations instead of using memory. Also it tries to describe how [files are supported by emscripten applications](https://emscripten.org/docs/api_reference/Filesystem-API.html)
|
||||
|
||||
Accessing the emscripten filesystem is often needed in OpenCV applications for example to load machine learning models such as the ones used in @ref tutorial_dnn_googlenet and @ref tutorial_dnn_javascript.
|
||||
|
||||
### Example setup ###
|
||||
|
||||
Before the example, is worth consider first how files are handled in emscripten applications such as OpenCV.js. Remember that OpenCV library is written in C++ and the file opencv.js is just that C++ code being translated to JavaScript or WebAssembly by emscripten C++ compiler.
|
||||
|
||||
These C++ sources use standard APIs to access the filesystem and the implementation often ends up in system calls that read a file in the hard drive. Since JavaScript applications in the browser don't have access to the local filesystem, [emscripten emulates a standard filesystem](https://emscripten.org/docs/api_reference/Filesystem-API.html) so compiled C++ code works out of the box.
|
||||
|
||||
In the browser, this filesystem is emulated in memory while in Node.js there's also the possibility of using the local filesystem directly. This is often preferable since there's no need of copy file's content in memory. This section is explains how to do do just that, this is, configuring emscripten so files are accessed directly from our local filesystem and relative paths match files relative to the current local directory as expected.
|
||||
|
||||
### The example ###
|
||||
|
||||
The following is an adaptation of @ref tutorial_js_face_detection.
|
||||
|
||||
@code{.js}
|
||||
const { Canvas, createCanvas, Image, ImageData, loadImage } = require('canvas');
|
||||
const { JSDOM } = require('jsdom');
|
||||
const { writeFileSync, readFileSync } = require('fs');
|
||||
|
||||
(async () => {
|
||||
await loadOpenCV();
|
||||
|
||||
const image = await loadImage('lena.jpg');
|
||||
const src = cv.imread(image);
|
||||
let gray = new cv.Mat();
|
||||
cv.cvtColor(src, gray, cv.COLOR_RGBA2GRAY, 0);
|
||||
let faces = new cv.RectVector();
|
||||
let eyes = new cv.RectVector();
|
||||
let faceCascade = new cv.CascadeClassifier();
|
||||
let eyeCascade = new cv.CascadeClassifier();
|
||||
|
||||
// Load pre-trained classifier files. Notice how we reference local files using relative paths just
|
||||
// like we normally would do
|
||||
faceCascade.load('./haarcascade_frontalface_default.xml');
|
||||
eyeCascade.load('./haarcascade_eye.xml');
|
||||
|
||||
let mSize = new cv.Size(0, 0);
|
||||
faceCascade.detectMultiScale(gray, faces, 1.1, 3, 0, mSize, mSize);
|
||||
for (let i = 0; i < faces.size(); ++i) {
|
||||
let roiGray = gray.roi(faces.get(i));
|
||||
let roiSrc = src.roi(faces.get(i));
|
||||
let point1 = new cv.Point(faces.get(i).x, faces.get(i).y);
|
||||
let point2 = new cv.Point(faces.get(i).x + faces.get(i).width, faces.get(i).y + faces.get(i).height);
|
||||
cv.rectangle(src, point1, point2, [255, 0, 0, 255]);
|
||||
eyeCascade.detectMultiScale(roiGray, eyes);
|
||||
for (let j = 0; j < eyes.size(); ++j) {
|
||||
let point1 = new cv.Point(eyes.get(j).x, eyes.get(j).y);
|
||||
let point2 = new cv.Point(eyes.get(j).x + eyes.get(j).width, eyes.get(j).y + eyes.get(j).height);
|
||||
cv.rectangle(roiSrc, point1, point2, [0, 0, 255, 255]);
|
||||
}
|
||||
roiGray.delete();
|
||||
roiSrc.delete();
|
||||
}
|
||||
|
||||
const canvas = createCanvas(image.width, image.height);
|
||||
cv.imshow(canvas, src);
|
||||
writeFileSync('output3.jpg', canvas.toBuffer('image/jpeg'));
|
||||
src.delete(); gray.delete(); faceCascade.delete(); eyeCascade.delete(); faces.delete(); eyes.delete()
|
||||
})();
|
||||
|
||||
/**
|
||||
* Loads opencv.js.
|
||||
*
|
||||
* Installs HTML Canvas emulation to support `cv.imread()` and `cv.imshow`
|
||||
*
|
||||
* Mounts given local folder `localRootDir` in emscripten filesystem folder `rootDir`. By default it will mount the local current directory in emscripten `/work` directory. This means that `/work/foo.txt` will be resolved to the local file `./foo.txt`
|
||||
* @param {string} rootDir The directory in emscripten filesystem in which the local filesystem will be mount.
|
||||
* @param {string} localRootDir The local directory to mount in emscripten filesystem.
|
||||
* @returns {Promise} resolved when the library is ready to use.
|
||||
*/
|
||||
function loadOpenCV(rootDir = '/work', localRootDir = process.cwd()) {
|
||||
if(global.Module && global.Module.onRuntimeInitialized && global.cv && global.cv.imread) {
|
||||
return Promise.resolve()
|
||||
}
|
||||
return new Promise(resolve => {
|
||||
installDOM()
|
||||
global.Module = {
|
||||
onRuntimeInitialized() {
|
||||
// We change emscripten current work directory to 'rootDir' so relative paths are resolved
|
||||
// relative to the current local folder, as expected
|
||||
cv.FS.chdir(rootDir)
|
||||
resolve()
|
||||
},
|
||||
preRun() {
|
||||
// preRun() is another callback like onRuntimeInitialized() but is called just before the
|
||||
// library code runs. Here we mount a local folder in emscripten filesystem and we want to
|
||||
// do this before the library is executed so the filesystem is accessible from the start
|
||||
const FS = global.Module.FS
|
||||
// create rootDir if it doesn't exists
|
||||
if(!FS.analyzePath(rootDir).exists) {
|
||||
FS.mkdir(rootDir);
|
||||
}
|
||||
// create localRootFolder if it doesn't exists
|
||||
if(!existsSync(localRootDir)) {
|
||||
mkdirSync(localRootDir, { recursive: true});
|
||||
}
|
||||
// FS.mount() is similar to Linux/POSIX mount operation. It basically mounts an external
|
||||
// filesystem with given format, in given current filesystem directory.
|
||||
FS.mount(FS.filesystems.NODEFS, { root: localRootDir}, rootDir);
|
||||
}
|
||||
};
|
||||
global.cv = require('./opencv.js')
|
||||
});
|
||||
}
|
||||
|
||||
function installDOM(){
|
||||
const dom = new JSDOM();
|
||||
global.document = dom.window.document;
|
||||
global.Image = Image;
|
||||
global.HTMLCanvasElement = Canvas;
|
||||
global.ImageData = ImageData;
|
||||
global.HTMLImageElement = Image;
|
||||
}
|
||||
@endcode
|
||||
|
||||
### Execute it ###
|
||||
|
||||
- Save the file as `exampleNodeCanvasData.js`.
|
||||
- Make sure the files `aarcascade_frontalface_default.xml` and `haarcascade_eye.xml` are present in project's directory. They can be obtained from [OpenCV sources](https://github.com/opencv/opencv/tree/master/data/haarcascades).
|
||||
- Make sure a sample image file `lena.jpg` exists in project's directory. It should display people's faces for this example to make sense. The following image is known to work:
|
||||
|
||||

|
||||
|
||||
The following command should generate the file `output3.jpg`:
|
||||
|
||||
@code{.bash}
|
||||
node exampleNodeCanvasData.js
|
||||
@endcode
|
||||
@@ -91,21 +91,60 @@ Building OpenCV.js from Source
|
||||
python ./platforms/js/build_js.py build_js --build_test
|
||||
@endcode
|
||||
|
||||
To run tests, launch a local web server in \<build_dir\>/bin folder. For example, node http-server which serves on `localhost:8080`.
|
||||
Running OpenCV.js Tests
|
||||
---------------------------------------
|
||||
|
||||
Navigate the web browser to `http://localhost:8080/tests.html`, which runs the unit tests automatically.
|
||||
Remember to launch the build command passing `--build_test` as mentioned previously. This will generate test source code ready to run together with `opencv.js` file in `build_js/bin`
|
||||
|
||||
You can also run tests using Node.js.
|
||||
### Manually in your browser
|
||||
|
||||
For example:
|
||||
@code{.sh}
|
||||
cd bin
|
||||
npm install
|
||||
node tests.js
|
||||
@endcode
|
||||
To run tests, launch a local web server in `\<build_dir\>/bin` folder. For example, node http-server which serves on `localhost:8080`.
|
||||
|
||||
Navigate the web browser to `http://localhost:8080/tests.html`, which runs the unit tests automatically. Command example:
|
||||
|
||||
@code{.sh}
|
||||
npx http-server build_js/bin
|
||||
firefox http://localhost:8080/tests.html
|
||||
@endcode
|
||||
|
||||
@note
|
||||
This snippet and the following require [Node.js](https://nodejs.org) to be installed.
|
||||
|
||||
### Headless with Puppeteer
|
||||
|
||||
Alternatively tests can run with [GoogleChrome/puppeteer](https://github.com/GoogleChrome/puppeteer#readme) which is a version of Google Chrome that runs in the terminal (useful for Continuos integration like travis CI, etc)
|
||||
|
||||
@code{.sh}
|
||||
cd build_js/bin
|
||||
npm install
|
||||
npm install --no-save puppeteer # automatically downloads Chromium package
|
||||
node run_puppeteer.js
|
||||
@endcode
|
||||
|
||||
@note
|
||||
Checkout `node run_puppeteer --help` for more options to debug and reporting.
|
||||
|
||||
@note
|
||||
The command `npm install` only needs to be executed once, since installs the tools dependencies; after that they are ready to use.
|
||||
|
||||
@note
|
||||
Use `PUPPETEER_SKIP_CHROMIUM_DOWNLOAD=1 npm install --no-save puppeteer` to skip automatic downloading of Chromium.
|
||||
You may specify own Chromium/Chrome binary through `PUPPETEER_EXECUTABLE_PATH=$(which google-chrome)` environment variable.
|
||||
**BEWARE**: Puppeteer is only guaranteed to work with the bundled Chromium, use at your own risk.
|
||||
|
||||
|
||||
### Using Node.js.
|
||||
|
||||
For example:
|
||||
|
||||
@code{.sh}
|
||||
cd build_js/bin
|
||||
npm install
|
||||
node tests.js
|
||||
@endcode
|
||||
|
||||
@note If all tests are failed, then consider using Node.js from 8.x version (`lts/carbon` from `nvm`).
|
||||
|
||||
@note
|
||||
It requires `node` installed in your development environment.
|
||||
|
||||
-# [optional] To build `opencv.js` with threads optimization, append `--threads` option.
|
||||
|
||||
|
||||
@@ -12,3 +12,7 @@ Introduction to OpenCV.js {#tutorial_js_table_of_contents_setup}
|
||||
- @subpage tutorial_js_setup
|
||||
|
||||
Build OpenCV.js from source
|
||||
|
||||
- @subpage tutorial_js_nodejs
|
||||
|
||||
Using OpenCV.js In Node.js
|
||||
@@ -102,6 +102,14 @@
|
||||
publisher = {Elsevier},
|
||||
url = {https://www.cs.bgu.ac.il/~icbv161/wiki.files/Readings/1981-Ballard-Generalizing_the_Hough_Transform_to_Detect_Arbitrary_Shapes.pdf}
|
||||
}
|
||||
@techreport{blanco2010tutorial,
|
||||
title = {A tutorial on SE(3) transformation parameterizations and on-manifold optimization},
|
||||
author = {Blanco, Jose-Luis},
|
||||
institution = {University of Malaga},
|
||||
number = {012010},
|
||||
year = {2010},
|
||||
url = {http://ingmec.ual.es/~jlblanco/papers/jlblanco2010geometry3D_techrep.pdf}
|
||||
}
|
||||
@article{Borgefors86,
|
||||
author = {Borgefors, Gunilla},
|
||||
title = {Distance transformations in digital images},
|
||||
@@ -277,6 +285,12 @@
|
||||
year = {2013},
|
||||
url = {http://ethaneade.com/optimization.pdf}
|
||||
}
|
||||
@misc{Eade17,
|
||||
author = {Eade, Ethan},
|
||||
title = {Lie Groups for 2D and 3D Transformation},
|
||||
year = {2017},
|
||||
url = {http://www.ethaneade.com/lie.pdf}
|
||||
}
|
||||
@inproceedings{EM11,
|
||||
author = {Gastal, Eduardo SL and Oliveira, Manuel M},
|
||||
title = {Domain transform for edge-aware image and video processing},
|
||||
@@ -397,6 +411,14 @@
|
||||
year = {1999},
|
||||
url = {https://pdfs.semanticscholar.org/090d/25f94cb021bdd3400a2f547f989a6a5e07ec.pdf}
|
||||
}
|
||||
@article{Gallego2014ACF,
|
||||
title = {A Compact Formula for the Derivative of a 3-D Rotation in Exponential Coordinates},
|
||||
author = {Guillermo Gallego and Anthony J. Yezzi},
|
||||
journal = {Journal of Mathematical Imaging and Vision},
|
||||
year = {2014},
|
||||
volume = {51},
|
||||
pages = {378-384}
|
||||
}
|
||||
@article{taubin1991,
|
||||
abstract = {The author addresses the problem of parametric representation and estimation of complex planar curves in 2-D surfaces in 3-D, and nonplanar space curves in 3-D. Curves and surfaces can be defined either parametrically or implicitly, with the latter representation used here. A planar curve is the set of zeros of a smooth function of two variables <e1>x</e1>-<e1>y</e1>, a surface is the set of zeros of a smooth function of three variables <e1>x</e1>-<e1>y</e1>-<e1>z</e1>, and a space curve is the intersection of two surfaces, which are the set of zeros of two linearly independent smooth functions of three variables <e1>x</e1>-<e1>y</e1>-<e1>z</e1> For example, the surface of a complex object in 3-D can be represented as a subset of a single implicit surface, with similar results for planar and space curves. It is shown how this unified representation can be used for object recognition, object position estimation, and segmentation of objects into meaningful subobjects, that is, the detection of `interest regions' that are more complex than high curvature regions and, hence, more useful as features for object recognition},
|
||||
author = {Taubin, Gabriel},
|
||||
@@ -915,6 +937,13 @@
|
||||
journal = {Retrieved on August},
|
||||
volume = {6}
|
||||
}
|
||||
@article{Sol2018AML,
|
||||
title = {A micro Lie theory for state estimation in robotics},
|
||||
author = {Joan Sol{\`a} and J{\'e}r{\'e}mie Deray and Dinesh Atchuthan},
|
||||
journal = {ArXiv},
|
||||
year = {2018},
|
||||
volume={abs/1812.01537}
|
||||
}
|
||||
@misc{SteweniusCFS,
|
||||
author = {Stewenius, Henrik},
|
||||
title = {Calibrated Fivepoint solver},
|
||||
|
||||
@@ -8,13 +8,13 @@ Learn to:
|
||||
|
||||
- Access pixel values and modify them
|
||||
- Access image properties
|
||||
- Setting Region of Interest (ROI)
|
||||
- Splitting and Merging images
|
||||
- Set a Region of Interest (ROI)
|
||||
- Split and merge images
|
||||
|
||||
Almost all the operations in this section is mainly related to Numpy rather than OpenCV. A good
|
||||
Almost all the operations in this section are mainly related to Numpy rather than OpenCV. A good
|
||||
knowledge of Numpy is required to write better optimized code with OpenCV.
|
||||
|
||||
*( Examples will be shown in Python terminal since most of them are just single line codes )*
|
||||
*( Examples will be shown in a Python terminal, since most of them are just single lines of code )*
|
||||
|
||||
Accessing and Modifying pixel values
|
||||
------------------------------------
|
||||
@@ -45,15 +45,15 @@ You can modify the pixel values the same way.
|
||||
[255 255 255]
|
||||
@endcode
|
||||
|
||||
**warning**
|
||||
**Warning**
|
||||
|
||||
Numpy is a optimized library for fast array calculations. So simply accessing each and every pixel
|
||||
values and modifying it will be very slow and it is discouraged.
|
||||
Numpy is an optimized library for fast array calculations. So simply accessing each and every pixel
|
||||
value and modifying it will be very slow and it is discouraged.
|
||||
|
||||
@note The above method is normally used for selecting a region of an array, say the first 5 rows
|
||||
and last 3 columns. For individual pixel access, the Numpy array methods, array.item() and
|
||||
array.itemset() are considered better, however they always return a scalar. If you want to access
|
||||
all B,G,R values, you need to call array.item() separately for all.
|
||||
array.itemset() are considered better. They always return a scalar, however, so if you want to access
|
||||
all the B,G,R values, you will need to call array.item() separately for each value.
|
||||
|
||||
Better pixel accessing and editing method :
|
||||
@code{.py}
|
||||
@@ -70,11 +70,10 @@ Better pixel accessing and editing method :
|
||||
Accessing Image Properties
|
||||
--------------------------
|
||||
|
||||
Image properties include number of rows, columns and channels, type of image data, number of pixels
|
||||
etc.
|
||||
Image properties include number of rows, columns, and channels; type of image data; number of pixels; etc.
|
||||
|
||||
The shape of an image is accessed by img.shape. It returns a tuple of number of rows, columns, and channels
|
||||
(if image is color):
|
||||
The shape of an image is accessed by img.shape. It returns a tuple of the number of rows, columns, and channels
|
||||
(if the image is color):
|
||||
@code{.py}
|
||||
>>> print( img.shape )
|
||||
(342, 548, 3)
|
||||
@@ -95,13 +94,13 @@ uint8
|
||||
@endcode
|
||||
|
||||
@note img.dtype is very important while debugging because a large number of errors in OpenCV-Python
|
||||
code is caused by invalid datatype.
|
||||
code are caused by invalid datatype.
|
||||
|
||||
Image ROI
|
||||
---------
|
||||
|
||||
Sometimes, you will have to play with certain region of images. For eye detection in images, first
|
||||
face detection is done all over the image. When a face is obtained, we select the face region alone
|
||||
Sometimes, you will have to play with certain regions of images. For eye detection in images, first
|
||||
face detection is done over the entire image. When a face is obtained, we select the face region alone
|
||||
and search for eyes inside it instead of searching the whole image. It improves accuracy (because eyes
|
||||
are always on faces :D ) and performance (because we search in a small area).
|
||||
|
||||
@@ -118,9 +117,9 @@ Check the results below:
|
||||
Splitting and Merging Image Channels
|
||||
------------------------------------
|
||||
|
||||
Sometimes you will need to work separately on B,G,R channels of image. In this case, you need
|
||||
to split the BGR images to single channels. In other cases, you may need to join these individual
|
||||
channels to a BGR image. You can do it simply by:
|
||||
Sometimes you will need to work separately on the B,G,R channels of an image. In this case, you need
|
||||
to split the BGR image into single channels. In other cases, you may need to join these individual
|
||||
channels to create a BGR image. You can do this simply by:
|
||||
@code{.py}
|
||||
>>> b,g,r = cv.split(img)
|
||||
>>> img = cv.merge((b,g,r))
|
||||
@@ -129,7 +128,7 @@ Or
|
||||
@code
|
||||
>>> b = img[:,:,0]
|
||||
@endcode
|
||||
Suppose you want to set all the red pixels to zero, you do not need to split the channels first.
|
||||
Suppose you want to set all the red pixels to zero - you do not need to split the channels first.
|
||||
Numpy indexing is faster:
|
||||
@code{.py}
|
||||
>>> img[:,:,2] = 0
|
||||
@@ -137,13 +136,13 @@ Numpy indexing is faster:
|
||||
|
||||
**Warning**
|
||||
|
||||
cv.split() is a costly operation (in terms of time). So do it only if you need it. Otherwise go
|
||||
cv.split() is a costly operation (in terms of time). So use it only if necessary. Otherwise go
|
||||
for Numpy indexing.
|
||||
|
||||
Making Borders for Images (Padding)
|
||||
-----------------------------------
|
||||
|
||||
If you want to create a border around the image, something like a photo frame, you can use
|
||||
If you want to create a border around an image, something like a photo frame, you can use
|
||||
**cv.copyMakeBorder()**. But it has more applications for convolution operation, zero
|
||||
padding etc. This function takes following arguments:
|
||||
|
||||
|
||||
@@ -4,21 +4,20 @@ Arithmetic Operations on Images {#tutorial_py_image_arithmetics}
|
||||
Goal
|
||||
----
|
||||
|
||||
- Learn several arithmetic operations on images like addition, subtraction, bitwise operations
|
||||
etc.
|
||||
- You will learn these functions : **cv.add()**, **cv.addWeighted()** etc.
|
||||
- Learn several arithmetic operations on images, like addition, subtraction, bitwise operations, and etc.
|
||||
- Learn these functions: **cv.add()**, **cv.addWeighted()**, etc.
|
||||
|
||||
Image Addition
|
||||
--------------
|
||||
|
||||
You can add two images by OpenCV function, cv.add() or simply by numpy operation,
|
||||
res = img1 + img2. Both images should be of same depth and type, or second image can just be a
|
||||
You can add two images with the OpenCV function, cv.add(), or simply by the numpy operation
|
||||
res = img1 + img2. Both images should be of same depth and type, or the second image can just be a
|
||||
scalar value.
|
||||
|
||||
@note There is a difference between OpenCV addition and Numpy addition. OpenCV addition is a
|
||||
saturated operation while Numpy addition is a modulo operation.
|
||||
|
||||
For example, consider below sample:
|
||||
For example, consider the below sample:
|
||||
@code{.py}
|
||||
>>> x = np.uint8([250])
|
||||
>>> y = np.uint8([10])
|
||||
@@ -29,13 +28,12 @@ For example, consider below sample:
|
||||
>>> print( x+y ) # 250+10 = 260 % 256 = 4
|
||||
[4]
|
||||
@endcode
|
||||
It will be more visible when you add two images. OpenCV function will provide a better result. So
|
||||
always better stick to OpenCV functions.
|
||||
This will be more visible when you add two images. Stick with OpenCV functions, because they will provide a better result.
|
||||
|
||||
Image Blending
|
||||
--------------
|
||||
|
||||
This is also image addition, but different weights are given to images so that it gives a feeling of
|
||||
This is also image addition, but different weights are given to images in order to give a feeling of
|
||||
blending or transparency. Images are added as per the equation below:
|
||||
|
||||
\f[g(x) = (1 - \alpha)f_{0}(x) + \alpha f_{1}(x)\f]
|
||||
@@ -43,8 +41,8 @@ blending or transparency. Images are added as per the equation below:
|
||||
By varying \f$\alpha\f$ from \f$0 \rightarrow 1\f$, you can perform a cool transition between one image to
|
||||
another.
|
||||
|
||||
Here I took two images to blend them together. First image is given a weight of 0.7 and second image
|
||||
is given 0.3. cv.addWeighted() applies following equation on the image.
|
||||
Here I took two images to blend together. The first image is given a weight of 0.7 and the second image
|
||||
is given 0.3. cv.addWeighted() applies the following equation to the image:
|
||||
|
||||
\f[dst = \alpha \cdot img1 + \beta \cdot img2 + \gamma\f]
|
||||
|
||||
@@ -66,14 +64,14 @@ Check the result below:
|
||||
Bitwise Operations
|
||||
------------------
|
||||
|
||||
This includes bitwise AND, OR, NOT and XOR operations. They will be highly useful while extracting
|
||||
This includes the bitwise AND, OR, NOT, and XOR operations. They will be highly useful while extracting
|
||||
any part of the image (as we will see in coming chapters), defining and working with non-rectangular
|
||||
ROI etc. Below we will see an example on how to change a particular region of an image.
|
||||
ROI's, and etc. Below we will see an example of how to change a particular region of an image.
|
||||
|
||||
I want to put OpenCV logo above an image. If I add two images, it will change color. If I blend it,
|
||||
I get an transparent effect. But I want it to be opaque. If it was a rectangular region, I could use
|
||||
ROI as we did in last chapter. But OpenCV logo is a not a rectangular shape. So you can do it with
|
||||
bitwise operations as below:
|
||||
I want to put the OpenCV logo above an image. If I add two images, it will change the color. If I blend them,
|
||||
I get a transparent effect. But I want it to be opaque. If it was a rectangular region, I could use
|
||||
ROI as we did in the last chapter. But the OpenCV logo is a not a rectangular shape. So you can do it with
|
||||
bitwise operations as shown below:
|
||||
@code{.py}
|
||||
# Load two images
|
||||
img1 = cv.imread('messi5.jpg')
|
||||
@@ -81,7 +79,7 @@ img2 = cv.imread('opencv-logo-white.png')
|
||||
|
||||
# I want to put logo on top-left corner, So I create a ROI
|
||||
rows,cols,channels = img2.shape
|
||||
roi = img1[0:rows, 0:cols ]
|
||||
roi = img1[0:rows, 0:cols]
|
||||
|
||||
# Now create a mask of logo and create its inverse mask also
|
||||
img2gray = cv.cvtColor(img2,cv.COLOR_BGR2GRAY)
|
||||
|
||||
@@ -4,28 +4,27 @@ Performance Measurement and Improvement Techniques {#tutorial_py_optimization}
|
||||
Goal
|
||||
----
|
||||
|
||||
In image processing, since you are dealing with large number of operations per second, it is
|
||||
mandatory that your code is not only providing the correct solution, but also in the fastest manner.
|
||||
So in this chapter, you will learn
|
||||
In image processing, since you are dealing with a large number of operations per second, it is mandatory that your code is not only providing the correct solution, but that it is also providing it in the fastest manner.
|
||||
So in this chapter, you will learn:
|
||||
|
||||
- To measure the performance of your code.
|
||||
- Some tips to improve the performance of your code.
|
||||
- You will see these functions : **cv.getTickCount**, **cv.getTickFrequency** etc.
|
||||
- You will see these functions: **cv.getTickCount**, **cv.getTickFrequency**, etc.
|
||||
|
||||
Apart from OpenCV, Python also provides a module **time** which is helpful in measuring the time of
|
||||
execution. Another module **profile** helps to get detailed report on the code, like how much time
|
||||
each function in the code took, how many times the function was called etc. But, if you are using
|
||||
execution. Another module **profile** helps to get a detailed report on the code, like how much time
|
||||
each function in the code took, how many times the function was called, etc. But, if you are using
|
||||
IPython, all these features are integrated in an user-friendly manner. We will see some important
|
||||
ones, and for more details, check links in **Additional Resources** section.
|
||||
ones, and for more details, check links in the **Additional Resources** section.
|
||||
|
||||
Measuring Performance with OpenCV
|
||||
---------------------------------
|
||||
|
||||
**cv.getTickCount** function returns the number of clock-cycles after a reference event (like the
|
||||
moment machine was switched ON) to the moment this function is called. So if you call it before and
|
||||
after the function execution, you get number of clock-cycles used to execute a function.
|
||||
The **cv.getTickCount** function returns the number of clock-cycles after a reference event (like the
|
||||
moment the machine was switched ON) to the moment this function is called. So if you call it before and
|
||||
after the function execution, you get the number of clock-cycles used to execute a function.
|
||||
|
||||
**cv.getTickFrequency** function returns the frequency of clock-cycles, or the number of
|
||||
The **cv.getTickFrequency** function returns the frequency of clock-cycles, or the number of
|
||||
clock-cycles per second. So to find the time of execution in seconds, you can do following:
|
||||
@code{.py}
|
||||
e1 = cv.getTickCount()
|
||||
@@ -33,8 +32,8 @@ e1 = cv.getTickCount()
|
||||
e2 = cv.getTickCount()
|
||||
time = (e2 - e1)/ cv.getTickFrequency()
|
||||
@endcode
|
||||
We will demonstrate with following example. Following example apply median filtering with a kernel
|
||||
of odd size ranging from 5 to 49. (Don't worry about what will the result look like, that is not our
|
||||
We will demonstrate with following example. The following example applies median filtering with kernels
|
||||
of odd sizes ranging from 5 to 49. (Don't worry about what the result will look like - that is not our
|
||||
goal):
|
||||
@code{.py}
|
||||
img1 = cv.imread('messi5.jpg')
|
||||
@@ -48,16 +47,16 @@ print( t )
|
||||
|
||||
# Result I got is 0.521107655 seconds
|
||||
@endcode
|
||||
@note You can do the same with time module. Instead of cv.getTickCount, use time.time() function.
|
||||
Then take the difference of two times.
|
||||
@note You can do the same thing with the time module. Instead of cv.getTickCount, use the time.time() function.
|
||||
Then take the difference of the two times.
|
||||
|
||||
Default Optimization in OpenCV
|
||||
------------------------------
|
||||
|
||||
Many of the OpenCV functions are optimized using SSE2, AVX etc. It contains unoptimized code also.
|
||||
Many of the OpenCV functions are optimized using SSE2, AVX, etc. It contains the unoptimized code also.
|
||||
So if our system support these features, we should exploit them (almost all modern day processors
|
||||
support them). It is enabled by default while compiling. So OpenCV runs the optimized code if it is
|
||||
enabled, else it runs the unoptimized code. You can use **cv.useOptimized()** to check if it is
|
||||
enabled, otherwise it runs the unoptimized code. You can use **cv.useOptimized()** to check if it is
|
||||
enabled/disabled and **cv.setUseOptimized()** to enable/disable it. Let's see a simple example.
|
||||
@code{.py}
|
||||
# check if optimization is enabled
|
||||
@@ -76,8 +75,8 @@ Out[8]: False
|
||||
In [9]: %timeit res = cv.medianBlur(img,49)
|
||||
10 loops, best of 3: 64.1 ms per loop
|
||||
@endcode
|
||||
See, optimized median filtering is \~2x faster than unoptimized version. If you check its source,
|
||||
you can see median filtering is SIMD optimized. So you can use this to enable optimization at the
|
||||
As you can see, optimized median filtering is \~2x faster than the unoptimized version. If you check its source,
|
||||
you can see that median filtering is SIMD optimized. So you can use this to enable optimization at the
|
||||
top of your code (remember it is enabled by default).
|
||||
|
||||
Measuring Performance in IPython
|
||||
@@ -85,10 +84,10 @@ Measuring Performance in IPython
|
||||
|
||||
Sometimes you may need to compare the performance of two similar operations. IPython gives you a
|
||||
magic command %timeit to perform this. It runs the code several times to get more accurate results.
|
||||
Once again, they are suitable to measure single line codes.
|
||||
Once again, it is suitable to measuring single lines of code.
|
||||
|
||||
For example, do you know which of the following addition operation is better, x = 5; y = x\*\*2,
|
||||
x = 5; y = x\*x, x = np.uint8([5]); y = x\*x or y = np.square(x) ? We will find it with %timeit in
|
||||
For example, do you know which of the following addition operations is better, x = 5; y = x\*\*2,
|
||||
x = 5; y = x\*x, x = np.uint8([5]); y = x\*x, or y = np.square(x)? We will find out with %timeit in the
|
||||
IPython shell.
|
||||
@code{.py}
|
||||
In [10]: x = 5
|
||||
@@ -108,15 +107,15 @@ In [19]: %timeit y=np.square(z)
|
||||
1000000 loops, best of 3: 1.16 us per loop
|
||||
@endcode
|
||||
You can see that, x = 5 ; y = x\*x is fastest and it is around 20x faster compared to Numpy. If you
|
||||
consider the array creation also, it may reach upto 100x faster. Cool, right? *(Numpy devs are
|
||||
consider the array creation also, it may reach up to 100x faster. Cool, right? *(Numpy devs are
|
||||
working on this issue)*
|
||||
|
||||
@note Python scalar operations are faster than Numpy scalar operations. So for operations including
|
||||
one or two elements, Python scalar is better than Numpy arrays. Numpy takes advantage when size of
|
||||
array is a little bit bigger.
|
||||
one or two elements, Python scalar is better than Numpy arrays. Numpy has the advantage when the size of
|
||||
the array is a little bit bigger.
|
||||
|
||||
We will try one more example. This time, we will compare the performance of **cv.countNonZero()**
|
||||
and **np.count_nonzero()** for same image.
|
||||
and **np.count_nonzero()** for the same image.
|
||||
|
||||
@code{.py}
|
||||
In [35]: %timeit z = cv.countNonZero(img)
|
||||
@@ -125,7 +124,7 @@ In [35]: %timeit z = cv.countNonZero(img)
|
||||
In [36]: %timeit z = np.count_nonzero(img)
|
||||
1000 loops, best of 3: 370 us per loop
|
||||
@endcode
|
||||
See, OpenCV function is nearly 25x faster than Numpy function.
|
||||
See, the OpenCV function is nearly 25x faster than the Numpy function.
|
||||
|
||||
@note Normally, OpenCV functions are faster than Numpy functions. So for same operation, OpenCV
|
||||
functions are preferred. But, there can be exceptions, especially when Numpy works with views
|
||||
@@ -134,8 +133,8 @@ instead of copies.
|
||||
More IPython magic commands
|
||||
---------------------------
|
||||
|
||||
There are several other magic commands to measure the performance, profiling, line profiling, memory
|
||||
measurement etc. They all are well documented. So only links to those docs are provided here.
|
||||
There are several other magic commands to measure performance, profiling, line profiling, memory
|
||||
measurement, and etc. They all are well documented. So only links to those docs are provided here.
|
||||
Interested readers are recommended to try them out.
|
||||
|
||||
Performance Optimization Techniques
|
||||
@@ -143,19 +142,18 @@ Performance Optimization Techniques
|
||||
|
||||
There are several techniques and coding methods to exploit maximum performance of Python and Numpy.
|
||||
Only relevant ones are noted here and links are given to important sources. The main thing to be
|
||||
noted here is that, first try to implement the algorithm in a simple manner. Once it is working,
|
||||
profile it, find the bottlenecks and optimize them.
|
||||
noted here is, first try to implement the algorithm in a simple manner. Once it is working,
|
||||
profile it, find the bottlenecks, and optimize them.
|
||||
|
||||
-# Avoid using loops in Python as far as possible, especially double/triple loops etc. They are
|
||||
-# Avoid using loops in Python as much as possible, especially double/triple loops etc. They are
|
||||
inherently slow.
|
||||
2. Vectorize the algorithm/code to the maximum possible extent because Numpy and OpenCV are
|
||||
2. Vectorize the algorithm/code to the maximum extent possible, because Numpy and OpenCV are
|
||||
optimized for vector operations.
|
||||
3. Exploit the cache coherence.
|
||||
4. Never make copies of array unless it is needed. Try to use views instead. Array copying is a
|
||||
4. Never make copies of an array unless it is necessary. Try to use views instead. Array copying is a
|
||||
costly operation.
|
||||
|
||||
Even after doing all these operations, if your code is still slow, or use of large loops are
|
||||
inevitable, use additional libraries like Cython to make it faster.
|
||||
If your code is still slow after doing all of these operations, or if the use of large loops is inevitable, use additional libraries like Cython to make it faster.
|
||||
|
||||
Additional Resources
|
||||
--------------------
|
||||
|
||||
@@ -43,11 +43,19 @@ points than for SURF points.
|
||||
In short, BRIEF is a faster method feature descriptor calculation and matching. It also provides
|
||||
high recognition rate unless there is large in-plane rotation.
|
||||
|
||||
STAR(CenSurE) in OpenCV
|
||||
------
|
||||
STAR is a feature detector derived from CenSurE.
|
||||
Unlike CenSurE however, which uses polygons like squares, hexagons and octagons to approach a circle,
|
||||
Star emulates a circle with 2 overlapping squares: 1 upright and 1 45-degree rotated. These polygons are bi-level.
|
||||
They can be seen as polygons with thick borders. The borders and the enclosed area have weights of opposing signs.
|
||||
This has better computational characteristics than other scale-space detectors and it is capable of real-time implementation.
|
||||
In contrast to SIFT and SURF, which find extrema at sub-sampled pixels that compromises accuracy at larger scales,
|
||||
CenSurE creates a feature vector using full spatial resolution at all scales in the pyramid.
|
||||
BRIEF in OpenCV
|
||||
---------------
|
||||
|
||||
Below code shows the computation of BRIEF descriptors with the help of CenSurE detector. (CenSurE
|
||||
detector is called STAR detector in OpenCV)
|
||||
Below code shows the computation of BRIEF descriptors with the help of CenSurE detector.
|
||||
|
||||
note, that you need [opencv contrib](https://github.com/opencv/opencv_contrib)) to use this.
|
||||
@code{.py}
|
||||
|
||||
@@ -4,21 +4,21 @@ Gui Features in OpenCV {#tutorial_py_table_of_contents_gui}
|
||||
- @subpage tutorial_py_image_display
|
||||
|
||||
Learn to load an
|
||||
image, display it and save it back
|
||||
image, display it, and save it back
|
||||
|
||||
- @subpage tutorial_py_video_display
|
||||
|
||||
Learn to play videos,
|
||||
capture videos from Camera and write it as a video
|
||||
capture videos from a camera, and write videos
|
||||
|
||||
- @subpage tutorial_py_drawing_functions
|
||||
|
||||
Learn to draw lines,
|
||||
rectangles, ellipses, circles etc with OpenCV
|
||||
rectangles, ellipses, circles, etc with OpenCV
|
||||
|
||||
- @subpage tutorial_py_mouse_handling
|
||||
|
||||
Draw stuffs with your
|
||||
Draw stuff with your
|
||||
mouse
|
||||
|
||||
- @subpage tutorial_py_trackbar
|
||||
|
||||
@@ -14,9 +14,9 @@ Here we will create a simple application which shows the color you specify. You
|
||||
shows the color and three trackbars to specify each of B,G,R colors. You slide the trackbar and
|
||||
correspondingly window color changes. By default, initial color will be set to Black.
|
||||
|
||||
For cv.getTrackbarPos() function, first argument is the trackbar name, second one is the window
|
||||
For cv.createTrackbar() function, first argument is the trackbar name, second one is the window
|
||||
name to which it is attached, third argument is the default value, fourth one is the maximum value
|
||||
and fifth one is the callback function which is executed everytime trackbar value changes. The
|
||||
and fifth one is the callback function which is executed every time trackbar value changes. The
|
||||
callback function always has a default argument which is the trackbar position. In our case,
|
||||
function does nothing, so we simply pass.
|
||||
|
||||
|
||||
@@ -4,19 +4,19 @@ Getting Started with Videos {#tutorial_py_video_display}
|
||||
Goal
|
||||
----
|
||||
|
||||
- Learn to read video, display video and save video.
|
||||
- Learn to capture from Camera and display it.
|
||||
- Learn to read video, display video, and save video.
|
||||
- Learn to capture video from a camera and display it.
|
||||
- You will learn these functions : **cv.VideoCapture()**, **cv.VideoWriter()**
|
||||
|
||||
Capture Video from Camera
|
||||
-------------------------
|
||||
|
||||
Often, we have to capture live stream with camera. OpenCV provides a very simple interface to this.
|
||||
Let's capture a video from the camera (I am using the in-built webcam of my laptop), convert it into
|
||||
Often, we have to capture live stream with a camera. OpenCV provides a very simple interface to do this.
|
||||
Let's capture a video from the camera (I am using the built-in webcam on my laptop), convert it into
|
||||
grayscale video and display it. Just a simple task to get started.
|
||||
|
||||
To capture a video, you need to create a **VideoCapture** object. Its argument can be either the
|
||||
device index or the name of a video file. Device index is just the number to specify which camera.
|
||||
device index or the name of a video file. A device index is just the number to specify which camera.
|
||||
Normally one camera will be connected (as in my case). So I simply pass 0 (or -1). You can select
|
||||
the second camera by passing 1 and so on. After that, you can capture frame-by-frame. But at the
|
||||
end, don't forget to release the capture.
|
||||
@@ -46,16 +46,16 @@ while True:
|
||||
# When everything done, release the capture
|
||||
cap.release()
|
||||
cv.destroyAllWindows()@endcode
|
||||
`cap.read()` returns a bool (`True`/`False`). If frame is read correctly, it will be `True`. So you can
|
||||
check end of the video by checking this return value.
|
||||
`cap.read()` returns a bool (`True`/`False`). If the frame is read correctly, it will be `True`. So you can
|
||||
check for the end of the video by checking this returned value.
|
||||
|
||||
Sometimes, cap may not have initialized the capture. In that case, this code shows error. You can
|
||||
Sometimes, cap may not have initialized the capture. In that case, this code shows an error. You can
|
||||
check whether it is initialized or not by the method **cap.isOpened()**. If it is `True`, OK.
|
||||
Otherwise open it using **cap.open()**.
|
||||
|
||||
You can also access some of the features of this video using **cap.get(propId)** method where propId
|
||||
is a number from 0 to 18. Each number denotes a property of the video (if it is applicable to that
|
||||
video) and full details can be seen here: cv::VideoCapture::get().
|
||||
video). Full details can be seen here: cv::VideoCapture::get().
|
||||
Some of these values can be modified using **cap.set(propId, value)**. Value is the new value you
|
||||
want.
|
||||
|
||||
@@ -63,13 +63,13 @@ For example, I can check the frame width and height by `cap.get(cv.CAP_PROP_FRAM
|
||||
640x480 by default. But I want to modify it to 320x240. Just use `ret = cap.set(cv.CAP_PROP_FRAME_WIDTH,320)` and
|
||||
`ret = cap.set(cv.CAP_PROP_FRAME_HEIGHT,240)`.
|
||||
|
||||
@note If you are getting error, make sure camera is working fine using any other camera application
|
||||
@note If you are getting an error, make sure your camera is working fine using any other camera application
|
||||
(like Cheese in Linux).
|
||||
|
||||
Playing Video from file
|
||||
-----------------------
|
||||
|
||||
It is same as capturing from Camera, just change camera index with video file name. Also while
|
||||
Playing video from file is the same as capturing it from camera, just change the camera index to a video file name. Also while
|
||||
displaying the frame, use appropriate time for `cv.waitKey()`. If it is too less, video will be very
|
||||
fast and if it is too high, video will be slow (Well, that is how you can display videos in slow
|
||||
motion). 25 milliseconds will be OK in normal cases.
|
||||
@@ -96,23 +96,23 @@ cap.release()
|
||||
cv.destroyAllWindows()
|
||||
@endcode
|
||||
|
||||
@note Make sure proper versions of ffmpeg or gstreamer is installed. Sometimes, it is a headache to
|
||||
work with Video Capture mostly due to wrong installation of ffmpeg/gstreamer.
|
||||
@note Make sure a proper version of ffmpeg or gstreamer is installed. Sometimes it is a headache to
|
||||
work with video capture, mostly due to wrong installation of ffmpeg/gstreamer.
|
||||
|
||||
Saving a Video
|
||||
--------------
|
||||
|
||||
So we capture a video, process it frame-by-frame and we want to save that video. For images, it is
|
||||
very simple, just use `cv.imwrite()`. Here a little more work is required.
|
||||
So we capture a video and process it frame-by-frame, and we want to save that video. For images, it is
|
||||
very simple: just use `cv.imwrite()`. Here, a little more work is required.
|
||||
|
||||
This time we create a **VideoWriter** object. We should specify the output file name (eg:
|
||||
output.avi). Then we should specify the **FourCC** code (details in next paragraph). Then number of
|
||||
frames per second (fps) and frame size should be passed. And last one is **isColor** flag. If it is
|
||||
`True`, encoder expect color frame, otherwise it works with grayscale frame.
|
||||
frames per second (fps) and frame size should be passed. And the last one is the **isColor** flag. If it is
|
||||
`True`, the encoder expect color frame, otherwise it works with grayscale frame.
|
||||
|
||||
[FourCC](http://en.wikipedia.org/wiki/FourCC) is a 4-byte code used to specify the video codec. The
|
||||
list of available codes can be found in [fourcc.org](http://www.fourcc.org/codecs.php). It is
|
||||
platform dependent. Following codecs works fine for me.
|
||||
platform dependent. The following codecs work fine for me.
|
||||
|
||||
- In Fedora: DIVX, XVID, MJPG, X264, WMV1, WMV2. (XVID is more preferable. MJPG results in high
|
||||
size video. X264 gives very small size video)
|
||||
@@ -122,7 +122,7 @@ platform dependent. Following codecs works fine for me.
|
||||
FourCC code is passed as `cv.VideoWriter_fourcc('M','J','P','G')` or
|
||||
`cv.VideoWriter_fourcc(*'MJPG')` for MJPG.
|
||||
|
||||
Below code capture from a Camera, flip every frame in vertical direction and saves it.
|
||||
The below code captures from a camera, flips every frame in the vertical direction, and saves the video.
|
||||
@code{.py}
|
||||
import numpy as np
|
||||
import cv2 as cv
|
||||
|
||||
@@ -5,44 +5,44 @@ Goal
|
||||
----
|
||||
|
||||
- In this tutorial, you will learn how to convert images from one color-space to another, like
|
||||
BGR \f$\leftrightarrow\f$ Gray, BGR \f$\leftrightarrow\f$ HSV etc.
|
||||
- In addition to that, we will create an application which extracts a colored object in a video
|
||||
- You will learn following functions : **cv.cvtColor()**, **cv.inRange()** etc.
|
||||
BGR \f$\leftrightarrow\f$ Gray, BGR \f$\leftrightarrow\f$ HSV, etc.
|
||||
- In addition to that, we will create an application to extract a colored object in a video
|
||||
- You will learn the following functions: **cv.cvtColor()**, **cv.inRange()**, etc.
|
||||
|
||||
Changing Color-space
|
||||
--------------------
|
||||
|
||||
There are more than 150 color-space conversion methods available in OpenCV. But we will look into
|
||||
only two which are most widely used ones, BGR \f$\leftrightarrow\f$ Gray and BGR \f$\leftrightarrow\f$ HSV.
|
||||
only two, which are most widely used ones: BGR \f$\leftrightarrow\f$ Gray and BGR \f$\leftrightarrow\f$ HSV.
|
||||
|
||||
For color conversion, we use the function cv.cvtColor(input_image, flag) where flag determines the
|
||||
type of conversion.
|
||||
|
||||
For BGR \f$\rightarrow\f$ Gray conversion we use the flags cv.COLOR_BGR2GRAY. Similarly for BGR
|
||||
For BGR \f$\rightarrow\f$ Gray conversion, we use the flag cv.COLOR_BGR2GRAY. Similarly for BGR
|
||||
\f$\rightarrow\f$ HSV, we use the flag cv.COLOR_BGR2HSV. To get other flags, just run following
|
||||
commands in your Python terminal :
|
||||
commands in your Python terminal:
|
||||
@code{.py}
|
||||
>>> import cv2 as cv
|
||||
>>> flags = [i for i in dir(cv) if i.startswith('COLOR_')]
|
||||
>>> print( flags )
|
||||
@endcode
|
||||
@note For HSV, Hue range is [0,179], Saturation range is [0,255] and Value range is [0,255].
|
||||
@note For HSV, hue range is [0,179], saturation range is [0,255], and value range is [0,255].
|
||||
Different software use different scales. So if you are comparing OpenCV values with them, you need
|
||||
to normalize these ranges.
|
||||
|
||||
Object Tracking
|
||||
---------------
|
||||
|
||||
Now we know how to convert BGR image to HSV, we can use this to extract a colored object. In HSV, it
|
||||
is more easier to represent a color than in BGR color-space. In our application, we will try to extract
|
||||
Now that we know how to convert a BGR image to HSV, we can use this to extract a colored object. In HSV, it
|
||||
is easier to represent a color than in BGR color-space. In our application, we will try to extract
|
||||
a blue colored object. So here is the method:
|
||||
|
||||
- Take each frame of the video
|
||||
- Convert from BGR to HSV color-space
|
||||
- We threshold the HSV image for a range of blue color
|
||||
- Now extract the blue object alone, we can do whatever on that image we want.
|
||||
- Now extract the blue object alone, we can do whatever we want on that image.
|
||||
|
||||
Below is the code which are commented in detail :
|
||||
Below is the code which is commented in detail:
|
||||
@code{.py}
|
||||
import cv2 as cv
|
||||
import numpy as np
|
||||
@@ -80,18 +80,18 @@ Below image shows tracking of the blue object:
|
||||
|
||||

|
||||
|
||||
@note There are some noises in the image. We will see how to remove them in later chapters.
|
||||
@note There is some noise in the image. We will see how to remove it in later chapters.
|
||||
|
||||
@note This is the simplest method in object tracking. Once you learn functions of contours, you can
|
||||
do plenty of things like find centroid of this object and use it to track the object, draw diagrams
|
||||
just by moving your hand in front of camera and many other funny stuffs.
|
||||
do plenty of things like find the centroid of an object and use it to track the object, draw diagrams
|
||||
just by moving your hand in front of a camera, and other fun stuff.
|
||||
|
||||
How to find HSV values to track?
|
||||
--------------------------------
|
||||
|
||||
This is a common question found in [stackoverflow.com](http://www.stackoverflow.com). It is very simple and
|
||||
you can use the same function, cv.cvtColor(). Instead of passing an image, you just pass the BGR
|
||||
values you want. For example, to find the HSV value of Green, try following commands in Python
|
||||
values you want. For example, to find the HSV value of Green, try the following commands in a Python
|
||||
terminal:
|
||||
@code{.py}
|
||||
>>> green = np.uint8([[[0,255,0 ]]])
|
||||
@@ -99,7 +99,7 @@ terminal:
|
||||
>>> print( hsv_green )
|
||||
[[[ 60 255 255]]]
|
||||
@endcode
|
||||
Now you take [H-10, 100,100] and [H+10, 255, 255] as lower bound and upper bound respectively. Apart
|
||||
Now you take [H-10, 100,100] and [H+10, 255, 255] as the lower bound and upper bound respectively. Apart
|
||||
from this method, you can use any image editing tools like GIMP or any online converters to find
|
||||
these values, but don't forget to adjust the HSV ranges.
|
||||
|
||||
@@ -109,5 +109,5 @@ Additional Resources
|
||||
Exercises
|
||||
---------
|
||||
|
||||
-# Try to find a way to extract more than one colored objects, for eg, extract red, blue, green
|
||||
objects simultaneously.
|
||||
-# Try to find a way to extract more than one colored object, for example, extract red, blue, and green
|
||||
objects simultaneously.
|
||||
|
||||
@@ -5,24 +5,24 @@ Goals
|
||||
-----
|
||||
|
||||
Learn to:
|
||||
- Blur the images with various low pass filters
|
||||
- Blur images with various low pass filters
|
||||
- Apply custom-made filters to images (2D convolution)
|
||||
|
||||
2D Convolution ( Image Filtering )
|
||||
----------------------------------
|
||||
|
||||
As in one-dimensional signals, images also can be filtered with various low-pass filters(LPF),
|
||||
high-pass filters(HPF) etc. LPF helps in removing noises, blurring the images etc. HPF filters helps
|
||||
in finding edges in the images.
|
||||
As in one-dimensional signals, images also can be filtered with various low-pass filters (LPF),
|
||||
high-pass filters (HPF), etc. LPF helps in removing noise, blurring images, etc. HPF filters help
|
||||
in finding edges in images.
|
||||
|
||||
OpenCV provides a function **cv.filter2D()** to convolve a kernel with an image. As an example, we
|
||||
will try an averaging filter on an image. A 5x5 averaging filter kernel will look like below:
|
||||
will try an averaging filter on an image. A 5x5 averaging filter kernel will look like the below:
|
||||
|
||||
\f[K = \frac{1}{25} \begin{bmatrix} 1 & 1 & 1 & 1 & 1 \\ 1 & 1 & 1 & 1 & 1 \\ 1 & 1 & 1 & 1 & 1 \\ 1 & 1 & 1 & 1 & 1 \\ 1 & 1 & 1 & 1 & 1 \end{bmatrix}\f]
|
||||
|
||||
Operation is like this: keep this kernel above a pixel, add all the 25 pixels below this kernel,
|
||||
take its average and replace the central pixel with the new average value. It continues this
|
||||
operation for all the pixels in the image. Try this code and check the result:
|
||||
The operation works like this: keep this kernel above a pixel, add all the 25 pixels below this kernel,
|
||||
take the average, and replace the central pixel with the new average value. This operation is continued
|
||||
for all the pixels in the image. Try this code and check the result:
|
||||
@code{.py}
|
||||
import numpy as np
|
||||
import cv2 as cv
|
||||
@@ -47,20 +47,20 @@ Image Blurring (Image Smoothing)
|
||||
--------------------------------
|
||||
|
||||
Image blurring is achieved by convolving the image with a low-pass filter kernel. It is useful for
|
||||
removing noises. It actually removes high frequency content (eg: noise, edges) from the image. So
|
||||
edges are blurred a little bit in this operation. (Well, there are blurring techniques which doesn't
|
||||
blur the edges too). OpenCV provides mainly four types of blurring techniques.
|
||||
removing noise. It actually removes high frequency content (eg: noise, edges) from the image. So
|
||||
edges are blurred a little bit in this operation (there are also blurring techniques which don't
|
||||
blur the edges). OpenCV provides four main types of blurring techniques.
|
||||
|
||||
### 1. Averaging
|
||||
|
||||
This is done by convolving image with a normalized box filter. It simply takes the average of all
|
||||
the pixels under kernel area and replace the central element. This is done by the function
|
||||
This is done by convolving an image with a normalized box filter. It simply takes the average of all
|
||||
the pixels under the kernel area and replaces the central element. This is done by the function
|
||||
**cv.blur()** or **cv.boxFilter()**. Check the docs for more details about the kernel. We should
|
||||
specify the width and height of kernel. A 3x3 normalized box filter would look like below:
|
||||
specify the width and height of the kernel. A 3x3 normalized box filter would look like the below:
|
||||
|
||||
\f[K = \frac{1}{9} \begin{bmatrix} 1 & 1 & 1 \\ 1 & 1 & 1 \\ 1 & 1 & 1 \end{bmatrix}\f]
|
||||
|
||||
@note If you don't want to use normalized box filter, use **cv.boxFilter()**. Pass an argument
|
||||
@note If you don't want to use a normalized box filter, use **cv.boxFilter()**. Pass an argument
|
||||
normalize=False to the function.
|
||||
|
||||
Check a sample demo below with a kernel of 5x5 size:
|
||||
@@ -85,12 +85,12 @@ Result:
|
||||
|
||||
### 2. Gaussian Blurring
|
||||
|
||||
In this, instead of box filter, gaussian kernel is used. It is done with the function,
|
||||
**cv.GaussianBlur()**. We should specify the width and height of kernel which should be positive
|
||||
and odd. We also should specify the standard deviation in X and Y direction, sigmaX and sigmaY
|
||||
respectively. If only sigmaX is specified, sigmaY is taken as same as sigmaX. If both are given as
|
||||
zeros, they are calculated from kernel size. Gaussian blurring is highly effective in removing
|
||||
gaussian noise from the image.
|
||||
In this method, instead of a box filter, a Gaussian kernel is used. It is done with the function,
|
||||
**cv.GaussianBlur()**. We should specify the width and height of the kernel which should be positive
|
||||
and odd. We also should specify the standard deviation in the X and Y directions, sigmaX and sigmaY
|
||||
respectively. If only sigmaX is specified, sigmaY is taken as the same as sigmaX. If both are given as
|
||||
zeros, they are calculated from the kernel size. Gaussian blurring is highly effective in removing
|
||||
Gaussian noise from an image.
|
||||
|
||||
If you want, you can create a Gaussian kernel with the function, **cv.getGaussianKernel()**.
|
||||
|
||||
@@ -104,14 +104,14 @@ Result:
|
||||
|
||||
### 3. Median Blurring
|
||||
|
||||
Here, the function **cv.medianBlur()** takes median of all the pixels under kernel area and central
|
||||
Here, the function **cv.medianBlur()** takes the median of all the pixels under the kernel area and the central
|
||||
element is replaced with this median value. This is highly effective against salt-and-pepper noise
|
||||
in the images. Interesting thing is that, in the above filters, central element is a newly
|
||||
in an image. Interestingly, in the above filters, the central element is a newly
|
||||
calculated value which may be a pixel value in the image or a new value. But in median blurring,
|
||||
central element is always replaced by some pixel value in the image. It reduces the noise
|
||||
the central element is always replaced by some pixel value in the image. It reduces the noise
|
||||
effectively. Its kernel size should be a positive odd integer.
|
||||
|
||||
In this demo, I added a 50% noise to our original image and applied median blur. Check the result:
|
||||
In this demo, I added a 50% noise to our original image and applied median blurring. Check the result:
|
||||
@code{.py}
|
||||
median = cv.medianBlur(img,5)
|
||||
@endcode
|
||||
@@ -122,19 +122,19 @@ Result:
|
||||
### 4. Bilateral Filtering
|
||||
|
||||
**cv.bilateralFilter()** is highly effective in noise removal while keeping edges sharp. But the
|
||||
operation is slower compared to other filters. We already saw that gaussian filter takes the a
|
||||
neighbourhood around the pixel and find its gaussian weighted average. This gaussian filter is a
|
||||
operation is slower compared to other filters. We already saw that a Gaussian filter takes the
|
||||
neighbourhood around the pixel and finds its Gaussian weighted average. This Gaussian filter is a
|
||||
function of space alone, that is, nearby pixels are considered while filtering. It doesn't consider
|
||||
whether pixels have almost same intensity. It doesn't consider whether pixel is an edge pixel or
|
||||
whether pixels have almost the same intensity. It doesn't consider whether a pixel is an edge pixel or
|
||||
not. So it blurs the edges also, which we don't want to do.
|
||||
|
||||
Bilateral filter also takes a gaussian filter in space, but one more gaussian filter which is a
|
||||
function of pixel difference. Gaussian function of space make sure only nearby pixels are considered
|
||||
for blurring while gaussian function of intensity difference make sure only those pixels with
|
||||
similar intensity to central pixel is considered for blurring. So it preserves the edges since
|
||||
Bilateral filtering also takes a Gaussian filter in space, but one more Gaussian filter which is a
|
||||
function of pixel difference. The Gaussian function of space makes sure that only nearby pixels are considered
|
||||
for blurring, while the Gaussian function of intensity difference makes sure that only those pixels with
|
||||
similar intensities to the central pixel are considered for blurring. So it preserves the edges since
|
||||
pixels at edges will have large intensity variation.
|
||||
|
||||
Below samples shows use bilateral filter (For details on arguments, visit docs).
|
||||
The below sample shows use of a bilateral filter (For details on arguments, visit docs).
|
||||
@code{.py}
|
||||
blur = cv.bilateralFilter(img,9,75,75)
|
||||
@endcode
|
||||
@@ -142,7 +142,7 @@ Result:
|
||||
|
||||

|
||||
|
||||
See, the texture on the surface is gone, but edges are still preserved.
|
||||
See, the texture on the surface is gone, but the edges are still preserved.
|
||||
|
||||
Additional Resources
|
||||
--------------------
|
||||
|
||||
+14
-14
@@ -4,7 +4,7 @@ Geometric Transformations of Images {#tutorial_py_geometric_transformations}
|
||||
Goals
|
||||
-----
|
||||
|
||||
- Learn to apply different geometric transformation to images like translation, rotation, affine
|
||||
- Learn to apply different geometric transformations to images, like translation, rotation, affine
|
||||
transformation etc.
|
||||
- You will see these functions: **cv.getPerspectiveTransform**
|
||||
|
||||
@@ -12,7 +12,7 @@ Transformations
|
||||
---------------
|
||||
|
||||
OpenCV provides two transformation functions, **cv.warpAffine** and **cv.warpPerspective**, with
|
||||
which you can have all kinds of transformations. **cv.warpAffine** takes a 2x3 transformation
|
||||
which you can perform all kinds of transformations. **cv.warpAffine** takes a 2x3 transformation
|
||||
matrix while **cv.warpPerspective** takes a 3x3 transformation matrix as input.
|
||||
|
||||
### Scaling
|
||||
@@ -21,8 +21,8 @@ Scaling is just resizing of the image. OpenCV comes with a function **cv.resize(
|
||||
purpose. The size of the image can be specified manually, or you can specify the scaling factor.
|
||||
Different interpolation methods are used. Preferable interpolation methods are **cv.INTER_AREA**
|
||||
for shrinking and **cv.INTER_CUBIC** (slow) & **cv.INTER_LINEAR** for zooming. By default,
|
||||
interpolation method used is **cv.INTER_LINEAR** for all resizing purposes. You can resize an
|
||||
input image either of following methods:
|
||||
the interpolation method **cv.INTER_LINEAR** is used for all resizing purposes. You can resize an
|
||||
input image with either of following methods:
|
||||
@code{.py}
|
||||
import numpy as np
|
||||
import cv2 as cv
|
||||
@@ -38,13 +38,13 @@ res = cv.resize(img,(2*width, 2*height), interpolation = cv.INTER_CUBIC)
|
||||
@endcode
|
||||
### Translation
|
||||
|
||||
Translation is the shifting of object's location. If you know the shift in (x,y) direction, let it
|
||||
Translation is the shifting of an object's location. If you know the shift in the (x,y) direction and let it
|
||||
be \f$(t_x,t_y)\f$, you can create the transformation matrix \f$\textbf{M}\f$ as follows:
|
||||
|
||||
\f[M = \begin{bmatrix} 1 & 0 & t_x \\ 0 & 1 & t_y \end{bmatrix}\f]
|
||||
|
||||
You can take make it into a Numpy array of type np.float32 and pass it into **cv.warpAffine()**
|
||||
function. See below example for a shift of (100,50):
|
||||
You can take make it into a Numpy array of type np.float32 and pass it into the **cv.warpAffine()**
|
||||
function. See the below example for a shift of (100,50):
|
||||
@code{.py}
|
||||
import numpy as np
|
||||
import cv2 as cv
|
||||
@@ -61,7 +61,7 @@ cv.destroyAllWindows()
|
||||
@endcode
|
||||
**warning**
|
||||
|
||||
Third argument of the **cv.warpAffine()** function is the size of the output image, which should
|
||||
The third argument of the **cv.warpAffine()** function is the size of the output image, which should
|
||||
be in the form of **(width, height)**. Remember width = number of columns, and height = number of
|
||||
rows.
|
||||
|
||||
@@ -76,7 +76,7 @@ Rotation of an image for an angle \f$\theta\f$ is achieved by the transformation
|
||||
\f[M = \begin{bmatrix} cos\theta & -sin\theta \\ sin\theta & cos\theta \end{bmatrix}\f]
|
||||
|
||||
But OpenCV provides scaled rotation with adjustable center of rotation so that you can rotate at any
|
||||
location you prefer. Modified transformation matrix is given by
|
||||
location you prefer. The modified transformation matrix is given by
|
||||
|
||||
\f[\begin{bmatrix} \alpha & \beta & (1- \alpha ) \cdot center.x - \beta \cdot center.y \\ - \beta & \alpha & \beta \cdot center.x + (1- \alpha ) \cdot center.y \end{bmatrix}\f]
|
||||
|
||||
@@ -84,7 +84,7 @@ where:
|
||||
|
||||
\f[\begin{array}{l} \alpha = scale \cdot \cos \theta , \\ \beta = scale \cdot \sin \theta \end{array}\f]
|
||||
|
||||
To find this transformation matrix, OpenCV provides a function, **cv.getRotationMatrix2D**. Check
|
||||
To find this transformation matrix, OpenCV provides a function, **cv.getRotationMatrix2D**. Check out the
|
||||
below example which rotates the image by 90 degree with respect to center without any scaling.
|
||||
@code{.py}
|
||||
img = cv.imread('messi5.jpg',0)
|
||||
@@ -101,11 +101,11 @@ See the result:
|
||||
### Affine Transformation
|
||||
|
||||
In affine transformation, all parallel lines in the original image will still be parallel in the
|
||||
output image. To find the transformation matrix, we need three points from input image and their
|
||||
corresponding locations in output image. Then **cv.getAffineTransform** will create a 2x3 matrix
|
||||
output image. To find the transformation matrix, we need three points from the input image and their
|
||||
corresponding locations in the output image. Then **cv.getAffineTransform** will create a 2x3 matrix
|
||||
which is to be passed to **cv.warpAffine**.
|
||||
|
||||
Check below example, and also look at the points I selected (which are marked in Green color):
|
||||
Check the below example, and also look at the points I selected (which are marked in green color):
|
||||
@code{.py}
|
||||
img = cv.imread('drawing.png')
|
||||
rows,cols,ch = img.shape
|
||||
@@ -130,7 +130,7 @@ See the result:
|
||||
For perspective transformation, you need a 3x3 transformation matrix. Straight lines will remain
|
||||
straight even after the transformation. To find this transformation matrix, you need 4 points on the
|
||||
input image and corresponding points on the output image. Among these 4 points, 3 of them should not
|
||||
be collinear. Then transformation matrix can be found by the function
|
||||
be collinear. Then the transformation matrix can be found by the function
|
||||
**cv.getPerspectiveTransform**. Then apply **cv.warpPerspective** with this 3x3 transformation
|
||||
matrix.
|
||||
|
||||
|
||||
@@ -4,13 +4,13 @@ Image Thresholding {#tutorial_py_thresholding}
|
||||
Goal
|
||||
----
|
||||
|
||||
- In this tutorial, you will learn Simple thresholding, Adaptive thresholding and Otsu's thresholding.
|
||||
- In this tutorial, you will learn simple thresholding, adaptive thresholding and Otsu's thresholding.
|
||||
- You will learn the functions **cv.threshold** and **cv.adaptiveThreshold**.
|
||||
|
||||
Simple Thresholding
|
||||
-------------------
|
||||
|
||||
Here, the matter is straight forward. For every pixel, the same threshold value is applied.
|
||||
Here, the matter is straight-forward. For every pixel, the same threshold value is applied.
|
||||
If the pixel value is smaller than the threshold, it is set to 0, otherwise it is set to a maximum value.
|
||||
The function **cv.threshold** is used to apply the thresholding.
|
||||
The first argument is the source image, which **should be a grayscale image**.
|
||||
@@ -65,11 +65,11 @@ Adaptive Thresholding
|
||||
|
||||
In the previous section, we used one global value as a threshold.
|
||||
But this might not be good in all cases, e.g. if an image has different lighting conditions in different areas.
|
||||
In that case, adaptive thresholding thresholding can help.
|
||||
In that case, adaptive thresholding can help.
|
||||
Here, the algorithm determines the threshold for a pixel based on a small region around it.
|
||||
So we get different thresholds for different regions of the same image which gives better results for images with varying illumination.
|
||||
|
||||
Additionally to the parameters described above, the method cv.adaptiveThreshold three input parameters:
|
||||
In addition to the parameters described above, the method cv.adaptiveThreshold takes three input parameters:
|
||||
|
||||
The **adaptiveMethod** decides how the threshold value is calculated:
|
||||
- cv.ADAPTIVE_THRESH_MEAN_C: The threshold value is the mean of the neighbourhood area minus the constant **C**.
|
||||
@@ -168,8 +168,8 @@ Result:
|
||||
|
||||
### How does Otsu's Binarization work?
|
||||
|
||||
This section demonstrates a Python implementation of Otsu's binarization to show how it works
|
||||
actually. If you are not interested, you can skip this.
|
||||
This section demonstrates a Python implementation of Otsu's binarization to show how it actually
|
||||
works. If you are not interested, you can skip this.
|
||||
|
||||
Since we are working with bimodal images, Otsu's algorithm tries to find a threshold value (t) which
|
||||
minimizes the **weighted within-class variance** given by the relation:
|
||||
|
||||
@@ -54,7 +54,7 @@ print( accuracy )
|
||||
@endcode
|
||||
So our basic OCR app is ready. This particular example gave me an accuracy of 91%. One option
|
||||
improve accuracy is to add more data for training, especially the wrong ones. So instead of finding
|
||||
this training data everytime I start application, I better save it, so that next time, I directly
|
||||
this training data every time I start application, I better save it, so that next time, I directly
|
||||
read this data from a file and start classification. You can do it with the help of some Numpy
|
||||
functions like np.savetxt, np.savez, np.load etc. Please check their docs for more details.
|
||||
@code{.py}
|
||||
|
||||
@@ -150,7 +150,7 @@ We observe that @ref cv::Mat::zeros returns a Matlab-style zero initializer base
|
||||
|
||||
Notice the following (**C++ code only**):
|
||||
- To access each pixel in the images we are using this syntax: *image.at\<Vec3b\>(y,x)[c]*
|
||||
where *y* is the row, *x* is the column and *c* is R, G or B (0, 1 or 2).
|
||||
where *y* is the row, *x* is the column and *c* is B, G or R (0, 1 or 2).
|
||||
- Since the operation \f$\alpha \cdot p(i,j) + \beta\f$ can give values out of range or not
|
||||
integers (if \f$\alpha\f$ is float), we use cv::saturate_cast to make sure the
|
||||
values are valid.
|
||||
@@ -220,12 +220,12 @@ gamma correction.
|
||||
### Brightness and contrast adjustments
|
||||
|
||||
Increasing (/ decreasing) the \f$\beta\f$ value will add (/ subtract) a constant value to every pixel. Pixel values outside of the [0 ; 255]
|
||||
range will be saturated (i.e. a pixel value higher (/ lesser) than 255 (/ 0) will be clamp to 255 (/ 0)).
|
||||
range will be saturated (i.e. a pixel value higher (/ lesser) than 255 (/ 0) will be clamped to 255 (/ 0)).
|
||||
|
||||

|
||||
|
||||
The histogram represents for each color level the number of pixels with that color level. A dark image will have many pixels with
|
||||
low color value and thus the histogram will present a peak in his left part. When adding a constant bias, the histogram is shifted to the
|
||||
low color value and thus the histogram will present a peak in its left part. When adding a constant bias, the histogram is shifted to the
|
||||
right as we have added a constant bias to all the pixels.
|
||||
|
||||
The \f$\alpha\f$ parameter will modify how the levels spread. If \f$ \alpha < 1 \f$, the color levels will be compressed and the result
|
||||
|
||||
@@ -10,7 +10,7 @@ Goal
|
||||
We'll seek answers for the following questions:
|
||||
|
||||
- How to go through each and every pixel of an image?
|
||||
- How is OpenCV matrix values stored?
|
||||
- How are OpenCV matrix values stored?
|
||||
- How to measure the performance of our algorithm?
|
||||
- What are lookup tables and why use them?
|
||||
|
||||
@@ -45,13 +45,13 @@ operation. In case of the *uchar* system this is 256 to be exact.
|
||||
Therefore, for larger images it would be wise to calculate all possible values beforehand and during
|
||||
the assignment just make the assignment, by using a lookup table. Lookup tables are simple arrays
|
||||
(having one or more dimensions) that for a given input value variation holds the final output value.
|
||||
Its strength lies that we do not need to make the calculation, we just need to read the result.
|
||||
Its strength is that we do not need to make the calculation, we just need to read the result.
|
||||
|
||||
Our test case program (and the sample presented here) will do the following: read in a console line
|
||||
argument image (that may be either color or gray scale - console line argument too) and apply the
|
||||
reduction with the given console line argument integer value. In OpenCV, at the moment there are
|
||||
Our test case program (and the code sample below) will do the following: read in an image passed
|
||||
as a command line argument (it may be either color or grayscale) and apply the reduction
|
||||
with the given command line argument integer value. In OpenCV, at the moment there are
|
||||
three major ways of going through an image pixel by pixel. To make things a little more interesting
|
||||
will make the scanning for each image using all of these methods, and print out how long it took.
|
||||
we'll make the scanning of the image using each of these methods, and print out how long it took.
|
||||
|
||||
You can download the full source code [here
|
||||
](https://github.com/opencv/opencv/tree/master/samples/cpp/tutorial_code/core/how_to_scan_images/how_to_scan_images.cpp) or look it up in
|
||||
@@ -59,7 +59,7 @@ the samples directory of OpenCV at the cpp tutorial code for the core section. I
|
||||
@code{.bash}
|
||||
how_to_scan_images imageName.jpg intValueToReduce [G]
|
||||
@endcode
|
||||
The final argument is optional. If given the image will be loaded in gray scale format, otherwise
|
||||
The final argument is optional. If given the image will be loaded in grayscale format, otherwise
|
||||
the BGR color space is used. The first thing is to calculate the lookup table.
|
||||
|
||||
@snippet how_to_scan_images.cpp dividewith
|
||||
@@ -71,8 +71,8 @@ No OpenCV specific stuff here.
|
||||
Another issue is how do we measure time? Well OpenCV offers two simple functions to achieve this
|
||||
cv::getTickCount() and cv::getTickFrequency() . The first returns the number of ticks of
|
||||
your systems CPU from a certain event (like since you booted your system). The second returns how
|
||||
many times your CPU emits a tick during a second. So to measure in seconds the number of time
|
||||
elapsed between two operations is easy as:
|
||||
many times your CPU emits a tick during a second. So, measuring amount of time elapsed between
|
||||
two operations is as easy as:
|
||||
@code{.cpp}
|
||||
double t = (double)getTickCount();
|
||||
// do something ...
|
||||
@@ -85,8 +85,8 @@ How is the image matrix stored in memory?
|
||||
-----------------------------------------
|
||||
|
||||
As you could already read in my @ref tutorial_mat_the_basic_image_container tutorial the size of the matrix
|
||||
depends on the color system used. More accurately, it depends from the number of channels used. In
|
||||
case of a gray scale image we have something like:
|
||||
depends on the color system used. More accurately, it depends on the number of channels used. In
|
||||
case of a grayscale image we have something like:
|
||||
|
||||

|
||||
|
||||
@@ -117,12 +117,12 @@ three channels so we need to pass through three times more items in each row.
|
||||
There's another way of this. The *data* data member of a *Mat* object returns the pointer to the
|
||||
first row, first column. If this pointer is null you have no valid input in that object. Checking
|
||||
this is the simplest method to check if your image loading was a success. In case the storage is
|
||||
continuous we can use this to go through the whole data pointer. In case of a gray scale image this
|
||||
continuous we can use this to go through the whole data pointer. In case of a grayscale image this
|
||||
would look like:
|
||||
@code{.cpp}
|
||||
uchar* p = I.data;
|
||||
|
||||
for( unsigned int i =0; i < ncol*nrows; ++i)
|
||||
for( unsigned int i = 0; i < ncol*nrows; ++i)
|
||||
*p++ = table[*p];
|
||||
@endcode
|
||||
You would get the same result. However, this code is a lot harder to read later on. It gets even
|
||||
@@ -135,7 +135,7 @@ The iterator (safe) method
|
||||
|
||||
In case of the efficient way making sure that you pass through the right amount of *uchar* fields
|
||||
and to skip the gaps that may occur between the rows was your responsibility. The iterator method is
|
||||
considered a safer way as it takes over these tasks from the user. All you need to do is ask the
|
||||
considered a safer way as it takes over these tasks from the user. All you need to do is to ask the
|
||||
begin and the end of the image matrix and then just increase the begin iterator until you reach the
|
||||
end. To acquire the value *pointed* by the iterator use the \* operator (add it before it).
|
||||
|
||||
@@ -152,17 +152,17 @@ On-the-fly address calculation with reference returning
|
||||
|
||||
The final method isn't recommended for scanning. It was made to acquire or modify somehow random
|
||||
elements in the image. Its basic usage is to specify the row and column number of the item you want
|
||||
to access. During our earlier scanning methods you could already observe that is important through
|
||||
to access. During our earlier scanning methods you could already notice that it is important through
|
||||
what type we are looking at the image. It's no different here as you need to manually specify what
|
||||
type to use at the automatic lookup. You can observe this in case of the gray scale images for the
|
||||
type to use at the automatic lookup. You can observe this in case of the grayscale images for the
|
||||
following source code (the usage of the + cv::Mat::at() function):
|
||||
|
||||
@snippet how_to_scan_images.cpp scan-random
|
||||
|
||||
The functions takes your input type and coordinates and calculates on the fly the address of the
|
||||
The function takes your input type and coordinates and calculates the address of the
|
||||
queried item. Then returns a reference to that. This may be a constant when you *get* the value and
|
||||
non-constant when you *set* the value. As a safety step in **debug mode only**\* there is performed
|
||||
a check that your input coordinates are valid and does exist. If this isn't the case you'll get a
|
||||
non-constant when you *set* the value. As a safety step in **debug mode only**\* there is a check
|
||||
performed that your input coordinates are valid and do exist. If this isn't the case you'll get a
|
||||
nice output message of this on the standard error output stream. Compared to the efficient way in
|
||||
release mode the only difference in using this is that for every element of the image you'll get a
|
||||
new row pointer for what we use the C operator[] to acquire the column element.
|
||||
@@ -173,7 +173,7 @@ OpenCV has a cv::Mat_ data type. It's the same as Mat with the extra need that a
|
||||
you need to specify the data type through what to look at the data matrix, however in return you can
|
||||
use the operator() for fast access of items. To make things even better this is easily convertible
|
||||
from and to the usual cv::Mat data type. A sample usage of this you can see in case of the
|
||||
color images of the upper function. Nevertheless, it's important to note that the same operation
|
||||
color images of the function above. Nevertheless, it's important to note that the same operation
|
||||
(with the same runtime speed) could have been done with the cv::Mat::at function. It's just a less
|
||||
to write for the lazy programmer trick.
|
||||
|
||||
@@ -195,7 +195,7 @@ Finally call the function (I is our input image and J the output one):
|
||||
Performance Difference
|
||||
----------------------
|
||||
|
||||
For the best result compile the program and run it on your own speed. To make the differences more
|
||||
For the best result compile the program and run it yourself. To make the differences more
|
||||
clear, I've used a quite large (2560 X 1600) image. The performance presented here are for
|
||||
color images. For a more accurate value I've averaged the value I got from the call of the function
|
||||
for hundred times.
|
||||
|
||||
@@ -4,7 +4,7 @@ Mask operations on matrices {#tutorial_mat_mask_operations}
|
||||
@prev_tutorial{tutorial_how_to_scan_images}
|
||||
@next_tutorial{tutorial_mat_operations}
|
||||
|
||||
Mask operations on matrices are quite simple. The idea is that we recalculate each pixels value in
|
||||
Mask operations on matrices are quite simple. The idea is that we recalculate each pixel's value in
|
||||
an image according to a mask matrix (also known as kernel). This mask holds values that will adjust
|
||||
how much influence neighboring pixels (and the current pixel) have on the new pixel value. From a
|
||||
mathematical point of view we make a weighted average, with our specified values.
|
||||
@@ -12,7 +12,7 @@ mathematical point of view we make a weighted average, with our specified values
|
||||
Our test case
|
||||
-------------
|
||||
|
||||
Let us consider the issue of an image contrast enhancement method. Basically we want to apply for
|
||||
Let's consider the issue of an image contrast enhancement method. Basically we want to apply for
|
||||
every pixel of the image the following formula:
|
||||
|
||||
\f[I(i,j) = 5*I(i,j) - [ I(i-1,j) + I(i+1,j) + I(i,j-1) + I(i,j+1)]\f]\f[\iff I(i,j)*M, \text{where }
|
||||
@@ -144,7 +144,7 @@ Then we apply the sum and put the new value in the Result matrix.
|
||||
The filter2D function
|
||||
---------------------
|
||||
|
||||
Applying such filters are so common in image processing that in OpenCV there exist a function that
|
||||
Applying such filters are so common in image processing that in OpenCV there is a function that
|
||||
will take care of applying the mask (also called a kernel in some places). For this you first need
|
||||
to define an object that holds the mask:
|
||||
|
||||
|
||||
+20
-20
@@ -61,7 +61,7 @@ The last thing we want to do is further decrease the speed of your program by ma
|
||||
copies of potentially *large* images.
|
||||
|
||||
To tackle this issue OpenCV uses a reference counting system. The idea is that each *Mat* object has
|
||||
its own header, however the matrix may be shared between two instance of them by having their matrix
|
||||
its own header, however a matrix may be shared between two *Mat* objects by having their matrix
|
||||
pointers point to the same address. Moreover, the copy operators **will only copy the headers** and
|
||||
the pointer to the large matrix, not the data itself.
|
||||
|
||||
@@ -74,32 +74,32 @@ Mat B(A); // Use the copy constructor
|
||||
C = A; // Assignment operator
|
||||
@endcode
|
||||
|
||||
All the above objects, in the end, point to the same single data matrix. Their headers are
|
||||
different, however, and making a modification using any of them will affect all the other ones as
|
||||
well. In practice the different objects just provide different access method to the same underlying
|
||||
data. Nevertheless, their header parts are different. The real interesting part is that you can
|
||||
create headers which refer to only a subsection of the full data. For example, to create a region of
|
||||
interest (*ROI*) in an image you just create a new header with the new boundaries:
|
||||
All the above objects, in the end, point to the same single data matrix and making a modification
|
||||
using any of them will affect all the other ones as well. In practice the different objects just
|
||||
provide different access methods to the same underlying data. Nevertheless, their header parts are
|
||||
different. The real interesting part is that you can create headers which refer to only a subsection
|
||||
of the full data. For example, to create a region of interest (*ROI*) in an image you just create
|
||||
a new header with the new boundaries:
|
||||
@code{.cpp}
|
||||
Mat D (A, Rect(10, 10, 100, 100) ); // using a rectangle
|
||||
Mat E = A(Range::all(), Range(1,3)); // using row and column boundaries
|
||||
@endcode
|
||||
Now you may ask if the matrix itself may belong to multiple *Mat* objects who takes responsibility
|
||||
Now you may ask -- if the matrix itself may belong to multiple *Mat* objects who takes responsibility
|
||||
for cleaning it up when it's no longer needed. The short answer is: the last object that used it.
|
||||
This is handled by using a reference counting mechanism. Whenever somebody copies a header of a
|
||||
*Mat* object, a counter is increased for the matrix. Whenever a header is cleaned this counter is
|
||||
decreased. When the counter reaches zero the matrix too is freed. Sometimes you will want to copy
|
||||
the matrix itself too, so OpenCV provides the @ref cv::Mat::clone() and @ref cv::Mat::copyTo() functions.
|
||||
*Mat* object, a counter is increased for the matrix. Whenever a header is cleaned, this counter
|
||||
is decreased. When the counter reaches zero the matrix is freed. Sometimes you will want to copy
|
||||
the matrix itself too, so OpenCV provides @ref cv::Mat::clone() and @ref cv::Mat::copyTo() functions.
|
||||
@code{.cpp}
|
||||
Mat F = A.clone();
|
||||
Mat G;
|
||||
A.copyTo(G);
|
||||
@endcode
|
||||
Now modifying *F* or *G* will not affect the matrix pointed by the *Mat* header. What you need to
|
||||
Now modifying *F* or *G* will not affect the matrix pointed by the *A*'s header. What you need to
|
||||
remember from all this is that:
|
||||
|
||||
- Output image allocation for OpenCV functions is automatic (unless specified otherwise).
|
||||
- You do not need to think about memory management with OpenCVs C++ interface.
|
||||
- You do not need to think about memory management with OpenCV's C++ interface.
|
||||
- The assignment operator and the copy constructor only copies the header.
|
||||
- The underlying matrix of an image may be copied using the @ref cv::Mat::clone() and @ref cv::Mat::copyTo()
|
||||
functions.
|
||||
@@ -109,7 +109,7 @@ Storing methods
|
||||
|
||||
This is about how you store the pixel values. You can select the color space and the data type used.
|
||||
The color space refers to how we combine color components in order to code a given color. The
|
||||
simplest one is the gray scale where the colors at our disposal are black and white. The combination
|
||||
simplest one is the grayscale where the colors at our disposal are black and white. The combination
|
||||
of these allows us to create many shades of gray.
|
||||
|
||||
For *colorful* ways we have a lot more methods to choose from. Each of them breaks it down to three
|
||||
@@ -121,15 +121,15 @@ added.
|
||||
There are, however, many other color systems each with their own advantages:
|
||||
|
||||
- RGB is the most common as our eyes use something similar, however keep in mind that OpenCV standard display
|
||||
system composes colors using the BGR color space (a switch of the red and blue channel).
|
||||
system composes colors using the BGR color space (red and blue channels are swapped places).
|
||||
- The HSV and HLS decompose colors into their hue, saturation and value/luminance components,
|
||||
which is a more natural way for us to describe colors. You might, for example, dismiss the last
|
||||
component, making your algorithm less sensible to the light conditions of the input image.
|
||||
- YCrCb is used by the popular JPEG image format.
|
||||
- CIE L\*a\*b\* is a perceptually uniform color space, which comes handy if you need to measure
|
||||
- CIE L\*a\*b\* is a perceptually uniform color space, which comes in handy if you need to measure
|
||||
the *distance* of a given color to another color.
|
||||
|
||||
Each of the building components has their own valid domains. This leads to the data type used. How
|
||||
Each of the building components has its own valid domains. This leads to the data type used. How
|
||||
we store a component defines the control we have over its domain. The smallest data type possible is
|
||||
*char*, which means one byte or 8 bits. This may be unsigned (so can store values from 0 to 255) or
|
||||
signed (values from -127 to +127). Although in case of three components this already gives 16
|
||||
@@ -165,8 +165,8 @@ object in multiple ways:
|
||||
CV_[The number of bits per item][Signed or Unsigned][Type Prefix]C[The channel number]
|
||||
@endcode
|
||||
For instance, *CV_8UC3* means we use unsigned char types that are 8 bit long and each pixel has
|
||||
three of these to form the three channels. This are predefined for up to four channel numbers. The
|
||||
@ref cv::Scalar is four element short vector. Specify this and you can initialize all matrix
|
||||
three of these to form the three channels. There are types predefined for up to four channels. The
|
||||
@ref cv::Scalar is four element short vector. Specify it and you can initialize all matrix
|
||||
points with a custom value. If you need more you can create the type with the upper macro, setting
|
||||
the channel number in parenthesis as you can see below.
|
||||
|
||||
@@ -210,7 +210,7 @@ object in multiple ways:
|
||||
|
||||
@note
|
||||
You can fill out a matrix with random values using the @ref cv::randu() function. You need to
|
||||
give the lower and upper value for the random values:
|
||||
give a lower and upper limit for the random values:
|
||||
@snippet mat_the_basic_image_container.cpp random
|
||||
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@ Tutorial was written for the following versions of corresponding software:
|
||||
|
||||
- Download and install Android Studio from https://developer.android.com/studio.
|
||||
|
||||
- Get the latest pre-built OpenCV for Android release from https://github.com/opencv/opencv/releases and unpack it (for example, `opencv-4.1.2-android-sdk.zip`).
|
||||
- Get the latest pre-built OpenCV for Android release from https://github.com/opencv/opencv/releases and unpack it (for example, `opencv-4.2.0-android-sdk.zip`).
|
||||
|
||||
- Download MobileNet object detection model from https://github.com/chuanqi305/MobileNet-SSD. We need a configuration file `MobileNetSSD_deploy.prototxt` and weights `MobileNetSSD_deploy.caffemodel`.
|
||||
|
||||
|
||||
+1
-1
@@ -157,7 +157,7 @@ Now this file can be visalized with a `dot` command like this:
|
||||
|
||||
$ dot segm.dot -Tpng -o segm.png
|
||||
|
||||
or viewed instantly with `xdot` command (please refer to your
|
||||
or viewed interactively with `xdot` (please refer to your
|
||||
distribution/operating system documentation on how to install these
|
||||
packages).
|
||||
|
||||
|
||||
@@ -0,0 +1,440 @@
|
||||
# Implementing a face beautification algorithm with G-API {#tutorial_gapi_face_beautification}
|
||||
|
||||
[TOC]
|
||||
|
||||
# Introduction {#gapi_fb_intro}
|
||||
|
||||
In this tutorial you will learn:
|
||||
* Basics of a sample face beautification algorithm;
|
||||
* How to infer different networks inside a pipeline with G-API;
|
||||
* How to run a G-API pipeline on a video stream.
|
||||
|
||||
## Prerequisites {#gapi_fb_prerec}
|
||||
|
||||
This sample requires:
|
||||
- PC with GNU/Linux or Microsoft Windows (Apple macOS is supported but
|
||||
was not tested);
|
||||
- OpenCV 4.2 or later built with Intel® Distribution of [OpenVINO™
|
||||
Toolkit](https://docs.openvinotoolkit.org/) (building with [Intel®
|
||||
TBB](https://www.threadingbuildingblocks.org/intel-tbb-tutorial) is
|
||||
a plus);
|
||||
- The following topologies from OpenVINO™ Toolkit [Open Model
|
||||
Zoo](https://github.com/opencv/open_model_zoo):
|
||||
- `face-detection-adas-0001`;
|
||||
- `facial-landmarks-35-adas-0002`.
|
||||
|
||||
## Face beautification algorithm {#gapi_fb_algorithm}
|
||||
|
||||
We will implement a simple face beautification algorithm using a
|
||||
combination of modern Deep Learning techniques and traditional
|
||||
Computer Vision. The general idea behind the algorithm is to make
|
||||
face skin smoother while preserving face features like eyes or a
|
||||
mouth contrast. The algorithm identifies parts of the face using a DNN
|
||||
inference, applies different filters to the parts found, and then
|
||||
combines it into the final result using basic image arithmetics:
|
||||
|
||||
\dot
|
||||
strict digraph Pipeline {
|
||||
node [shape=record fontname=Helvetica fontsize=10 style=filled color="#4c7aa4" fillcolor="#5b9bd5" fontcolor="white"];
|
||||
edge [color="#62a8e7"];
|
||||
ordering="out";
|
||||
splines=ortho;
|
||||
rankdir=LR;
|
||||
|
||||
input [label="Input"];
|
||||
fd [label="Face\ndetector"];
|
||||
bgMask [label="Generate\nBG mask"];
|
||||
unshMask [label="Unsharp\nmask"];
|
||||
bilFil [label="Bilateral\nfilter"];
|
||||
shMask [label="Generate\nsharp mask"];
|
||||
blMask [label="Generate\nblur mask"];
|
||||
mul_1 [label="*" fontsize=24 shape=circle labelloc=b];
|
||||
mul_2 [label="*" fontsize=24 shape=circle labelloc=b];
|
||||
mul_3 [label="*" fontsize=24 shape=circle labelloc=b];
|
||||
|
||||
subgraph cluster_0 {
|
||||
style=dashed
|
||||
fontsize=10
|
||||
ld [label="Landmarks\ndetector"];
|
||||
label="for each face"
|
||||
}
|
||||
|
||||
sum_1 [label="+" fontsize=24 shape=circle];
|
||||
out [label="Output"];
|
||||
|
||||
temp_1 [style=invis shape=point width=0];
|
||||
temp_2 [style=invis shape=point width=0];
|
||||
temp_3 [style=invis shape=point width=0];
|
||||
temp_4 [style=invis shape=point width=0];
|
||||
temp_5 [style=invis shape=point width=0];
|
||||
temp_6 [style=invis shape=point width=0];
|
||||
temp_7 [style=invis shape=point width=0];
|
||||
temp_8 [style=invis shape=point width=0];
|
||||
temp_9 [style=invis shape=point width=0];
|
||||
|
||||
input -> temp_1 [arrowhead=none]
|
||||
temp_1 -> fd -> ld
|
||||
ld -> temp_4 [arrowhead=none]
|
||||
temp_4 -> bgMask
|
||||
bgMask -> mul_1 -> sum_1 -> out
|
||||
|
||||
temp_4 -> temp_5 -> temp_6 [arrowhead=none constraint=none]
|
||||
ld -> temp_2 -> temp_3 [style=invis constraint=none]
|
||||
|
||||
temp_1 -> {unshMask, bilFil}
|
||||
fd -> unshMask [style=invis constraint=none]
|
||||
unshMask -> bilFil [style=invis constraint=none]
|
||||
|
||||
bgMask -> shMask [style=invis constraint=none]
|
||||
shMask -> blMask [style=invis constraint=none]
|
||||
mul_1 -> mul_2 [style=invis constraint=none]
|
||||
temp_5 -> shMask -> mul_2
|
||||
temp_6 -> blMask -> mul_3
|
||||
|
||||
unshMask -> temp_2 -> temp_5 [style=invis]
|
||||
bilFil -> temp_3 -> temp_6 [style=invis]
|
||||
|
||||
mul_2 -> temp_7 [arrowhead=none]
|
||||
mul_3 -> temp_8 [arrowhead=none]
|
||||
|
||||
temp_8 -> temp_7 [arrowhead=none constraint=none]
|
||||
temp_7 -> sum_1 [constraint=none]
|
||||
|
||||
unshMask -> mul_2 [constraint=none]
|
||||
bilFil -> mul_3 [constraint=none]
|
||||
temp_1 -> mul_1 [constraint=none]
|
||||
}
|
||||
\enddot
|
||||
|
||||
Briefly the algorithm is described as follows:
|
||||
- Input image \f$I\f$ is passed to unsharp mask and bilateral filters
|
||||
(\f$U\f$ and \f$L\f$ respectively);
|
||||
- Input image \f$I\f$ is passed to an SSD-based face detector;
|
||||
- SSD result (a \f$[1 \times 1 \times 200 \times 7]\f$ blob) is parsed
|
||||
and converted to an array of faces;
|
||||
- Every face is passed to a landmarks detector;
|
||||
- Based on landmarks found for every face, three image masks are
|
||||
generated:
|
||||
- A background mask \f$b\f$ -- indicating which areas from the
|
||||
original image to keep as-is;
|
||||
- A face part mask \f$p\f$ -- identifying regions to preserve
|
||||
(sharpen).
|
||||
- A face skin mask \f$s\f$ -- identifying regions to blur;
|
||||
- The final result \f$O\f$ is a composition of features above
|
||||
calculated as \f$O = b*I + p*U + s*L\f$.
|
||||
|
||||
Generating face element masks based on a limited set of features (just
|
||||
35 per face, including all its parts) is not very trivial and is
|
||||
described in the sections below.
|
||||
|
||||
# Constructing a G-API pipeline {#gapi_fb_pipeline}
|
||||
|
||||
## Declaring Deep Learning topologies {#gapi_fb_decl_nets}
|
||||
|
||||
This sample is using two DNN detectors. Every network takes one input
|
||||
and produces one output. In G-API, networks are defined with macro
|
||||
G_API_NET():
|
||||
|
||||
@snippet cpp/tutorial_code/gapi/face_beautification/face_beautification.cpp net_decl
|
||||
|
||||
To get more information, see
|
||||
[Declaring Deep Learning topologies](@ref gapi_ifd_declaring_nets)
|
||||
described in the "Face Analytics pipeline" tutorial.
|
||||
|
||||
## Describing the processing graph {#gapi_fb_ppline}
|
||||
|
||||
The code below generates a graph for the algorithm above:
|
||||
|
||||
@snippet cpp/tutorial_code/gapi/face_beautification/face_beautification.cpp ppl
|
||||
|
||||
The resulting graph is a mixture of G-API's standard operations,
|
||||
user-defined operations (namespace `custom::`), and DNN inference.
|
||||
The generic function `cv::gapi::infer<>()` allows to trigger inference
|
||||
within the pipeline; networks to infer are specified as template
|
||||
parameters. The sample code is using two versions of `cv::gapi::infer<>()`:
|
||||
- A frame-oriented one is used to detect faces on the input frame.
|
||||
- An ROI-list oriented one is used to run landmarks inference on a
|
||||
list of faces -- this version produces an array of landmarks per
|
||||
every face.
|
||||
|
||||
More on this in "Face Analytics pipeline"
|
||||
([Building a GComputation](@ref gapi_ifd_gcomputation) section).
|
||||
|
||||
## Unsharp mask in G-API {#gapi_fb_unsh}
|
||||
|
||||
The unsharp mask \f$U\f$ for image \f$I\f$ is defined as:
|
||||
|
||||
\f[U = I - s * L(M(I)),\f]
|
||||
|
||||
where \f$M()\f$ is a median filter, \f$L()\f$ is the Laplace operator,
|
||||
and \f$s\f$ is a strength coefficient. While G-API doesn't provide
|
||||
this function out-of-the-box, it is expressed naturally with the
|
||||
existing G-API operations:
|
||||
|
||||
@snippet cpp/tutorial_code/gapi/face_beautification/face_beautification.cpp unsh
|
||||
|
||||
Note that the code snipped above is a regular C++ function defined
|
||||
with G-API types. Users can write functions like this to simplify
|
||||
graph construction; when called, this function just puts the relevant
|
||||
nodes to the pipeline it is used in.
|
||||
|
||||
# Custom operations {#gapi_fb_proc}
|
||||
|
||||
The face beautification graph is using custom operations
|
||||
extensively. This chapter focuses on the most interesting kernels,
|
||||
refer to [G-API Kernel API](@ref gapi_kernel_api) for general
|
||||
information on defining operations and implementing kernels in G-API.
|
||||
|
||||
## Face detector post-processing {#gapi_fb_face_detect}
|
||||
|
||||
A face detector output is converted to an array of faces with the
|
||||
following kernel:
|
||||
|
||||
@snippet cpp/tutorial_code/gapi/face_beautification/face_beautification.cpp vec_ROI
|
||||
@snippet cpp/tutorial_code/gapi/face_beautification/face_beautification.cpp fd_pp
|
||||
|
||||
## Facial landmarks post-processing {#gapi_fb_landm_detect}
|
||||
|
||||
The algorithm infers locations of face elements (like the eyes, the mouth
|
||||
and the head contour itself) using a generic facial landmarks detector
|
||||
(<a href="https://github.com/opencv/open_model_zoo/blob/master/models/intel/facial-landmarks-35-adas-0002/description/facial-landmarks-35-adas-0002.md">details</a>)
|
||||
from OpenVINO™ Open Model Zoo. However, the detected landmarks as-is are not
|
||||
enough to generate masks --- this operation requires regions of interest on
|
||||
the face represented by closed contours, so some interpolation is applied to
|
||||
get them. This landmarks
|
||||
processing and interpolation is performed by the following kernel:
|
||||
|
||||
@snippet cpp/tutorial_code/gapi/face_beautification/face_beautification.cpp ld_pp_cnts
|
||||
|
||||
The kernel takes two arrays of denormalized landmarks coordinates and
|
||||
returns an array of elements' closed contours and an array of faces'
|
||||
closed contours; in other words, outputs are, the first, an array of
|
||||
contours of image areas to be sharpened and, the second, another one
|
||||
to be smoothed.
|
||||
|
||||
Here and below `Contour` is a vector of points.
|
||||
|
||||
### Getting an eye contour {#gapi_fb_ld_eye}
|
||||
|
||||
Eye contours are estimated with the following function:
|
||||
|
||||
@snippet cpp/tutorial_code/gapi/face_beautification/face_beautification.cpp ld_pp_incl
|
||||
@snippet cpp/tutorial_code/gapi/face_beautification/face_beautification.cpp ld_pp_eye
|
||||
|
||||
Briefly, this function restores the bottom side of an eye by a
|
||||
half-ellipse based on two points in left and right eye
|
||||
corners. In fact, `cv::ellipse2Poly()` is used to approximate the eye region, and
|
||||
the function only defines ellipse parameters based on just two points:
|
||||
- The ellipse center and the \f$X\f$ half-axis calculated by two eye Points;
|
||||
- The \f$Y\f$ half-axis calculated according to the assumption that an average
|
||||
eye width is \f$1/3\f$ of its length;
|
||||
- The start and the end angles which are 0 and 180 (refer to
|
||||
`cv::ellipse()` documentation);
|
||||
- The angle delta: how much points to produce in the contour;
|
||||
- The inclination angle of the axes.
|
||||
|
||||
The use of the `atan2()` instead of just `atan()` in function
|
||||
`custom::getLineInclinationAngleDegrees()` is essential as it allows to
|
||||
return a negative value depending on the `x` and the `y` signs so we
|
||||
can get the right angle even in case of upside-down face arrangement
|
||||
(if we put the points in the right order, of course).
|
||||
|
||||
### Getting a forehead contour {#gapi_fb_ld_fhd}
|
||||
|
||||
The function approximates the forehead contour:
|
||||
|
||||
@snippet cpp/tutorial_code/gapi/face_beautification/face_beautification.cpp ld_pp_fhd
|
||||
|
||||
As we have only jaw points in our detected landmarks, we have to get a
|
||||
half-ellipse based on three points of a jaw: the leftmost, the
|
||||
rightmost and the lowest one. The jaw width is assumed to be equal to the
|
||||
forehead width and the latter is calculated using the left and the
|
||||
right points. Speaking of the \f$Y\f$ axis, we have no points to get
|
||||
it directly, and instead assume that the forehead height is about \f$2/3\f$
|
||||
of the jaw height, which can be figured out from the face center (the
|
||||
middle between the left and right points) and the lowest jaw point.
|
||||
|
||||
## Drawing masks {#gapi_fb_masks_drw}
|
||||
|
||||
When we have all the contours needed, we are able to draw masks:
|
||||
|
||||
@snippet cpp/tutorial_code/gapi/face_beautification/face_beautification.cpp msk_ppline
|
||||
|
||||
The steps to get the masks are:
|
||||
* the "sharp" mask calculation:
|
||||
* fill the contours that should be sharpened;
|
||||
* blur that to get the "sharp" mask (`mskSharpG`);
|
||||
* the "bilateral" mask calculation:
|
||||
* fill all the face contours fully;
|
||||
* blur that;
|
||||
* subtract areas which intersect with the "sharp" mask --- and get the
|
||||
"bilateral" mask (`mskBlurFinal`);
|
||||
* the background mask calculation:
|
||||
* add two previous masks
|
||||
* set all non-zero pixels of the result as 255 (by `cv::gapi::threshold()`)
|
||||
* revert the output (by `cv::gapi::bitwise_not`) to get the background
|
||||
mask (`mskNoFaces`).
|
||||
|
||||
# Configuring and running the pipeline {#gapi_fb_comp_args}
|
||||
|
||||
Once the graph is fully expressed, we can finally compile it and run
|
||||
on real data. G-API graph compilation is the stage where the G-API
|
||||
framework actually understands which kernels and networks to use. This
|
||||
configuration happens via G-API compilation arguments.
|
||||
|
||||
## DNN parameters {#gapi_fb_comp_args_net}
|
||||
|
||||
This sample is using OpenVINO™ Toolkit Inference Engine backend for DL
|
||||
inference, which is configured the following way:
|
||||
|
||||
@snippet cpp/tutorial_code/gapi/face_beautification/face_beautification.cpp net_param
|
||||
|
||||
Every `cv::gapi::ie::Params<>` object is related to the network
|
||||
specified in its template argument. We should pass there the network
|
||||
type we have defined in `G_API_NET()` in the early beginning of the
|
||||
tutorial.
|
||||
|
||||
Network parameters are then wrapped in `cv::gapi::NetworkPackage`:
|
||||
|
||||
@snippet cpp/tutorial_code/gapi/face_beautification/face_beautification.cpp netw
|
||||
|
||||
More details in "Face Analytics Pipeline"
|
||||
([Configuring the pipeline](@ref gapi_ifd_configuration) section).
|
||||
|
||||
## Kernel packages {#gapi_fb_comp_args_kernels}
|
||||
|
||||
In this example we use a lot of custom kernels, in addition to that we
|
||||
use Fluid backend to optimize out memory for G-API's standard kernels
|
||||
where applicable. The resulting kernel package is formed like this:
|
||||
|
||||
@snippet cpp/tutorial_code/gapi/face_beautification/face_beautification.cpp kern_pass_1
|
||||
|
||||
## Compiling the streaming pipeline {#gapi_fb_compiling}
|
||||
|
||||
G-API optimizes execution for video streams when compiled in the
|
||||
"Streaming" mode.
|
||||
|
||||
@snippet cpp/tutorial_code/gapi/face_beautification/face_beautification.cpp str_comp
|
||||
|
||||
More on this in "Face Analytics Pipeline"
|
||||
([Configuring the pipeline](@ref gapi_ifd_configuration) section).
|
||||
|
||||
## Running the streaming pipeline {#gapi_fb_running}
|
||||
|
||||
In order to run the G-API streaming pipeline, all we need is to
|
||||
specify the input video source, call
|
||||
`cv::GStreamingCompiled::start()`, and then fetch the pipeline
|
||||
processing results:
|
||||
|
||||
@snippet cpp/tutorial_code/gapi/face_beautification/face_beautification.cpp str_src
|
||||
@snippet cpp/tutorial_code/gapi/face_beautification/face_beautification.cpp str_loop
|
||||
|
||||
Once results are ready and can be pulled from the pipeline we display
|
||||
it on the screen and handle GUI events.
|
||||
|
||||
See [Running the pipeline](@ref gapi_ifd_running) section
|
||||
in the "Face Analytics Pipeline" tutorial for more details.
|
||||
|
||||
# Conclusion {#gapi_fb_cncl}
|
||||
|
||||
The tutorial has two goals: to show the use of brand new features of
|
||||
G-API introduced in OpenCV 4.2, and give a basic understanding on a
|
||||
sample face beautification algorithm.
|
||||
|
||||
The result of the algorithm application:
|
||||
|
||||

|
||||
|
||||
On the test machine (Intel® Core™ i7-8700) the G-API-optimized video
|
||||
pipeline outperforms its serial (non-pipelined) version by a factor of
|
||||
**2.7** -- meaning that for such a non-trivial graph, the proper
|
||||
pipelining can bring almost 3x increase in performance.
|
||||
|
||||
<!---
|
||||
The idea in general is to implement a real-time video stream processing that
|
||||
detects faces and applies some filters to make them look beautiful (more or
|
||||
less). The pipeline is the following:
|
||||
|
||||
Two topologies from OMZ have been used in this sample: the
|
||||
<a href="https://github.com/opencv/open_model_zoo/tree/master/models/intel
|
||||
/face-detection-adas-0001">face-detection-adas-0001</a>
|
||||
and the
|
||||
<a href="https://github.com/opencv/open_model_zoo/blob/master/models/intel
|
||||
/facial-landmarks-35-adas-0002/description/facial-landmarks-35-adas-0002.md">
|
||||
facial-landmarks-35-adas-0002</a>.
|
||||
|
||||
The face detector takes the input image and returns a blob with the shape
|
||||
[1,1,200,7] after the inference (200 is the maximum number of
|
||||
faces which can be detected).
|
||||
In order to process every face individually, we need to convert this output to a
|
||||
list of regions on the image.
|
||||
|
||||
The masks for different filters are built based on facial landmarks, which are
|
||||
inferred for every face. The result of the inference
|
||||
is a blob with 35 landmarks: the first 18 of them are facial elements
|
||||
(eyes, eyebrows, a nose, a mouth) and the last 17 --- a jaw contour. Landmarks
|
||||
are floating point values of coordinates normalized relatively to an input ROI
|
||||
(not the original frame). In addition, for the further goals we need contours of
|
||||
eyes, mouths, faces, etc., not the landmarks. So, post-processing of the Mat is
|
||||
also required here. The process is split into two parts --- landmarks'
|
||||
coordinates denormalization to the real pixel coordinates of the source frame
|
||||
and getting necessary closed contours based on these coordinates.
|
||||
|
||||
The last step of processing the inference data is drawing masks using the
|
||||
calculated contours. In this demo the contours don't need to be pixel accurate,
|
||||
since masks are blurred with Gaussian filter anyway. Another point that should
|
||||
be mentioned here is getting
|
||||
three masks (for areas to be smoothed, for ones to be sharpened and for the
|
||||
background) which have no intersections with each other; this approach allows to
|
||||
apply the calculated masks to the corresponding images prepared beforehand and
|
||||
then just to summarize them to get the output image without any other actions.
|
||||
|
||||
As we can see, this algorithm is appropriate to illustrate G-API usage
|
||||
convenience and efficiency in the context of solving a real CV/DL problem.
|
||||
|
||||
(On detector post-proc)
|
||||
Some points to be mentioned about this kernel implementation:
|
||||
|
||||
- It takes a `cv::Mat` from the detector and a `cv::Mat` from the input; it
|
||||
returns an array of ROI's where faces have been detected.
|
||||
|
||||
- `cv::Mat` data parsing by the pointer on a float is used here.
|
||||
|
||||
- By far the most important thing here is solving an issue that sometimes
|
||||
detector returns coordinates located outside of the image; if we pass such an
|
||||
ROI to be processed, errors in the landmarks detection will occur. The frame box
|
||||
`borders` is created and then intersected with the face rectangle
|
||||
(by `operator&()`) to handle such cases and save the ROI which is for sure
|
||||
inside the frame.
|
||||
|
||||
Data parsing after the facial landmarks detector happens according to the same
|
||||
scheme with inconsiderable adjustments.
|
||||
|
||||
|
||||
## Possible further improvements
|
||||
|
||||
There are some points in the algorithm to be improved.
|
||||
|
||||
### Correct ROI reshaping for meeting conditions required by the facial landmarks detector
|
||||
|
||||
The input of the facial landmarks detector is a square ROI, but the face
|
||||
detector gives non-square rectangles in general. If we let the backend within
|
||||
Inference-API compress the rectangle to a square by itself, the lack of
|
||||
inference accuracy can be noticed in some cases.
|
||||
There is a solution: we can give a describing square ROI instead of the
|
||||
rectangular one to the landmarks detector, so there will be no need to compress
|
||||
the ROI, which will lead to accuracy improvement.
|
||||
Unfortunately, another problem occurs if we do that:
|
||||
if the rectangular ROI is near the border, a describing square will probably go
|
||||
out of the frame --- that leads to errors of the landmarks detector.
|
||||
To aviod such a mistake, we have to implement an algorithm that, firstly,
|
||||
describes every rectangle by a square, then counts the farthest coordinates
|
||||
turned up to be outside of the frame and, finally, pads the source image by
|
||||
borders (e.g. single-colored) with the size counted. It will be safe to take
|
||||
square ROIs for the facial landmarks detector after that frame adjustment.
|
||||
|
||||
### Research for the best parameters (used in GaussianBlur() or unsharpMask(), etc.)
|
||||
|
||||
### Parameters autoscaling
|
||||
|
||||
-->
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 172 KiB |
@@ -0,0 +1,353 @@
|
||||
# Face analytics pipeline with G-API {#tutorial_gapi_interactive_face_detection}
|
||||
|
||||
[TOC]
|
||||
|
||||
# Overview {#gapi_ifd_intro}
|
||||
|
||||
In this tutorial you will learn:
|
||||
* How to integrate Deep Learning inference in a G-API graph;
|
||||
* How to run a G-API graph on a video stream and obtain data from it.
|
||||
|
||||
# Prerequisites {#gapi_ifd_prereq}
|
||||
|
||||
This sample requires:
|
||||
- PC with GNU/Linux or Microsoft Windows (Apple macOS is supported but
|
||||
was not tested);
|
||||
- OpenCV 4.2 or later built with Intel® Distribution of [OpenVINO™
|
||||
Toolkit](https://docs.openvinotoolkit.org/) (building with [Intel®
|
||||
TBB](https://www.threadingbuildingblocks.org/intel-tbb-tutorial) is
|
||||
a plus);
|
||||
- The following topologies from OpenVINO™ Toolkit [Open Model
|
||||
Zoo](https://github.com/opencv/open_model_zoo):
|
||||
- `face-detection-adas-0001`;
|
||||
- `age-gender-recognition-retail-0013`;
|
||||
- `emotions-recognition-retail-0003`.
|
||||
|
||||
# Introduction: why G-API {#gapi_ifd_why}
|
||||
|
||||
Many computer vision algorithms run on a video stream rather than on
|
||||
individual images. Stream processing usually consists of multiple
|
||||
steps -- like decode, preprocessing, detection, tracking,
|
||||
classification (on detected objects), and visualization -- forming a
|
||||
*video processing pipeline*. Moreover, many these steps of such
|
||||
pipeline can run in parallel -- modern platforms have different
|
||||
hardware blocks on the same chip like decoders and GPUs, and extra
|
||||
accelerators can be plugged in as extensions, like Intel® Movidius™
|
||||
Neural Compute Stick for deep learning offload.
|
||||
|
||||
Given all this manifold of options and a variety in video analytics
|
||||
algorithms, managing such pipelines effectively quickly becomes a
|
||||
problem. For sure it can be done manually, but this approach doesn't
|
||||
scale: if a change is required in the algorithm (e.g. a new pipeline
|
||||
step is added), or if it is ported on a new platform with different
|
||||
capabilities, the whole pipeline needs to be re-optimized.
|
||||
|
||||
Starting with version 4.2, OpenCV offers a solution to this
|
||||
problem. OpenCV G-API now can manage Deep Learning inference (a
|
||||
cornerstone of any modern analytics pipeline) with a traditional
|
||||
Computer Vision as well as video capturing/decoding, all in a single
|
||||
pipeline. G-API takes care of pipelining itself -- so if the algorithm
|
||||
or platform changes, the execution model adapts to it automatically.
|
||||
|
||||
# Pipeline overview {#gapi_ifd_overview}
|
||||
|
||||
Our sample application is based on ["Interactive Face Detection"] demo
|
||||
from OpenVINO™ Toolkit Open Model Zoo. A simplified pipeline consists
|
||||
of the following steps:
|
||||
1. Image acquisition and decode;
|
||||
2. Detection with preprocessing;
|
||||
3. Classification with preprocessing for every detected object with
|
||||
two networks;
|
||||
4. Visualization.
|
||||
|
||||
\dot
|
||||
digraph pipeline {
|
||||
node [shape=record fontname=Helvetica fontsize=10 style=filled color="#4c7aa4" fillcolor="#5b9bd5" fontcolor="white"];
|
||||
edge [color="#62a8e7"];
|
||||
splines=ortho;
|
||||
|
||||
rankdir = LR;
|
||||
subgraph cluster_0 {
|
||||
color=invis;
|
||||
capture [label="Capture\nDecode"];
|
||||
resize [label="Resize\nConvert"];
|
||||
detect [label="Detect faces"];
|
||||
capture -> resize -> detect
|
||||
}
|
||||
|
||||
subgraph cluster_1 {
|
||||
graph[style=dashed];
|
||||
|
||||
subgraph cluster_2 {
|
||||
color=invis;
|
||||
temp_4 [style=invis shape=point width=0];
|
||||
postproc_1 [label="Crop\nResize\nConvert"];
|
||||
age_gender [label="Classify\nAge/gender"];
|
||||
postproc_1 -> age_gender [constraint=true]
|
||||
temp_4 -> postproc_1 [constraint=none]
|
||||
}
|
||||
|
||||
subgraph cluster_3 {
|
||||
color=invis;
|
||||
postproc_2 [label="Crop\nResize\nConvert"];
|
||||
emo [label="Classify\nEmotions"];
|
||||
postproc_2 -> emo [constraint=true]
|
||||
}
|
||||
label="(for each face)";
|
||||
}
|
||||
|
||||
temp_1 [style=invis shape=point width=0];
|
||||
temp_2 [style=invis shape=point width=0];
|
||||
detect -> temp_1 [arrowhead=none]
|
||||
temp_1 -> postproc_1
|
||||
|
||||
capture -> {temp_4, temp_2} [arrowhead=none constraint=false]
|
||||
temp_2 -> postproc_2
|
||||
|
||||
temp_1 -> temp_2 [arrowhead=none constraint=false]
|
||||
|
||||
temp_3 [style=invis shape=point width=0];
|
||||
show [label="Visualize\nDisplay"];
|
||||
|
||||
{age_gender, emo} -> temp_3 [arrowhead=none]
|
||||
temp_3 -> show
|
||||
}
|
||||
\enddot
|
||||
|
||||
# Constructing a pipeline {#gapi_ifd_constructing}
|
||||
|
||||
Constructing a G-API graph for a video streaming case does not differ
|
||||
much from a [regular usage](@ref gapi_example) of G-API -- it is still
|
||||
about defining graph *data* (with cv::GMat, cv::GScalar, and
|
||||
cv::GArray) and *operations* over it. Inference also becomes an
|
||||
operation in the graph, but is defined in a little bit different way.
|
||||
|
||||
## Declaring Deep Learning topologies {#gapi_ifd_declaring_nets}
|
||||
|
||||
In contrast with traditional CV functions (see [core] and [imgproc])
|
||||
where G-API declares distinct operations for every function, inference
|
||||
in G-API is a single generic operation cv::gapi::infer<>. As usual, it
|
||||
is just an interface and it can be implemented in a number of ways under
|
||||
the hood. In OpenCV 4.2, only OpenVINO™ Inference Engine-based backend
|
||||
is available, and OpenCV's own DNN module-based backend is to come.
|
||||
|
||||
cv::gapi::infer<> is _parametrized_ by the details of a topology we are
|
||||
going to execute. Like operations, topologies in G-API are strongly
|
||||
typed and are defined with a special macro G_API_NET():
|
||||
|
||||
@snippet cpp/tutorial_code/gapi/age_gender_emotion_recognition/age_gender_emotion_recognition.cpp G_API_NET
|
||||
|
||||
Similar to how operations are defined with G_API_OP(), network
|
||||
description requires three parameters:
|
||||
1. A type name. Every defined topology is declared as a distinct C++
|
||||
type which is used further in the program -- see below;
|
||||
2. A `std::function<>`-like API signature. G-API traits networks as
|
||||
regular "functions" which take and return data. Here network
|
||||
`Faces` (a detector) takes a cv::GMat and returns a cv::GMat, while
|
||||
network `AgeGender` is known to provide two outputs (age and gender
|
||||
blobs, respecitvely) -- so its has a `std::tuple<>` as a return
|
||||
type.
|
||||
3. A topology name -- can be any non-empty string, G-API is using
|
||||
these names to distinguish networks inside. Names should be unique
|
||||
in the scope of a single graph.
|
||||
|
||||
## Building a GComputation {#gapi_ifd_gcomputation}
|
||||
|
||||
Now the above pipeline is expressed in G-API like this:
|
||||
|
||||
@snippet cpp/tutorial_code/gapi/age_gender_emotion_recognition/age_gender_emotion_recognition.cpp GComputation
|
||||
|
||||
Every pipeline starts with declaring empty data objects -- which act
|
||||
as inputs to the pipeline. Then we call a generic cv::gapi::infer<>
|
||||
specialized to `Faces` detection network. cv::gapi::infer<> inherits its
|
||||
signature from its template parameter -- and in this case it expects
|
||||
one input cv::GMat and produces one output cv::GMat.
|
||||
|
||||
In this sample we use a pre-trained SSD-based network and its output
|
||||
needs to be parsed to an array of detections (object regions of
|
||||
interest, ROIs). It is done by a custom operation `custom::PostProc`,
|
||||
which returns an array of rectangles (of type `cv::GArray<cv::Rect>`)
|
||||
back to the pipeline. This operation also filters out results by a
|
||||
confidence threshold -- and these details are hidden in the kernel
|
||||
itself. Still, at the moment of graph construction we operate with
|
||||
interfaces only and don't need actual kernels to express the pipeline
|
||||
-- so the implementation of this post-processing will be listed later.
|
||||
|
||||
After detection result output is parsed to an array of objects, we can run
|
||||
classification on any of those. G-API doesn't support syntax for
|
||||
in-graph loops like `for_each()` yet, but instead cv::gapi::infer<>
|
||||
comes with a special list-oriented overload.
|
||||
|
||||
User can call cv::gapi::infer<> with a cv::GArray as the first
|
||||
argument, so then G-API assumes it needs to run the associated network
|
||||
on every rectangle from the given list of the given frame (second
|
||||
argument). Result of such operation is also a list -- a cv::GArray of
|
||||
cv::GMat.
|
||||
|
||||
Since `AgeGender` network itself produces two outputs, it's output
|
||||
type for a list-based version of cv::gapi::infer is a tuple of
|
||||
arrays. We use `std::tie()` to decompose this input into two distinct
|
||||
objects.
|
||||
|
||||
`Emotions` network produces a single output so its list-based
|
||||
inference's return type is `cv::GArray<cv::GMat>`.
|
||||
|
||||
# Configuring the pipeline {#gapi_ifd_configuration}
|
||||
|
||||
G-API strictly separates construction from configuration -- with the
|
||||
idea to keep algorithm code itself platform-neutral. In the above
|
||||
listings we only declared our operations and expressed the overall
|
||||
data flow, but didn't even mention that we use OpenVINO™. We only
|
||||
described *what* we do, but not *how* we do it. Keeping these two
|
||||
aspects clearly separated is the design goal for G-API.
|
||||
|
||||
Platform-specific details arise when the pipeline is *compiled* --
|
||||
i.e. is turned from a declarative to an executable form. The way *how*
|
||||
to run stuff is specified via compilation arguments, and new
|
||||
inference/streaming features are no exception from this rule.
|
||||
|
||||
G-API is built on backends which implement interfaces (see
|
||||
[Architecture] and [Kernels] for details) -- thus cv::gapi::infer<> is
|
||||
a function which can be implemented by different backends. In OpenCV
|
||||
4.2, only OpenVINO™ Inference Engine backend for inference is
|
||||
available. Every inference backend in G-API has to provide a special
|
||||
parameterizable structure to express *backend-specific* neural network
|
||||
parameters -- and in this case, it is cv::gapi::ie::Params:
|
||||
|
||||
@snippet cpp/tutorial_code/gapi/age_gender_emotion_recognition/age_gender_emotion_recognition.cpp Param_Cfg
|
||||
|
||||
Here we define three parameter objects: `det_net`, `age_net`, and
|
||||
`emo_net`. Every object is a cv::gapi::ie::Params structure
|
||||
parametrization for each particular network we use. On a compilation
|
||||
stage, G-API automatically matches network parameters with their
|
||||
cv::gapi::infer<> calls in graph using this information.
|
||||
|
||||
Regardless of the topology, every parameter structure is constructed
|
||||
with three string arguments -- specific to the OpenVINO™ Inference
|
||||
Engine:
|
||||
1. Path to the topology's intermediate representation (.xml file);
|
||||
2. Path to the topology's model weights (.bin file);
|
||||
3. Device where to run -- "CPU", "GPU", and others -- based on your
|
||||
OpenVINO™ Toolkit installation.
|
||||
These arguments are taken from the command-line parser.
|
||||
|
||||
Once networks are defined and custom kernels are implemented, the
|
||||
pipeline is compiled for streaming:
|
||||
|
||||
@snippet cpp/tutorial_code/gapi/age_gender_emotion_recognition/age_gender_emotion_recognition.cpp Compile
|
||||
|
||||
cv::GComputation::compileStreaming() triggers a special video-oriented
|
||||
form of graph compilation where G-API is trying to optimize
|
||||
throughput. Result of this compilation is an object of special type
|
||||
cv::GStreamingCompiled -- in constract to a traditional callable
|
||||
cv::GCompiled, these objects are closer to media players in their
|
||||
semantics.
|
||||
|
||||
@note There is no need to pass metadata arguments describing the
|
||||
format of the input video stream in
|
||||
cv::GComputation::compileStreaming() -- G-API figures automatically
|
||||
what are the formats of the input vector and adjusts the pipeline to
|
||||
these formats on-the-fly. User still can pass metadata there as with
|
||||
regular cv::GComputation::compile() in order to fix the pipeline to
|
||||
the specific input format.
|
||||
|
||||
# Running the pipeline {#gapi_ifd_running}
|
||||
|
||||
Pipelining optimization is based on processing multiple input video
|
||||
frames simultaneously, running different steps of the pipeline in
|
||||
parallel. This is why it works best when the framework takes full
|
||||
control over the video stream.
|
||||
|
||||
The idea behind streaming API is that user specifies an *input source*
|
||||
to the pipeline and then G-API manages its execution automatically
|
||||
until the source ends or user interrupts the execution. G-API pulls
|
||||
new image data from the source and passes it to the pipeline for
|
||||
processing.
|
||||
|
||||
Streaming sources are represented by the interface
|
||||
cv::gapi::wip::IStreamSource. Objects implementing this interface may
|
||||
be passed to `GStreamingCompiled` as regular inputs via `cv::gin()`
|
||||
helper function. In OpenCV 4.2, only one streaming source is allowed
|
||||
per pipeline -- this requirement will be relaxed in the future.
|
||||
|
||||
OpenCV comes with a great class cv::VideoCapture and by default G-API
|
||||
ships with a stream source class based on it --
|
||||
cv::gapi::wip::GCaptureSource. Users can implement their own
|
||||
streaming sources e.g. using [VAAPI] or other Media or Networking
|
||||
APIs.
|
||||
|
||||
Sample application specifies the input source as follows:
|
||||
|
||||
@snippet cpp/tutorial_code/gapi/age_gender_emotion_recognition/age_gender_emotion_recognition.cpp Source
|
||||
|
||||
Please note that a GComputation may still have multiple inputs like
|
||||
cv::GMat, cv::GScalar, or cv::GArray objects. User can pass their
|
||||
respective host-side types (cv::Mat, cv::Scalar, std::vector<>) in the
|
||||
input vector as well, but in Streaming mode these objects will create
|
||||
"endless" constant streams. Mixing a real video source stream and a
|
||||
const data stream is allowed.
|
||||
|
||||
Running a pipeline is easy -- just call
|
||||
cv::GStreamingCompiled::start() and fetch your data with blocking
|
||||
cv::GStreamingCompiled::pull() or non-blocking
|
||||
cv::GStreamingCompiled::try_pull(); repeat until the stream ends:
|
||||
|
||||
@snippet cpp/tutorial_code/gapi/age_gender_emotion_recognition/age_gender_emotion_recognition.cpp Run
|
||||
|
||||
The above code may look complex but in fact it handles two modes --
|
||||
with and without graphical user interface (GUI):
|
||||
- When a sample is running in a "headless" mode (`--pure` option is
|
||||
set), this code simply pulls data from the pipeline with the
|
||||
blocking `pull()` until it ends. This is the most performant mode of
|
||||
execution.
|
||||
- When results are also displayed on the screen, the Window System
|
||||
needs to take some time to refresh the window contents and handle
|
||||
GUI events. In this case, the demo pulls data with a non-blocking
|
||||
`try_pull()` until there is no more data available (but it does not
|
||||
mark end of the stream -- just means new data is not ready yet), and
|
||||
only then displays the latest obtained result and refreshes the
|
||||
screen. Reducing the time spent in GUI with this trick increases the
|
||||
overall performance a little bit.
|
||||
|
||||
# Comparison with serial mode {#gapi_ifd_comparison}
|
||||
|
||||
The sample can also run in a serial mode for a reference and
|
||||
benchmarking purposes. In this case, a regular
|
||||
cv::GComputation::compile() is used and a regular single-frame
|
||||
cv::GCompiled object is produced; the pipelining optimization is not
|
||||
applied within G-API; it is the user responsibility to acquire image
|
||||
frames from cv::VideoCapture object and pass those to G-API.
|
||||
|
||||
@snippet cpp/tutorial_code/gapi/age_gender_emotion_recognition/age_gender_emotion_recognition.cpp Run_Serial
|
||||
|
||||
On a test machine (Intel® Core™ i5-6600), with OpenCV built with
|
||||
[Intel® TBB]
|
||||
support, detector network assigned to CPU, and classifiers to iGPU,
|
||||
the pipelined sample outperformes the serial one by the factor of
|
||||
1.36x (thus adding +36% in overall throughput).
|
||||
|
||||
# Conclusion {#gapi_ifd_conclusion}
|
||||
|
||||
G-API introduces a technological way to build and optimize hybrid
|
||||
pipelines. Switching to a new execution model does not require changes
|
||||
in the algorithm code expressed with G-API -- only the way how graph
|
||||
is triggered differs.
|
||||
|
||||
# Listing: post-processing kernel {#gapi_ifd_pp}
|
||||
|
||||
G-API gives an easy way to plug custom code into the pipeline even if
|
||||
it is running in a streaming mode and processing tensor
|
||||
data. Inference results are represented by multi-dimensional cv::Mat
|
||||
objects so accessing those is as easy as with a regular DNN module.
|
||||
|
||||
The OpenCV-based SSD post-processing kernel is defined and implemented in this
|
||||
sample as follows:
|
||||
|
||||
@snippet cpp/tutorial_code/gapi/age_gender_emotion_recognition/age_gender_emotion_recognition.cpp Postproc
|
||||
|
||||
["Interactive Face Detection"]: https://github.com/opencv/open_model_zoo/tree/master/demos/interactive_face_detection_demo
|
||||
[core]: @ref gapi_core
|
||||
[imgproc]: @ref gapi_imgproc
|
||||
[Architecture]: @ref gapi_hld
|
||||
[Kernels]: @ref gapi_kernel_api
|
||||
[VAAPI]: https://01.org/vaapi
|
||||
@@ -3,6 +3,20 @@
|
||||
In this section you will learn about graph-based image processing and
|
||||
how G-API module can be used for that.
|
||||
|
||||
- @subpage tutorial_gapi_interactive_face_detection
|
||||
|
||||
*Languages:* C++
|
||||
|
||||
*Compatibility:* \> OpenCV 4.2
|
||||
|
||||
*Author:* Dmitry Matveev
|
||||
|
||||
This tutorial illustrates how to build a hybrid video processing
|
||||
pipeline with G-API where Deep Learning and image processing are
|
||||
combined effectively to maximize the overall throughput. This
|
||||
sample requires Intel® distribution of OpenVINO™ Toolkit version
|
||||
2019R2 or later.
|
||||
|
||||
- @subpage tutorial_gapi_anisotropic_segmentation
|
||||
|
||||
*Languages:* C++
|
||||
@@ -15,3 +29,14 @@ how G-API module can be used for that.
|
||||
is ported on G-API, covering the basic intuition behind this
|
||||
transition process, and examining benefits which a graph model
|
||||
brings there.
|
||||
|
||||
- @subpage tutorial_gapi_face_beautification
|
||||
|
||||
*Languages:* C++
|
||||
|
||||
*Compatibility:* \> OpenCV 4.2
|
||||
|
||||
*Author:* Orest Chura
|
||||
|
||||
In this tutorial we build a complex hybrid Computer Vision/Deep
|
||||
Learning video processing pipeline with G-API.
|
||||
|
||||
@@ -210,12 +210,12 @@ Explanation
|
||||
@code{.cpp}
|
||||
image2 = image - Scalar::all(i)
|
||||
@endcode
|
||||
So, **image2** is the substraction of **image** and **Scalar::all(i)**. In fact, what happens
|
||||
here is that every pixel of **image2** will be the result of substracting every pixel of
|
||||
So, **image2** is the subtraction of **image** and **Scalar::all(i)**. In fact, what happens
|
||||
here is that every pixel of **image2** will be the result of subtracting every pixel of
|
||||
**image** minus the value of **i** (remember that for each pixel we are considering three values
|
||||
such as R, G and B, so each of them will be affected)
|
||||
|
||||
Also remember that the substraction operation *always* performs internally a **saturate**
|
||||
Also remember that the subtraction operation *always* performs internally a **saturate**
|
||||
operation, which means that the result obtained will always be inside the allowed range (no
|
||||
negative and between 0 and 255 for our example).
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@ Cross referencing OpenCV from other Doxygen projects {#tutorial_cross_referencin
|
||||
Cross referencing OpenCV
|
||||
------------------------
|
||||
|
||||
[Doxygen](https://www.stack.nl/~dimitri/doxygen/) is a tool to generate
|
||||
[Doxygen](http://www.doxygen.nl) is a tool to generate
|
||||
documentations like the OpenCV documentation you are reading right now.
|
||||
It is used by a variety of software projects and if you happen to use it
|
||||
to generate your own documentation, and you are using OpenCV inside your
|
||||
@@ -36,14 +36,14 @@ Open your Doxyfile using your favorite text editor and search for the key
|
||||
`TAGFILES`. Change it as follows:
|
||||
|
||||
@code
|
||||
TAGFILES = ./docs/doxygen-tags/opencv.tag=http://docs.opencv.org/4.1.2
|
||||
TAGFILES = ./docs/doxygen-tags/opencv.tag=http://docs.opencv.org/4.2.0
|
||||
@endcode
|
||||
|
||||
If you had other definitions already, you can append the line using a `\`:
|
||||
|
||||
@code
|
||||
TAGFILES = ./docs/doxygen-tags/libstdc++.tag=https://gcc.gnu.org/onlinedocs/libstdc++/latest-doxygen \
|
||||
./docs/doxygen-tags/opencv.tag=http://docs.opencv.org/4.1.2
|
||||
./docs/doxygen-tags/opencv.tag=http://docs.opencv.org/4.2.0
|
||||
@endcode
|
||||
|
||||
Doxygen can now use the information from the tag file to link to the OpenCV
|
||||
@@ -57,5 +57,5 @@ contain a `your_project.tag` file in its root directory.
|
||||
References
|
||||
----------
|
||||
|
||||
- [Doxygen: Linking to external documentation](https://www.stack.nl/~dimitri/doxygen/manual/external.html)
|
||||
- [Doxygen: Linking to external documentation](http://www.doxygen.nl/manual/external.html)
|
||||
- [opencv.tag](opencv.tag)
|
||||
|
||||
@@ -684,12 +684,12 @@ References {#tutorial_documentation_refs}
|
||||
- [Command reference] - supported commands and their parameters
|
||||
|
||||
<!-- invisible references list -->
|
||||
[Doxygen]: http://www.stack.nl/~dimitri/doxygen/index.html)
|
||||
[Doxygen download]: http://www.stack.nl/~dimitri/doxygen/download.html
|
||||
[Doxygen installation]: http://www.stack.nl/~dimitri/doxygen/manual/install.html
|
||||
[Documenting basics]: http://www.stack.nl/~dimitri/doxygen/manual/docblocks.html
|
||||
[Markdown support]: http://www.stack.nl/~dimitri/doxygen/manual/markdown.html
|
||||
[Formulas support]: http://www.stack.nl/~dimitri/doxygen/manual/formulas.html
|
||||
[Doxygen]: http://www.doxygen.nl
|
||||
[Doxygen download]: http://doxygen.nl/download.html
|
||||
[Doxygen installation]: http://doxygen.nl/manual/install.html
|
||||
[Documenting basics]: http://www.doxygen.nl/manual/docblocks.html
|
||||
[Markdown support]: http://www.doxygen.nl/manual/markdown.html
|
||||
[Formulas support]: http://www.doxygen.nl/manual/formulas.html
|
||||
[Supported formula commands]: http://docs.mathjax.org/en/latest/tex.html#supported-latex-commands
|
||||
[Command reference]: http://www.stack.nl/~dimitri/doxygen/manual/commands.html
|
||||
[Command reference]: http://www.doxygen.nl/manual/commands.html
|
||||
[Google Scholar]: http://scholar.google.ru/
|
||||
|
||||
@@ -9,7 +9,7 @@ Required Packages
|
||||
|
||||
### Getting the Cutting-edge OpenCV from Git Repository
|
||||
|
||||
Launch GIT client and clone OpenCV repository from [here](http://github.com/opencv/opencv)
|
||||
Launch Git client and clone OpenCV repository from [GitHub](http://github.com/opencv/opencv).
|
||||
|
||||
In MacOS it can be done using the following command in Terminal:
|
||||
|
||||
@@ -18,24 +18,48 @@ cd ~/<my_working _directory>
|
||||
git clone https://github.com/opencv/opencv.git
|
||||
@endcode
|
||||
|
||||
If you want to install OpenCV’s extra modules, clone the opencv_contrib repository as well:
|
||||
|
||||
@code{.bash}
|
||||
cd ~/<my_working _directory>
|
||||
git clone https://github.com/opencv/opencv_contrib.git
|
||||
@endcode
|
||||
|
||||
|
||||
Building OpenCV from Source, using CMake and Command Line
|
||||
---------------------------------------------------------
|
||||
|
||||
-# Make symbolic link for Xcode to let OpenCV build scripts find the compiler, header files etc.
|
||||
1. Make sure the xcode command line tools are installed:
|
||||
@code{.bash}
|
||||
cd /
|
||||
sudo ln -s /Applications/Xcode.app/Contents/Developer Developer
|
||||
xcode-select --install
|
||||
@endcode
|
||||
|
||||
-# Build OpenCV framework:
|
||||
2. Build OpenCV framework:
|
||||
@code{.bash}
|
||||
cd ~/<my_working_directory>
|
||||
python opencv/platforms/ios/build_framework.py ios
|
||||
@endcode
|
||||
|
||||
If everything's fine, a few minutes later you will get
|
||||
\~/\<my_working_directory\>/ios/opencv2.framework. You can add this framework to your Xcode
|
||||
projects.
|
||||
3. To install OpenCV’s extra modules, append `--contrib opencv_contrib` to the python command above. **Note:** the extra modules are not included in the iOS Pack download at [OpenCV Releases](https://opencv.org/releases/). If you want to use the extra modules (e.g. aruco), you must build OpenCV yourself and include this option:
|
||||
@code{.bash}
|
||||
cd ~/<my_working_directory>
|
||||
python opencv/platforms/ios/build_framework.py ios --contrib opencv_contrib
|
||||
@endcode
|
||||
|
||||
4. To exclude a specific module, append `--without <module_name>`. For example, to exclude the "optflow" module from opencv_contrib:
|
||||
@code{.bash}
|
||||
cd ~/<my_working_directory>
|
||||
python opencv/platforms/ios/build_framework.py ios --contrib opencv_contrib --without optflow
|
||||
@endcode
|
||||
|
||||
5. The build process can take a significant amount of time. Currently (OpenCV 3.4 and 4.1), five separate architectures are built: armv7, armv7s, and arm64 for iOS plus i386 and x86_64 for the iPhone simulator. If you want to specify the architectures to include in the framework, use the `--iphoneos_archs` and/or `--iphonesimulator_archs` options. For example, to only build arm64 for iOS and x86_64 for the simulator:
|
||||
@code{.bash}
|
||||
cd ~/<my_working_directory>
|
||||
python opencv/platforms/ios/build_framework.py ios --contrib opencv_contrib --iphoneos_archs arm64 --iphonesimulator_archs x86_64
|
||||
@endcode
|
||||
|
||||
If everything’s fine, the build process will create
|
||||
`~/<my_working_directory>/ios/opencv2.framework`. You can add this framework to your Xcode projects.
|
||||
|
||||
Further Reading
|
||||
---------------
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user