Compare commits
1235 Commits
Author | SHA1 | Date |
---|---|---|
|
dcd1d20a75 | 21 hours ago |
|
f9b79e67fd | 5 days ago |
|
024d7f73b8 | 5 days ago |
|
476fa0ab7c | 5 days ago |
|
9ea9043a0e | 5 days ago |
|
8acd884797 | 5 days ago |
|
bbcd2691ed | 5 days ago |
|
c65ffab9dd | 5 days ago |
|
99d08c30ef | 5 days ago |
|
a38e3801ae | 5 days ago |
|
a9ac0743f7 | 5 days ago |
|
a7c4356d93 | 5 days ago |
|
2baf96fe67 | 5 days ago |
|
913747e6c3 | 6 days ago |
|
0b1aa8a13a | 6 days ago |
|
b5aec7ae17 | 6 days ago |
|
6f3cae9d02 | 7 days ago |
|
7a67a142cb | 7 days ago |
|
0a6850d92b | 7 days ago |
|
42dab55bc4 | 1 week ago |
|
662d3307b0 | 1 week ago |
|
2b9c1feae5 | 1 week ago |
|
aa04fca845 | 1 week ago |
|
a72118a5a6 | 1 week ago |
|
d50b21d0d6 | 1 week ago |
|
6255fa932c | 1 week ago |
|
8aec5f30ef | 2 weeks ago |
|
c23f30ce71 | 2 weeks ago |
|
118ffdc638 | 2 weeks ago |
|
058e4b2f46 | 2 weeks ago |
|
48d61baf6f | 2 weeks ago |
|
9ff07b8c12 | 2 weeks ago |
|
69958ba1d2 | 2 weeks ago |
|
3979e03882 | 2 weeks ago |
|
b96fe6bbfd | 3 weeks ago |
|
94edf1324f | 3 weeks ago |
|
18efb1e439 | 3 weeks ago |
|
b2a1a5cde3 | 3 weeks ago |
|
cdf664b448 | 3 weeks ago |
|
e549126563 | 3 weeks ago |
|
1322ddd8b2 | 3 weeks ago |
|
139a6a5fde | 3 weeks ago |
|
9721eced21 | 3 weeks ago |
|
b425713f63 | 3 weeks ago |
|
7e3ff1b741 | 3 weeks ago |
|
284f61a86e | 3 weeks ago |
|
aa4ca70d07 | 3 weeks ago |
|
6e1655813d | 3 weeks ago |
|
8468978f19 | 3 weeks ago |
|
ce2b36422c | 3 weeks ago |
|
38bfd44bcd | 3 weeks ago |
|
5ac7838839 | 3 weeks ago |
|
86b7e63849 | 3 weeks ago |
|
adb24119ef | 3 weeks ago |
|
436058fefa | 3 weeks ago |
|
7a6497ee40 | 3 weeks ago |
|
03110ac486 | 3 weeks ago |
|
8612c66ed3 | 3 weeks ago |
|
60989b5c40 | 4 weeks ago |
|
4df8d72c42 | 4 weeks ago |
|
f3e5cda1da | 4 weeks ago |
|
6d68a33224 | 4 weeks ago |
|
9d0f93b6ff | 4 weeks ago |
|
789547b503 | 4 weeks ago |
|
514fc054a7 | 4 weeks ago |
|
afc69b9dff | 4 weeks ago |
|
d29fb0280c | 4 weeks ago |
|
eca28cd6f7 | 4 weeks ago |
|
9efab780cf | 4 weeks ago |
|
c60587661b | 4 weeks ago |
|
1b40738b71 | 4 weeks ago |
|
8bff5f0741 | 4 weeks ago |
|
3c58e63c6a | 4 weeks ago |
|
450556375e | 4 weeks ago |
|
4cb359d0fd | 4 weeks ago |
|
a39614c581 | 4 weeks ago |
|
8c702f86c6 | 4 weeks ago |
|
81d4cd700c | 4 weeks ago |
|
15873ca510 | 4 weeks ago |
|
f06a7dfcff | 4 weeks ago |
|
1fbb9ffe51 | 4 weeks ago |
|
1772027e9e | 4 weeks ago |
|
8ff8a4bb1e | 4 weeks ago |
|
c071d8ec71 | 4 weeks ago |
|
e47acced9b | 4 weeks ago |
|
4bc9005ebb | 4 weeks ago |
|
5eb8e630c6 | 4 weeks ago |
|
02e66332ea | 4 weeks ago |
|
bd3451f47c | 4 weeks ago |
|
812688ae68 | 4 weeks ago |
|
5de3b6e73e | 4 weeks ago |
|
c34a649fa5 | 4 weeks ago |
|
cf557a599a | 4 weeks ago |
|
fd15553e70 | 4 weeks ago |
|
deb8b06820 | 4 weeks ago |
|
740dc120f6 | 4 weeks ago |
|
f66955ba99 | 4 weeks ago |
|
fed87bc421 | 4 weeks ago |
|
3bec3c924b | 4 weeks ago |
|
0d97a04da0 | 4 weeks ago |
|
0a88e9b918 | 4 weeks ago |
|
e5fd96efce | 4 weeks ago |
|
621bbb9c78 | 4 weeks ago |
|
c7351b8000 | 4 weeks ago |
|
d27321974f | 4 weeks ago |
|
75864122c5 | 4 weeks ago |
|
4eb14e755c | 4 weeks ago |
|
a22f41d537 | 4 weeks ago |
|
a58544eab1 | 1 month ago |
|
ebf8312f6f | 1 month ago |
|
46ec0a4f39 | 1 month ago |
|
189ebe3f41 | 1 month ago |
|
ba42b72744 | 1 month ago |
|
92ce03d2e8 | 1 month ago |
|
553bae38d9 | 1 month ago |
|
d3b493efbc | 1 month ago |
|
90df33d1f7 | 1 month ago |
|
38edbefcda | 1 month ago |
|
707aa9bd94 | 1 month ago |
|
6239916828 | 1 month ago |
|
3f4531ecd6 | 1 month ago |
|
5945fc1d1c | 1 month ago |
|
0f54876da8 | 1 month ago |
|
f433903745 | 1 month ago |
|
f216640381 | 1 month ago |
|
afae4794d3 | 1 month ago |
|
d96f337162 | 1 month ago |
|
ba500120b4 | 1 month ago |
|
6b8e477ed1 | 1 month ago |
|
f7ae9d42d2 | 1 month ago |
|
31d65ea586 | 1 month ago |
|
911a39a22f | 1 month ago |
|
1f07b07004 | 1 month ago |
|
6bc8849d3c | 1 month ago |
|
c67bebed88 | 1 month ago |
|
f7912c8882 | 1 month ago |
|
35b31577b2 | 1 month ago |
|
f8e627abc5 | 1 month ago |
|
92412cf7d7 | 1 month ago |
|
4222c6ff8a | 1 month ago |
|
2bba3867e9 | 1 month ago |
|
5df43d28d6 | 1 month ago |
|
724db375ca | 1 month ago |
|
2b416c3f0d | 1 month ago |
|
713f69048f | 1 month ago |
|
c85a5b9371 | 1 month ago |
|
031dc97548 | 1 month ago |
|
456268c682 | 1 month ago |
|
cc55b5aa4d | 1 month ago |
|
7558b79c5e | 1 month ago |
|
0d2090153f | 1 month ago |
|
024cca3104 | 1 month ago |
|
be9f65d3a3 | 1 month ago |
|
792a694f66 | 1 month ago |
|
b916e63a00 | 1 month ago |
|
eb5af62ad3 | 1 month ago |
|
67479e0acc | 1 month ago |
|
05b9a7a5e5 | 1 month ago |
|
c1a06203e3 | 1 month ago |
|
02fc11fc22 | 1 month ago |
|
026bd1a022 | 1 month ago |
|
200131ec67 | 1 month ago |
|
0b08c08d81 | 1 month ago |
|
75f0fdc73c | 1 month ago |
|
a7f4866ac8 | 1 month ago |
|
bc823212aa | 1 month ago |
|
7e23d09814 | 1 month ago |
|
5e869d0f92 | 1 month ago |
|
c6dbc278da | 1 month ago |
|
a248c9e297 | 1 month ago |
|
185ed0abff | 1 month ago |
|
3a0ae0ab75 | 1 month ago |
|
69bc6dced9 | 1 month ago |
|
6e1625b58d | 1 month ago |
|
115a22dd47 | 1 month ago |
|
c104f26f7d | 1 month ago |
|
d36d0d916d | 1 month ago |
|
0c57bca352 | 1 month ago |
|
8abeaf9e04 | 1 month ago |
|
f0b82e235e | 1 month ago |
|
3db1ae1c08 | 1 month ago |
|
43148b5962 | 1 month ago |
|
a58e86b519 | 1 month ago |
|
a6bfbdcea2 | 1 month ago |
|
752b5fe318 | 1 month ago |
|
4ca40f0424 | 1 month ago |
|
d75ad0949e | 1 month ago |
|
e3388d77c3 | 1 month ago |
|
4067f2d6ed | 1 month ago |
|
aeb8e49b81 | 1 month ago |
|
3bc6775f84 | 1 month ago |
|
dc17a15210 | 1 month ago |
|
e4e174a083 | 1 month ago |
|
44d780a0ce | 1 month ago |
|
0097f11b69 | 1 month ago |
|
b533cd645f | 1 month ago |
|
e0063691f0 | 1 month ago |
|
31039c213d | 1 month ago |
|
86f22853ca | 1 month ago |
|
b3024a934b | 1 month ago |
|
b1b2b0d550 | 1 month ago |
|
0c1143d0d0 | 1 month ago |
|
362a23f58d | 1 month ago |
|
ce762add59 | 1 month ago |
|
8e44768f7e | 1 month ago |
|
fc7fc294eb | 1 month ago |
|
ee0a2b632e | 1 month ago |
|
d94124aa3c | 1 month ago |
|
22b81f8c46 | 1 month ago |
|
2811c694b7 | 1 month ago |
|
06f38f3ff0 | 1 month ago |
|
f8ccfbba76 | 1 month ago |
|
96b07fa223 | 1 month ago |
|
ae6d49ed99 | 1 month ago |
|
5d57a099cd | 1 month ago |
|
c8f34e9319 | 1 month ago |
|
e01b32e166 | 1 month ago |
|
db2ff686c6 | 1 month ago |
|
e022748f71 | 1 month ago |
|
c6405cf287 | 1 month ago |
|
38fe69d93b | 1 month ago |
|
e75fe1648c | 1 month ago |
|
ab438f68b8 | 1 month ago |
|
ba37692f73 | 1 month ago |
|
97c8cc3038 | 1 month ago |
|
5872857f8d | 1 month ago |
|
9c7be4fe96 | 1 month ago |
|
4e5b1b7b2a | 1 month ago |
|
cbf60b12f6 | 1 month ago |
|
9a88a2dcd2 | 1 month ago |
|
00a8de5e54 | 1 month ago |
|
1fcc231ac1 | 2 months ago |
|
667ebb60c8 | 2 months ago |
|
dcf3dffe02 | 2 months ago |
|
20c5eebd20 | 2 months ago |
|
eeabd15b87 | 2 months ago |
|
d930f65393 | 2 months ago |
|
3974564746 | 2 months ago |
|
3c5f49a563 | 2 months ago |
|
b1b70395c7 | 2 months ago |
|
21aac05206 | 2 months ago |
|
e2c4e51550 | 2 months ago |
|
75a7c17d62 | 2 months ago |
|
d9b75e2c2f | 2 months ago |
|
f90cbd0446 | 2 months ago |
|
a90617698b | 2 months ago |
|
738f4f230a | 2 months ago |
|
55bda9770e | 2 months ago |
|
e580737761 | 2 months ago |
|
c0e0b3874f | 2 months ago |
|
31391c2bab | 2 months ago |
|
025c9fb07f | 2 months ago |
|
07f7744ad8 | 2 months ago |
|
58d8c614ab | 2 months ago |
|
82e8212027 | 2 months ago |
|
32a1fbbeb1 | 2 months ago |
|
2dd6cb8f31 | 2 months ago |
|
0d8bb73f17 | 2 months ago |
|
89b263e4de | 2 months ago |
|
2d600c18e4 | 2 months ago |
|
424bd6ee88 | 2 months ago |
|
987d2e8053 | 2 months ago |
|
387673bf99 | 2 months ago |
|
8bb1c53bc2 | 2 months ago |
|
7ae8505809 | 2 months ago |
|
d2881112f2 | 2 months ago |
|
40d36e1577 | 2 months ago |
|
47ff714ae5 | 2 months ago |
|
f8784c5b26 | 2 months ago |
|
ee492d0e60 | 2 months ago |
|
8fd275a6e7 | 2 months ago |
|
a9fd536a8a | 2 months ago |
|
142fca3dd0 | 2 months ago |
|
ed35d74c94 | 2 months ago |
|
cd520ebefe | 2 months ago |
|
9ff79bbe1b | 2 months ago |
|
6598b45d3e | 2 months ago |
|
62de685e4e | 2 months ago |
|
a0c4651e97 | 2 months ago |
|
883e9f94e4 | 2 months ago |
|
44a5a32741 | 2 months ago |
|
31e33b065b | 2 months ago |
|
5a8be5883b | 2 months ago |
|
b734a9cc2a | 2 months ago |
|
be7b992bdd | 2 months ago |
|
2b06803252 | 2 months ago |
|
396d20ea3f | 2 months ago |
|
fde6514d16 | 2 months ago |
|
f6d3ccbfa1 | 2 months ago |
|
754d3bebb7 | 2 months ago |
|
a04db636c6 | 2 months ago |
|
6e379548e6 | 2 months ago |
|
38a8498dbc | 2 months ago |
|
5dae67b03b | 2 months ago |
|
b821156ca1 | 2 months ago |
|
870cffab5c | 2 months ago |
|
55c3776ac8 | 2 months ago |
|
126c1f5e1b | 2 months ago |
|
28be0c4c80 | 2 months ago |
|
8c3103eef6 | 2 months ago |
|
e59f6fbbea | 2 months ago |
|
b216ad7546 | 2 months ago |
|
c3e4ef0847 | 2 months ago |
|
101d2b945a | 2 months ago |
|
7ea3b47abb | 2 months ago |
|
cf6c5c8158 | 2 months ago |
|
b9fe6ce048 | 2 months ago |
|
138040baa8 | 2 months ago |
|
e21e409712 | 2 months ago |
|
b3f3703c74 | 2 months ago |
|
f445f5f6c8 | 2 months ago |
|
cafa30c728 | 2 months ago |
|
167878de1a | 2 months ago |
|
c51fb6924b | 2 months ago |
|
ec6a27df28 | 2 months ago |
|
11e5b98a90 | 2 months ago |
|
d4c5986943 | 2 months ago |
|
5a91bd8dcb | 2 months ago |
|
4c42820cea | 2 months ago |
|
db78ce7728 | 2 months ago |
|
b5a188a5b4 | 2 months ago |
|
90465c4ac1 | 2 months ago |
|
7ef6086f50 | 2 months ago |
|
5151f9291a | 2 months ago |
|
7037eab930 | 2 months ago |
|
2d67c94bc9 | 2 months ago |
|
39b94cee90 | 2 months ago |
|
801a9e98ba | 2 months ago |
|
868fbebb6b | 2 months ago |
|
1806c36148 | 2 months ago |
|
f2f29482a0 | 2 months ago |
|
a52255e9d9 | 2 months ago |
|
2cc92025fc | 2 months ago |
|
744057c407 | 2 months ago |
|
d23116aa57 | 2 months ago |
|
3fab6d4542 | 2 months ago |
|
21c789155c | 2 months ago |
|
11dbea7170 | 2 months ago |
|
17ae352c31 | 2 months ago |
|
31c0d81e13 | 2 months ago |
|
18ecc420ed | 2 months ago |
|
b58969cc5e | 2 months ago |
|
9bb9b6848c | 2 months ago |
|
7a557ac8fd | 2 months ago |
|
e0a23a6c3b | 2 months ago |
|
2d0fe6609b | 2 months ago |
|
7e57c3cc08 | 2 months ago |
|
0fc7ff436e | 2 months ago |
|
2009c6d472 | 2 months ago |
|
24333d1882 | 2 months ago |
|
04abdbeb61 | 2 months ago |
|
4106ce8cb3 | 2 months ago |
|
7ad52b042c | 2 months ago |
|
6925da78b1 | 2 months ago |
|
9f00a81b8e | 2 months ago |
|
1c8372d5a2 | 2 months ago |
|
1782269938 | 2 months ago |
|
8398ae8246 | 2 months ago |
|
49aff958d0 | 2 months ago |
|
b6932b116e | 2 months ago |
|
4976337878 | 2 months ago |
|
897b53e44e | 2 months ago |
|
621cf4f3ba | 2 months ago |
|
476ebbd5e0 | 2 months ago |
|
c54cecacf0 | 2 months ago |
|
9fedc2532c | 2 months ago |
|
8e9597eaf4 | 2 months ago |
|
b94039cbd6 | 2 months ago |
|
6d2f4c7535 | 2 months ago |
|
252e2575f5 | 2 months ago |
|
6587298604 | 2 months ago |
|
eb96c9c6c7 | 2 months ago |
|
d1d34ebcfc | 2 months ago |
|
3a0a8b71a0 | 2 months ago |
|
030ca9c7d7 | 2 months ago |
|
a9535f2df7 | 2 months ago |
|
1ba1b9d56a | 2 months ago |
|
7a51b96100 | 2 months ago |
|
8a7d571e0e | 2 months ago |
|
2336758523 | 2 months ago |
|
95213a4ecd | 2 months ago |
|
8f49d82dec | 2 months ago |
|
0170e21b1d | 2 months ago |
|
94ca4f77a4 | 2 months ago |
|
2af459d57a | 2 months ago |
|
1f3943b34b | 2 months ago |
|
ec5799f167 | 2 months ago |
|
9f23994521 | 2 months ago |
|
e3aa7d04eb | 2 months ago |
|
5049677133 | 2 months ago |
|
eb5aa8396a | 2 months ago |
|
d35bf3d71c | 2 months ago |
|
17e2ae7fd9 | 2 months ago |
|
8108bf4c7c | 2 months ago |
|
996cb9109f | 2 months ago |
|
164b14697c | 2 months ago |
|
74f42bcfd3 | 2 months ago |
|
3ea1fbde25 | 2 months ago |
|
2c53f2fc0a | 2 months ago |
|
a74db09f67 | 2 months ago |
|
ae213c7a9b | 2 months ago |
|
13cb6722f3 | 2 months ago |
|
820158e650 | 2 months ago |
|
78cacaeba1 | 2 months ago |
|
40e9533d31 | 2 months ago |
|
357c58112f | 2 months ago |
|
4afdbdae62 | 2 months ago |
|
79dc4e8ec7 | 2 months ago |
|
6388002df2 | 2 months ago |
|
ebe52ca252 | 2 months ago |
|
ce81c8dd4f | 2 months ago |
|
7033ad7e0b | 2 months ago |
|
9397d53107 | 2 months ago |
|
48856f0c0b | 2 months ago |
|
2d5cc27c0e | 2 months ago |
|
e3c74db577 | 2 months ago |
|
dada0e876f | 2 months ago |
|
9170d2bec0 | 2 months ago |
|
06842bca13 | 2 months ago |
|
410ded43f1 | 2 months ago |
|
1d92d624f8 | 2 months ago |
|
5a26bd6353 | 2 months ago |
|
f247a6f4e0 | 2 months ago |
|
991fc0a449 | 3 months ago |
|
ac357883ac | 3 months ago |
|
604819a8a1 | 3 months ago |
|
95c0fb4415 | 3 months ago |
|
4e10f77ce3 | 3 months ago |
|
3575377767 | 3 months ago |
|
f577a1be3d | 3 months ago |
|
0b8c29eb91 | 3 months ago |
|
4b09ea6428 | 3 months ago |
|
2b5916c9ce | 3 months ago |
|
0b5e19541e | 3 months ago |
|
bae16b9427 | 3 months ago |
|
22bc6af87c | 3 months ago |
|
8951b8cdaf | 3 months ago |
|
ae9ea20948 | 3 months ago |
|
963ac6bdaf | 3 months ago |
|
a2aef51df6 | 3 months ago |
|
182c592f98 | 3 months ago |
|
c09ed76819 | 3 months ago |
|
3b492e7ea3 | 3 months ago |
|
bd7d4132e2 | 3 months ago |
|
9c2346befd | 3 months ago |
|
385c456401 | 3 months ago |
|
c5d9e19aff | 3 months ago |
|
9a227b149e | 3 months ago |
|
49b614b7b9 | 3 months ago |
|
4033f978a5 | 3 months ago |
|
3c7e35b5ee | 3 months ago |
|
e6228f7dfa | 3 months ago |
|
8381d6a0b9 | 3 months ago |
|
a42bf5390c | 3 months ago |
|
44aeadbe2d | 3 months ago |
|
d7a9212d90 | 3 months ago |
|
c106ae42fd | 3 months ago |
|
5f4af242be | 3 months ago |
|
7212994a5f | 3 months ago |
|
09dae14725 | 3 months ago |
|
7b0c32e7b9 | 3 months ago |
|
b0c9e5f412 | 3 months ago |
|
180b2efe0b | 3 months ago |
|
bd81fd52d0 | 3 months ago |
|
eaf7bfb875 | 3 months ago |
|
82ebd92ad2 | 3 months ago |
|
ae3f25b14a | 3 months ago |
|
9017c2bd7d | 3 months ago |
|
b1071b52c5 | 3 months ago |
|
89443efa26 | 3 months ago |
|
d8defe3b63 | 3 months ago |
|
496a6fbdce | 3 months ago |
|
3a9b85c868 | 3 months ago |
|
e280e76015 | 3 months ago |
|
41208a587c | 3 months ago |
|
b381fa562f | 3 months ago |
|
0375b13c67 | 3 months ago |
|
864a8dc0dd | 3 months ago |
|
a506598b42 | 3 months ago |
|
441cdc40b1 | 3 months ago |
|
ab91aa6cef | 3 months ago |
|
b40f77076d | 3 months ago |
|
3fdf189af5 | 3 months ago |
|
447f9999eb | 3 months ago |
|
0c33cc68c4 | 3 months ago |
|
e999149187 | 3 months ago |
|
0aabc2af52 | 3 months ago |
|
c17dc6a83e | 3 months ago |
|
026508615d | 3 months ago |
|
deaf5ec7e1 | 3 months ago |
|
c8610406ef | 3 months ago |
|
3902d69997 | 3 months ago |
|
22e81844ac | 3 months ago |
|
4c426cf5ff | 3 months ago |
|
8f9fa84028 | 3 months ago |
|
1925e923e2 | 3 months ago |
|
9aa25b6d66 | 3 months ago |
|
0cb2927b13 | 3 months ago |
|
5578b2e40f | 3 months ago |
|
f0c6738887 | 3 months ago |
|
9f8e92030a | 3 months ago |
|
f5c38b5127 | 3 months ago |
|
b18b9f54e4 | 3 months ago |
|
808271c7f1 | 3 months ago |
|
21fd57061c | 3 months ago |
|
32698886d0 | 3 months ago |
|
3dbb79e94c | 3 months ago |
|
ca7e8e5acf | 3 months ago |
|
c8966009b8 | 3 months ago |
|
ccc0e0e334 | 3 months ago |
|
5d312ed1f0 | 3 months ago |
|
120d7fdde7 | 3 months ago |
|
238f0aeb4f | 3 months ago |
|
69a3997805 | 3 months ago |
|
ebcd0c4dca | 3 months ago |
|
e74c0185a5 | 3 months ago |
|
b08adad0a4 | 3 months ago |
|
955efa5389 | 3 months ago |
|
2c0b32fe64 | 3 months ago |
|
28315925ee | 3 months ago |
|
1fd8c79f58 | 3 months ago |
|
b4e7cf8fab | 3 months ago |
|
56d42d8774 | 3 months ago |
|
fe97b3c5bc | 3 months ago |
|
1583dc6a46 | 3 months ago |
|
44dc885717 | 3 months ago |
|
fb42203056 | 3 months ago |
|
5cf252b12f | 3 months ago |
|
6a80e97a43 | 3 months ago |
|
4d5020803c | 3 months ago |
|
e05bcedb6e | 3 months ago |
|
296858be26 | 3 months ago |
|
34a1c4377b | 3 months ago |
|
620af9bb26 | 3 months ago |
|
7624c2b249 | 3 months ago |
|
d939b8089e | 3 months ago |
|
99784847e3 | 3 months ago |
|
eba500f734 | 3 months ago |
|
454c5d6cc6 | 3 months ago |
|
c07508a711 | 3 months ago |
|
d51ff00473 | 3 months ago |
|
b23224415e | 3 months ago |
|
4e80dced8b | 3 months ago |
|
b579a0062d | 3 months ago |
|
2ae0368468 | 3 months ago |
|
9ad980f666 | 3 months ago |
|
0cfd48ff92 | 3 months ago |
|
0957cc3a12 | 3 months ago |
|
6dee0b0ad4 | 3 months ago |
|
d37d4e0c13 | 3 months ago |
|
c308feff90 | 3 months ago |
|
0f50e62000 | 3 months ago |
|
24321ae011 | 3 months ago |
|
bdfe3e2bd7 | 3 months ago |
|
485301e714 | 3 months ago |
|
64316cedf3 | 3 months ago |
|
b5eb21d063 | 3 months ago |
|
feb4981377 | 3 months ago |
|
4109741628 | 3 months ago |
|
fedae228b1 | 3 months ago |
|
0d92c10c60 | 3 months ago |
|
8b51cfdefb | 3 months ago |
|
a7d3266624 | 3 months ago |
|
b8416b8e1b | 3 months ago |
|
2e12a1ac84 | 3 months ago |
|
3453e2d221 | 3 months ago |
|
12197fa28d | 3 months ago |
|
a1601cd71c | 3 months ago |
|
a14c76c4ed | 3 months ago |
|
4cae89014d | 3 months ago |
|
89fe772caa | 3 months ago |
|
216a053086 | 3 months ago |
|
52608b8324 | 3 months ago |
|
5734762c07 | 3 months ago |
|
3a8ef64e2f | 3 months ago |
|
4ba4be427e | 3 months ago |
|
5c7f6abb71 | 3 months ago |
|
02663dd08b | 3 months ago |
|
fd84d84157 | 3 months ago |
|
70b795fe27 | 3 months ago |
|
d206a59d9a | 3 months ago |
|
ee93791683 | 3 months ago |
|
53578065f1 | 3 months ago |
|
6f56bf0fe3 | 3 months ago |
|
d1298663f3 | 3 months ago |
|
b82fe124d8 | 3 months ago |
|
83d9e4e6c9 | 3 months ago |
|
79e8a8ff71 | 3 months ago |
|
d9f6c6e8ac | 3 months ago |
|
c912c70572 | 3 months ago |
|
bbb78f44b9 | 3 months ago |
|
df1fc220d1 | 3 months ago |
|
4de7693aaa | 3 months ago |
|
79c828324d | 3 months ago |
|
a34e66c7ff | 3 months ago |
|
f2a965caf0 | 3 months ago |
|
1bdc190a1b | 3 months ago |
|
127b06de77 | 3 months ago |
|
d908a36d3b | 3 months ago |
|
9eafc3d2f3 | 3 months ago |
|
2167afa1d8 | 3 months ago |
|
341045651c | 3 months ago |
|
b6e58239d3 | 3 months ago |
|
b996c663a9 | 3 months ago |
|
55a01c47b6 | 3 months ago |
|
87f9559cfd | 3 months ago |
|
cdd0fcc361 | 3 months ago |
|
6d84ed0ab7 | 3 months ago |
|
ed08d8099c | 3 months ago |
|
8ddc778cc4 | 3 months ago |
|
63339c199d | 3 months ago |
|
b15f12bf5f | 3 months ago |
|
f08871da64 | 3 months ago |
|
691acaf67e | 3 months ago |
|
964fa2a91b | 3 months ago |
|
aa434414ed | 3 months ago |
|
e1721d52d7 | 3 months ago |
|
7376d4cb0f | 3 months ago |
|
614b0d4500 | 3 months ago |
|
ba223f90bd | 3 months ago |
|
0d7d3a69b6 | 3 months ago |
|
4c03d73752 | 3 months ago |
|
e88c2ddfcf | 3 months ago |
|
a6bf1e117c | 3 months ago |
|
1f6276860e | 3 months ago |
|
15fd93a73a | 3 months ago |
|
ce96fcb46c | 3 months ago |
|
995d9cc127 | 3 months ago |
|
6d1f0832c6 | 3 months ago |
|
3033c8a68d | 3 months ago |
|
cd0a6befd6 | 3 months ago |
|
d9c1cbac2f | 3 months ago |
|
f8bbdc58cf | 3 months ago |
|
05048bbf53 | 3 months ago |
|
a17f6a4278 | 3 months ago |
|
a9145e8871 | 3 months ago |
|
295aadfc16 | 3 months ago |
|
9a89445183 | 3 months ago |
|
8f595e415e | 3 months ago |
|
22741fe3e2 | 3 months ago |
|
567a4b81fd | 3 months ago |
|
c97342abc7 | 3 months ago |
|
6db86856e7 | 3 months ago |
|
d841200500 | 3 months ago |
|
fd88feeacd | 3 months ago |
|
d0288551fc | 3 months ago |
|
b1d1e3f65c | 3 months ago |
|
f3bd23680d | 3 months ago |
|
2b818f920c | 3 months ago |
|
3d6a3b6559 | 3 months ago |
|
878bae9961 | 3 months ago |
|
a6e928c2d2 | 3 months ago |
|
03a2888369 | 3 months ago |
|
fc39cd0eb1 | 3 months ago |
|
fa6d7929ff | 3 months ago |
|
9e76c216f7 | 3 months ago |
|
b8dad4ed13 | 3 months ago |
|
06843dff13 | 3 months ago |
|
9b0f50c8b3 | 3 months ago |
|
3ec9d6e711 | 3 months ago |
|
2baecd5289 | 3 months ago |
|
05d265d559 | 3 months ago |
|
369f4d7c87 | 3 months ago |
|
f2023d130d | 3 months ago |
|
293431c608 | 3 months ago |
|
65e56000cb | 3 months ago |
|
5001581438 | 3 months ago |
|
0f2b29a61e | 3 months ago |
|
f9ca260bcc | 3 months ago |
|
3633fda081 | 3 months ago |
|
4de735fb19 | 3 months ago |
|
e5c25f084e | 3 months ago |
|
8fc0354f0c | 3 months ago |
|
20aaed0bb2 | 3 months ago |
|
12035d2990 | 3 months ago |
|
ee68539dde | 3 months ago |
|
8d31b18917 | 3 months ago |
|
92e5dabee8 | 3 months ago |
|
a38f473ce8 | 3 months ago |
|
1b6c7ef372 | 3 months ago |
|
04b8ce7796 | 3 months ago |
|
8a14578952 | 3 months ago |
|
e01c19e9f8 | 3 months ago |
|
ace0d5babb | 3 months ago |
|
003193d95a | 3 months ago |
|
853e556d8a | 3 months ago |
|
2c46504b36 | 3 months ago |
|
515864c93d | 3 months ago |
|
148ce5cb57 | 3 months ago |
|
e66667eb11 | 3 months ago |
|
3db0d0387b | 4 months ago |
|
876ab5ab05 | 4 months ago |
|
d8ab5050b3 | 4 months ago |
|
78101816a4 | 4 months ago |
|
291d72b135 | 4 months ago |
|
1a36d83b29 | 4 months ago |
|
9ae985965c | 4 months ago |
|
226932ae02 | 4 months ago |
|
4e16d4ff74 | 4 months ago |
|
20736f0c56 | 4 months ago |
|
8f45e88547 | 4 months ago |
|
160dffd274 | 4 months ago |
|
2d6db87e11 | 4 months ago |
|
54e35bce2c | 4 months ago |
|
2898295236 | 4 months ago |
|
8a3da517b4 | 4 months ago |
|
bba91ff3a6 | 4 months ago |
|
1801ceb965 | 4 months ago |
|
4371f39872 | 4 months ago |
|
a8c945be2b | 4 months ago |
|
4d39dda520 | 4 months ago |
|
d69c7d257a | 4 months ago |
|
bf91569905 | 4 months ago |
|
5e93009f25 | 4 months ago |
|
95bd8ff7b3 | 4 months ago |
|
84597d0108 | 4 months ago |
|
3d4aa720d9 | 4 months ago |
|
238d572f1e | 4 months ago |
|
80e8c5e7c7 | 4 months ago |
|
63259f11c8 | 4 months ago |
|
6fdfe988a7 | 4 months ago |
|
97eb35e169 | 4 months ago |
|
cff24f65ae | 4 months ago |
|
856c1a8b10 | 4 months ago |
|
a98dc5126c | 4 months ago |
|
5a4840dcbf | 4 months ago |
|
3234185c8b | 4 months ago |
|
486805b469 | 4 months ago |
|
70ff5e7ea2 | 4 months ago |
|
e0d1cbb60a | 4 months ago |
|
a205ee22f9 | 4 months ago |
|
c64cc8ef44 | 4 months ago |
|
d8ecf26722 | 4 months ago |
|
522d0b96e3 | 4 months ago |
|
ef8efd48b0 | 4 months ago |
|
4ca8e83188 | 4 months ago |
|
52e2b5f064 | 4 months ago |
|
7fbffb2930 | 4 months ago |
|
033e632e3b | 4 months ago |
|
dabff33bbc | 4 months ago |
|
fa3bb289bf | 4 months ago |
|
cae5b6996b | 4 months ago |
|
e39f42c28a | 4 months ago |
|
6129d10749 | 4 months ago |
|
2be8a53a67 | 4 months ago |
|
76e63a8808 | 4 months ago |
|
c186ab9098 | 4 months ago |
|
ac43db4e4a | 4 months ago |
|
600c46f024 | 4 months ago |
|
46bc13a608 | 4 months ago |
|
b91432cc4d | 4 months ago |
|
e27cd3e5fe | 4 months ago |
|
d6dc5729fe | 4 months ago |
|
2d90f18220 | 4 months ago |
|
5a74b57eb1 | 4 months ago |
|
cb36acfbd6 | 4 months ago |
|
dc448adc34 | 4 months ago |
|
79aa0134a5 | 4 months ago |
|
0001c9dc5a | 4 months ago |
|
6162c220b4 | 4 months ago |
|
5275d0c272 | 4 months ago |
|
4686003763 | 4 months ago |
|
a81dfe5bcf | 4 months ago |
|
50ee50f3d9 | 4 months ago |
|
e20f442512 | 4 months ago |
|
1e71375144 | 4 months ago |
|
e1c99fa228 | 4 months ago |
|
b751429199 | 4 months ago |
|
99b0d9ef70 | 4 months ago |
|
50adc38110 | 4 months ago |
|
2fe94152e5 | 4 months ago |
|
46b1c48c5c | 4 months ago |
|
28d58199bf | 4 months ago |
|
09cd545777 | 4 months ago |
|
dc692d05b1 | 4 months ago |
|
6be75f4a8e | 4 months ago |
|
9b59489969 | 4 months ago |
|
31954a3f17 | 4 months ago |
|
b7f6631672 | 4 months ago |
|
c4d8bd7f92 | 4 months ago |
|
672159ddc5 | 4 months ago |
|
727e397efe | 4 months ago |
|
9fa531b98d | 4 months ago |
|
3c3cb9b130 | 4 months ago |
|
bcda473b40 | 4 months ago |
|
011bd762ab | 4 months ago |
|
fe174f800d | 4 months ago |
|
101c8f41a4 | 4 months ago |
|
49cdb9d3ab | 4 months ago |
|
53df77733d | 4 months ago |
|
3d3a02158e | 4 months ago |
|
e0ed5615c3 | 4 months ago |
|
85a3d99f21 | 4 months ago |
|
018ccea1f8 | 4 months ago |
|
4f1b8c3b49 | 4 months ago |
|
db208237d6 | 4 months ago |
|
7adebb3019 | 4 months ago |
|
b7bfefe3e6 | 4 months ago |
|
af9ace5183 | 4 months ago |
|
87e8853737 | 4 months ago |
|
384d999ead | 4 months ago |
|
fc3f5fafa1 | 4 months ago |
|
b597e1f4eb | 4 months ago |
|
b9ba8135cd | 4 months ago |
|
63264f8e1c | 4 months ago |
|
2ba7429b56 | 4 months ago |
|
ae75a59f51 | 4 months ago |
|
de466ba891 | 4 months ago |
|
874f7db0cd | 4 months ago |
|
30e4562f1d | 4 months ago |
|
04504794a0 | 4 months ago |
|
7d7234037f | 4 months ago |
|
f2db516071 | 4 months ago |
|
becda30777 | 4 months ago |
|
6915001d02 | 4 months ago |
|
c680c83015 | 4 months ago |
|
c08b8a03b1 | 4 months ago |
|
5bfd1f58ea | 4 months ago |
|
772690d57e | 4 months ago |
|
5000fdecef | 4 months ago |
|
c91c97e5d0 | 4 months ago |
|
30360e9a05 | 4 months ago |
|
511852597c | 4 months ago |
|
2b0f921157 | 4 months ago |
|
650765e87d | 4 months ago |
|
345f9cc09d | 4 months ago |
|
055cd6cc7f | 4 months ago |
|
a8c97d93e2 | 4 months ago |
|
b5684256fe | 4 months ago |
|
e762e2b53c | 4 months ago |
|
a716856a4f | 4 months ago |
|
0bfbe32077 | 4 months ago |
|
72c936c3dd | 4 months ago |
|
278703deed | 5 months ago |
|
eba83ab37d | 5 months ago |
|
da75bd8519 | 5 months ago |
|
c307b7e56e | 5 months ago |
|
51a744cc51 | 5 months ago |
|
72c2ebebc6 | 5 months ago |
|
378bc2d0fb | 5 months ago |
|
5a8d4fdf9e | 5 months ago |
|
bbb4d8b4a3 | 5 months ago |
|
6a7295d9c3 | 5 months ago |
|
f1c6153852 | 5 months ago |
|
26725d8bdc | 5 months ago |
|
dfac4eeba6 | 5 months ago |
|
ac3e4627e9 | 5 months ago |
|
697fe83f5e | 5 months ago |
|
cdfb158437 | 5 months ago |
|
f3b2f01f8b | 5 months ago |
|
d4a561f3c2 | 5 months ago |
|
417d5c5083 | 5 months ago |
|
2c35ca4b3d | 5 months ago |
|
c408fa2e68 | 5 months ago |
|
34f0c173cb | 5 months ago |
|
5a8b7427c4 | 5 months ago |
|
a76b654593 | 5 months ago |
|
35ad7d8d25 | 5 months ago |
|
950e520fb2 | 5 months ago |
|
5cc05c373d | 5 months ago |
|
de288c860f | 5 months ago |
|
7b240fd01b | 5 months ago |
|
dc2b8c1504 | 5 months ago |
|
4e4904241c | 5 months ago |
|
b71fcebdf2 | 5 months ago |
|
7baaae19d8 | 5 months ago |
|
128a0a0003 | 5 months ago |
|
26144c195b | 5 months ago |
|
dfd970d1a9 | 5 months ago |
|
5d318b62c8 | 5 months ago |
|
9417980864 | 5 months ago |
|
a29b471633 | 5 months ago |
|
680b763ff6 | 5 months ago |
|
eb26477067 | 5 months ago |
|
8dbe00c8f9 | 5 months ago |
|
6e337a986f | 5 months ago |
|
25e465df3b | 5 months ago |
|
a423c07820 | 5 months ago |
|
e4b9245bed | 5 months ago |
|
0d9b6a1736 | 5 months ago |
|
5007405469 | 5 months ago |
|
f910187a48 | 5 months ago |
|
8b79de9208 | 5 months ago |
|
7eac44e6ce | 5 months ago |
|
c7c91af66d | 5 months ago |
|
a3599ccfc1 | 6 months ago |
|
dd35758e59 | 6 months ago |
|
ac5905f9fc | 6 months ago |
|
9231d205ca | 6 months ago |
|
0243d9b7d4 | 6 months ago |
|
a59144c97b | 6 months ago |
|
69a634edab | 6 months ago |
|
03628c4a70 | 6 months ago |
|
65f9e617e8 | 6 months ago |
|
25ae7ec464 | 6 months ago |
|
fa7b1d2bfa | 6 months ago |
|
2b6db343be | 6 months ago |
|
d5b46fb343 | 6 months ago |
|
8c1492052d | 6 months ago |
|
8dabf9aecc | 6 months ago |
|
43fde7df76 | 6 months ago |
|
2354403941 | 6 months ago |
|
3b03dce9e4 | 6 months ago |
|
4967f47036 | 6 months ago |
|
b482a184a0 | 6 months ago |
|
ef1a061fad | 6 months ago |
|
b73e58bfe7 | 6 months ago |
|
09073b86b0 | 6 months ago |
|
c2d4c3d296 | 6 months ago |
|
a6d4e065b3 | 6 months ago |
|
4cca774a09 | 6 months ago |
|
2f176a0a33 | 6 months ago |
|
e07de45f59 | 6 months ago |
|
a6134b860d | 6 months ago |
|
ebe573f0f3 | 6 months ago |
|
d4e64008bc | 6 months ago |
|
af0de16a67 | 6 months ago |
|
b5413de417 | 6 months ago |
|
a24345e01f | 6 months ago |
|
816881fd89 | 6 months ago |
|
ce4f7358b4 | 6 months ago |
|
c410e1e6a9 | 6 months ago |
|
bf9ee3aea8 | 6 months ago |
|
f0ed4adea3 | 6 months ago |
|
e0d926efb1 | 6 months ago |
|
92781524d7 | 6 months ago |
|
907836d3d6 | 6 months ago |
|
23877165ab | 6 months ago |
|
470d2be42a | 6 months ago |
|
61402ee8cb | 6 months ago |
|
23f2ab7527 | 6 months ago |
|
56ceb40f6d | 6 months ago |
|
eebf9b5a03 | 6 months ago |
|
9facbf3c55 | 6 months ago |
|
c501b689de | 6 months ago |
|
162d7624af | 6 months ago |
|
99efe9c91f | 6 months ago |
|
a3291e2dd2 | 6 months ago |
|
ddff8bdaca | 6 months ago |
|
1195bb01a1 | 6 months ago |
|
cd1e7b8a8c | 6 months ago |
|
ebe0def611 | 6 months ago |
|
a10f029175 | 6 months ago |
|
6940c89caf | 6 months ago |
|
7cc3de572b | 6 months ago |
|
48d37afa1e | 6 months ago |
|
2b26a090a0 | 6 months ago |
|
c3f7d99a0b | 6 months ago |
|
d671565f0b | 6 months ago |
|
a4deb30423 | 6 months ago |
|
7a2acd5f97 | 6 months ago |
|
3a98b0a451 | 6 months ago |
|
4af9c92440 | 6 months ago |
|
7aa553b1fa | 6 months ago |
|
fd8b9c093e | 6 months ago |
|
77aa962521 | 6 months ago |
|
7ae11e40fe | 6 months ago |
|
2ddb82a345 | 6 months ago |
|
8311ad8ba9 | 6 months ago |
|
b73c395357 | 6 months ago |
|
fc7a00d10c | 6 months ago |
|
d80ae9c119 | 6 months ago |
|
d92bc67606 | 6 months ago |
|
7d8c5d2342 | 6 months ago |
|
e2455cfd5a | 6 months ago |
|
bc01722f5d | 6 months ago |
|
feadee8ae4 | 6 months ago |
|
224659c163 | 6 months ago |
|
fa08064340 | 6 months ago |
|
6f8772d6a3 | 6 months ago |
|
09c7af6947 | 6 months ago |
|
bf3cb6416f | 6 months ago |
|
d6392eb1d3 | 6 months ago |
|
b2d2100dfc | 6 months ago |
|
ab9ba337dc | 6 months ago |
|
0b69dc75bc | 6 months ago |
|
653812fa56 | 6 months ago |
|
c2687cc87e | 6 months ago |
|
fcd78f02a6 | 6 months ago |
|
99c8e158c2 | 6 months ago |
|
56c1394bce | 6 months ago |
|
bec5b74a4f | 6 months ago |
|
dc69600477 | 6 months ago |
|
20f93c20bf | 6 months ago |
|
467d042a7b | 6 months ago |
|
99ee66139b | 6 months ago |
|
b24798af08 | 6 months ago |
|
8d05947395 | 6 months ago |
|
93864e30b9 | 6 months ago |
|
1633412d17 | 6 months ago |
|
30d69e378a | 6 months ago |
|
d435bab3c2 | 6 months ago |
|
2445315c5d | 6 months ago |
|
da14135af3 | 6 months ago |
|
06b8185a0a | 6 months ago |
|
5f48449da6 | 6 months ago |
|
6908711c3a | 6 months ago |
|
0fef92ddaf | 6 months ago |
|
4ba68b4d82 | 6 months ago |
|
f4a93cae9a | 6 months ago |
|
96a1221a53 | 6 months ago |
|
76e6b91e7e | 6 months ago |
|
e664a5f766 | 6 months ago |
|
a07e656a56 | 6 months ago |
|
66a4fa7f00 | 6 months ago |
|
77184e796d | 6 months ago |
|
6bd0ebf5b1 | 6 months ago |
|
ae02d3b366 | 6 months ago |
|
e943c79efc | 6 months ago |
|
dc4b2fbeed | 6 months ago |
|
bccff01bbd | 6 months ago |
|
8332dd98ef | 6 months ago |
|
fed405d585 | 6 months ago |
|
601c47b11b | 6 months ago |
|
7a35404099 | 6 months ago |
|
24856c5fb4 | 6 months ago |
|
a5603100f8 | 6 months ago |
|
3ccd293f4a | 6 months ago |
|
973000aa6b | 6 months ago |
|
af475d2795 | 6 months ago |
|
70f6f69fc3 | 6 months ago |
|
bca5c93430 | 6 months ago |
|
ce5e543f6b | 6 months ago |
|
a282111810 | 6 months ago |
|
9767036d54 | 6 months ago |
|
30b57fcce5 | 6 months ago |
|
8122341f79 | 6 months ago |
|
a43965639a | 6 months ago |
|
30a1706af5 | 6 months ago |
|
821dd34c18 | 6 months ago |
|
c2aa754b4b | 6 months ago |
|
915259ba2f | 6 months ago |
|
dfcb3623a9 | 6 months ago |
|
78ceadc24e | 6 months ago |
|
a7b7623c93 | 6 months ago |
|
d3a6903f5e | 6 months ago |
|
101f136036 | 6 months ago |
|
517323a28e | 6 months ago |
|
c2c6a2a10f | 6 months ago |
|
5f1f788694 | 6 months ago |
|
2e3cd76f01 | 6 months ago |
|
bd125e73fb | 6 months ago |
|
c98472d1fb | 6 months ago |
|
9cb881ce0a | 6 months ago |
|
856dc1c239 | 6 months ago |
|
76e6770cac | 6 months ago |
|
cf9e7b8264 | 6 months ago |
|
1fe9648704 | 6 months ago |
|
7a784a89e0 | 6 months ago |
|
1b96064578 | 6 months ago |
|
b236eb40d2 | 6 months ago |
|
58b01573ad | 6 months ago |
|
e59497e159 | 6 months ago |
|
187d715cd5 | 6 months ago |
|
e7a0a3b290 | 6 months ago |
|
11bab5a631 | 6 months ago |
|
87eac6bd08 | 6 months ago |
|
dbbfd96de2 | 6 months ago |
|
4f77f847b1 | 6 months ago |
|
3e26f35c65 | 6 months ago |
|
98c99e016c | 6 months ago |
|
ceb89a0c70 | 6 months ago |
|
bd706d1cf4 | 6 months ago |
|
385ae48185 | 6 months ago |
|
f1cfe67f72 | 6 months ago |
|
2fead34087 | 6 months ago |
|
7e48c76d33 | 6 months ago |
|
8c9ff7f2ba | 6 months ago |
|
ef3cc6d726 | 6 months ago |
|
da6074e2ec | 6 months ago |
|
01a056da4b | 6 months ago |
|
f10c33a5f9 | 6 months ago |
|
879801088d | 6 months ago |
|
f5590a708a | 7 months ago |
|
2694aec84a | 7 months ago |
|
e90e9a3d4e | 7 months ago |
|
7cf1055452 | 7 months ago |
|
3fa3a53507 | 7 months ago |
|
57c028c877 | 7 months ago |
|
f8b02ee28c | 7 months ago |
|
11f1dcd2f6 | 7 months ago |
|
4bddca2cd2 | 7 months ago |
|
251e120676 | 7 months ago |
|
096c253dbf | 7 months ago |
|
90e11f8763 | 7 months ago |
|
c271753dec | 7 months ago |
|
2a8a9e6074 | 7 months ago |
|
6b4076785f | 7 months ago |
|
25e459faea | 7 months ago |
|
01c8767a29 | 7 months ago |
|
5d844da1c4 | 7 months ago |
|
b7a7221e31 | 7 months ago |
|
7c16733906 | 7 months ago |
|
9ebbe19c4f | 7 months ago |
|
0ba0901619 | 7 months ago |
|
ce39427eda | 7 months ago |
|
05208d202f | 7 months ago |
|
fd2b54aef1 | 7 months ago |
|
39493ec75b | 7 months ago |
|
cd220aa256 | 7 months ago |
|
36750d6e85 | 7 months ago |
|
36d2188ce9 | 7 months ago |
|
eb13dea4fa | 7 months ago |
|
56b53eb8d4 | 7 months ago |
|
0b8fc17c22 | 7 months ago |
|
1728e08f7a | 7 months ago |
|
1e47fe04e6 | 7 months ago |
|
8cdcbc690e | 7 months ago |
|
30e9987c9e | 7 months ago |
|
d1571fad00 | 7 months ago |
|
02037ac34f | 7 months ago |
|
e7da756eec | 7 months ago |
|
fefa90b01f | 7 months ago |
|
c6eee57115 | 7 months ago |
|
80c9ed157a | 7 months ago |
|
64b75e994d | 7 months ago |
|
140726808f | 7 months ago |
|
9617743566 | 7 months ago |
|
1e72f8e4c4 | 7 months ago |
|
e2c0cff2e3 | 7 months ago |
|
e06b9178bf | 7 months ago |
|
83dfd4fb7c | 7 months ago |
|
70075a0ecc | 7 months ago |
|
7bdde7b70d | 7 months ago |
|
69536dc45d | 7 months ago |
|
c098420f1e | 7 months ago |
|
a71841c725 | 7 months ago |
|
53cd58bbe1 | 7 months ago |
|
002cb79223 | 7 months ago |
|
a15a0f6ed6 | 7 months ago |
|
f6d5f38bfc | 7 months ago |
|
2653290682 | 7 months ago |
|
a7e4fabe13 | 7 months ago |
|
9e260d2b93 | 7 months ago |
|
494b26d92d | 7 months ago |
|
1b0d0f421f | 7 months ago |
|
7cfe23f078 | 7 months ago |
|
7cd424953a | 7 months ago |
|
1f18a20b14 | 7 months ago |
|
99644acba9 | 7 months ago |
|
539b701460 | 7 months ago |
|
7978dd32a7 | 7 months ago |
|
8a91ac73ce | 7 months ago |
|
6dceb6f70f | 7 months ago |
|
3bdd0ce344 | 7 months ago |
|
4bad6887f3 | 7 months ago |
|
14b3777128 | 7 months ago |
|
92e5473ca3 | 7 months ago |
|
35aa8bf2b0 | 7 months ago |
|
b70a6a2999 | 7 months ago |
|
cc6a91d739 | 7 months ago |
|
36b02a4cf1 | 7 months ago |
|
8376c1b1e3 | 7 months ago |
|
4db0d5e092 | 7 months ago |
|
9a3edcd087 | 7 months ago |
|
78b00b42e9 | 7 months ago |
|
5213b007d6 | 8 months ago |
|
edda95c35d | 8 months ago |
|
d142253bfa | 8 months ago |
|
63db3248e2 | 8 months ago |
|
6ffdb1bd22 | 8 months ago |
|
9dc4c8ebfb | 8 months ago |
|
94a2c4a480 | 8 months ago |
|
7bd68ea40f | 8 months ago |
|
961ce1d768 | 8 months ago |
|
2961c8d39e | 8 months ago |
|
48fee82987 | 8 months ago |
|
e168cf27de | 8 months ago |
|
1292427410 | 8 months ago |
|
2bbb51184a | 8 months ago |
|
3fcdc8b7d7 | 8 months ago |
|
32573af5b0 | 8 months ago |
|
73fd656cb6 | 8 months ago |
|
e3150e8f1a | 8 months ago |
|
3aa81b4ed1 | 8 months ago |
|
b7c329548e | 8 months ago |
|
827890deed | 8 months ago |
|
091dfc86f0 | 8 months ago |
|
ec09fc8dff | 8 months ago |
|
275961e581 | 8 months ago |
|
9f4bf01718 | 8 months ago |
|
3c65c5d5a9 | 8 months ago |
|
823ad5a992 | 8 months ago |
|
cf0f3f52d3 | 8 months ago |
|
358505aeb0 | 8 months ago |
|
370e89f802 | 8 months ago |
|
0dd32ce9d3 | 8 months ago |
|
83d3376f72 | 8 months ago |
|
cbf3dce87e | 8 months ago |
|
0779d47b36 | 8 months ago |
|
6d421a6cc1 | 9 months ago |
|
f95a79ba89 | 9 months ago |
|
a068cf6815 | 9 months ago |
|
3fee94e03e | 9 months ago |
|
562d49382c | 9 months ago |
|
54ddc6f9d6 | 9 months ago |
|
21018ae898 | 9 months ago |
|
0c7fa1ab70 | 9 months ago |
|
43c6fe8ec1 | 9 months ago |
|
8dab92b04d | 9 months ago |
|
72f595b7a3 | 9 months ago |
|
9c9a122034 | 9 months ago |
|
cf77fb46da | 9 months ago |
|
33030ca2b7 | 9 months ago |
|
160b599c6e | 9 months ago |
|
6ceeba91cf | 9 months ago |
|
be1834e132 | 9 months ago |
|
4396e9c4e9 | 9 months ago |
|
9116ca8bc9 | 9 months ago |
|
b073ab05b3 | 9 months ago |
|
3b0a857a52 | 9 months ago |
|
81f6dede64 | 9 months ago |
|
93fcd7c49e | 9 months ago |
|
6bbf1b6195 | 9 months ago |
|
1fdbc1701e | 9 months ago |
|
965c3dbd5e | 9 months ago |
|
c381da7947 | 9 months ago |
|
0cd8952824 | 9 months ago |
|
116af6163d | 9 months ago |
|
835ca6ae72 | 9 months ago |
|
23c3a482ee | 9 months ago |
|
7a9824b4a6 | 9 months ago |
|
b52b2985fe | 9 months ago |
|
7c9c0a14cb | 9 months ago |
|
54f89afc4f | 9 months ago |
|
bba323a08a | 9 months ago |
|
4a154984f4 | 9 months ago |
|
eee22756a3 | 9 months ago |
|
c80c9e90f2 | 9 months ago |
|
65d7ec6ff7 | 9 months ago |
|
f8039312c5 | 9 months ago |
|
e3eccd603f | 9 months ago |
|
9871b4d8fb | 9 months ago |
|
5b9b14555a | 9 months ago |
|
a1c4739f6d | 9 months ago |
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -0,0 +1,227 @@
|
||||
#!/system/bin/sh
|
||||
|
||||
# ==============================================
|
||||
# Configuration parameters - modify as needed
|
||||
# ==============================================
|
||||
ETH_IP="192.168.68.91" # Ethernet IP address
|
||||
ETH_NETMASK="24" # Subnet mask (CIDR format)
|
||||
ETH_NETWORK="192.168.68.0" # Network address
|
||||
ETH_BROADCAST="192.168.68.255" # Broadcast address
|
||||
ETH_GATEWAY="192.168.68.1" # Default gateway
|
||||
ROUTE_TABLE="20" # Routing table number
|
||||
MAX_INIT_WAIT=150 # Maximum seconds to wait for ethernet interface
|
||||
MAX_UP_WAIT=10 # Maximum seconds to wait for interface to come UP
|
||||
MAX_ROUTE_WAIT=5 # Maximum seconds to wait for routing rules
|
||||
|
||||
# For debugging only - comment out in production
|
||||
# set -x
|
||||
|
||||
ANDROID_VERSION=$(getprop ro.build.version.release 2>/dev/null | cut -d '.' -f1)
|
||||
|
||||
# Record script start time
|
||||
SCRIPT_START=$(date +%s)
|
||||
|
||||
# Cleanup function - handles unexpected interruptions
|
||||
cleanup() {
|
||||
echo "Script interrupted, cleaning up..." >&2
|
||||
# Add additional cleanup code here if needed
|
||||
exit 1
|
||||
}
|
||||
trap cleanup INT TERM
|
||||
|
||||
# Get script directory for finding tools like ethtool
|
||||
SCRIPT_PATH="$0"
|
||||
# Ensure path is absolute
|
||||
case "$SCRIPT_PATH" in
|
||||
/*) ;; # Already absolute path
|
||||
*) SCRIPT_PATH="$PWD/$SCRIPT_PATH" ;;
|
||||
esac
|
||||
SCRIPT_DIR=$(dirname "$SCRIPT_PATH")
|
||||
echo "Script directory detected as: $SCRIPT_DIR"
|
||||
|
||||
# Only configure rp_filter for eth0 interface
|
||||
echo 0 > /proc/sys/net/ipv4/conf/eth0/rp_filter 2>/dev/null || true
|
||||
|
||||
# Wait for eth0 interface to appear
|
||||
WAITED=0
|
||||
while [ $WAITED -lt $MAX_INIT_WAIT ]; do
|
||||
if [ -d "/sys/class/net/eth0" ]; then
|
||||
echo "eth0 found after $WAITED seconds"
|
||||
break
|
||||
fi
|
||||
echo "Wait eth0... ($WAITED/$MAX_INIT_WAIT)"
|
||||
sleep 0.1
|
||||
WAITED=$((WAITED+1))
|
||||
done
|
||||
|
||||
# Check if eth0 exists
|
||||
if ! [ -d "/sys/class/net/eth0" ]; then
|
||||
echo "Error: eth0 not exists" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Check physical connection status
|
||||
if [ -f "/sys/class/net/eth0/carrier" ]; then
|
||||
CARRIER=$(cat /sys/class/net/eth0/carrier)
|
||||
echo "Physical connection status: $CARRIER (1=connected, 0=disconnected)"
|
||||
if [ "$CARRIER" != "1" ]; then
|
||||
echo "Warning: Ethernet physical connection may have issues, please check the cable" >&2
|
||||
fi
|
||||
fi
|
||||
|
||||
# Clear previous configuration
|
||||
/system/bin/ip link set eth0 down
|
||||
/system/bin/ip addr flush dev eth0
|
||||
/system/bin/ip route flush dev eth0
|
||||
/system/bin/ip route flush table $ROUTE_TABLE
|
||||
/system/bin/ip rule del to $ETH_NETWORK/$ETH_NETMASK 2>/dev/null || true
|
||||
|
||||
# Configure physical layer with ethtool (while interface is DOWN)
|
||||
if [ -x "$SCRIPT_DIR/ethtool" ]; then
|
||||
echo "Using ethtool from script directory: $SCRIPT_DIR/ethtool"
|
||||
"$SCRIPT_DIR/ethtool" -s eth0 speed 10 duplex full autoneg off
|
||||
# Try alternative path next
|
||||
elif [ -x "/data/data/com.xypower.mpapp/files/ethtool" ]; then
|
||||
echo "Configuring eth0 to 10Mbps full duplex..."
|
||||
/data/data/com.xypower.mpapp/files/ethtool -s eth0 speed 10 duplex full autoneg off
|
||||
else
|
||||
echo "Warning: ethtool not found, falling back to sysfs configuration" >&2
|
||||
# Try sysfs configuration as fallback
|
||||
if [ -f "/sys/class/net/eth0/speed" ]; then
|
||||
echo "off" > /sys/class/net/eth0/autoneg 2>/dev/null || true
|
||||
echo "10" > /sys/class/net/eth0/speed 2>/dev/null || true
|
||||
echo "full" > /sys/class/net/eth0/duplex 2>/dev/null || true
|
||||
fi
|
||||
fi
|
||||
|
||||
# ====================================================
|
||||
# MTK Android 9 IP configuration with loss prevention
|
||||
# ====================================================
|
||||
|
||||
# Configure IP address first while interface is DOWN
|
||||
echo "Setting IP address while interface is DOWN..."
|
||||
/system/bin/ip addr add $ETH_IP/$ETH_NETMASK broadcast $ETH_BROADCAST dev eth0
|
||||
PRE_UP_IP=$(/system/bin/ip addr show eth0 | grep -c "inet $ETH_IP")
|
||||
echo "IP configuration before UP: $PRE_UP_IP (1=configured, 0=missing)"
|
||||
|
||||
# Enable interface and wait for UP
|
||||
echo "Bringing up interface..."
|
||||
/system/bin/ip link set eth0 up
|
||||
if [ "$ANDROID_VERSION" = "9" ]; then
|
||||
sleep 3
|
||||
else
|
||||
# Use standard configuration for other devices
|
||||
sleep 1
|
||||
fi
|
||||
|
||||
# Check if IP was lost after interface UP (common issue on MTK devices)
|
||||
POST_UP_IP=$(/system/bin/ip addr show eth0 | grep -c "inet $ETH_IP")
|
||||
echo "IP configuration after UP: $POST_UP_IP (1=retained, 0=lost)"
|
||||
|
||||
# IP address lost detection and recovery
|
||||
if [ "$PRE_UP_IP" = "1" ] && [ "$POST_UP_IP" = "0" ]; then
|
||||
echo "Warning: IP address was lost after bringing interface up - MTK issue detected"
|
||||
echo "Reapplying IP configuration..."
|
||||
/system/bin/ip addr add $ETH_IP/$ETH_NETMASK broadcast $ETH_BROADCAST dev eth0
|
||||
|
||||
# Check if reapplied configuration worked
|
||||
FIXED_IP=$(/system/bin/ip addr show eth0 | grep -c "inet $ETH_IP")
|
||||
echo "IP reapplication result: $FIXED_IP (1=success, 0=still missing)"
|
||||
|
||||
# If standard method fails, try MTK-specific approaches
|
||||
if [ "$FIXED_IP" = "0" ]; then
|
||||
echo "Standard IP configuration failed, trying MTK-specific methods"
|
||||
|
||||
# Try ifconfig if available (works better on some MTK devices)
|
||||
if command -v ifconfig >/dev/null 2>&1; then
|
||||
echo "Using ifconfig method..."
|
||||
ifconfig eth0 $ETH_IP netmask 255.255.255.0 up
|
||||
sleep 1
|
||||
fi
|
||||
|
||||
# Try Android's netd service if available
|
||||
if [ -x "/system/bin/ndc" ]; then
|
||||
echo "Using MTK netd service..."
|
||||
/system/bin/ndc network interface setcfg eth0 $ETH_IP 255.255.255.0 up
|
||||
sleep 1
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
# Use loop to wait for interface UP instead of fixed sleep
|
||||
WAITED=0
|
||||
while [ $WAITED -lt $MAX_UP_WAIT ]; do
|
||||
# Check both link status and IP configuration
|
||||
IF_STATUS=$(/system/bin/ip link show eth0 | grep -c ",UP")
|
||||
IP_STATUS=$(/system/bin/ip addr show eth0 | grep -c "inet $ETH_IP")
|
||||
|
||||
if [ "$IF_STATUS" = "1" ] && [ "$IP_STATUS" = "1" ]; then
|
||||
echo "Interface is UP with correct IP after $WAITED seconds"
|
||||
break
|
||||
fi
|
||||
|
||||
echo "Waiting for interface UP with IP... ($WAITED/$MAX_UP_WAIT)"
|
||||
|
||||
# If interface is UP but IP is missing, reapply IP
|
||||
if [ "$IF_STATUS" = "1" ] && [ "$IP_STATUS" = "0" ]; then
|
||||
echo "Interface UP but IP missing, reapplying IP..."
|
||||
/system/bin/ip addr add $ETH_IP/$ETH_NETMASK broadcast $ETH_BROADCAST dev eth0
|
||||
fi
|
||||
|
||||
sleep 0.5
|
||||
WAITED=$((WAITED+1))
|
||||
done
|
||||
|
||||
# Final status check
|
||||
FINAL_IF_STATUS=$(/system/bin/ip link show eth0 | grep -c ",UP")
|
||||
FINAL_IP_STATUS=$(/system/bin/ip addr show eth0 | grep -c "inet $ETH_IP")
|
||||
|
||||
if [ "$FINAL_IF_STATUS" != "1" ] || [ "$FINAL_IP_STATUS" != "1" ]; then
|
||||
echo "Warning: Failed to achieve stable interface state with IP" >&2
|
||||
echo "Final interface status: $FINAL_IF_STATUS (1=UP, 0=DOWN)"
|
||||
echo "Final IP status: $FINAL_IP_STATUS (1=configured, 0=missing)"
|
||||
/system/bin/ip addr show eth0
|
||||
else
|
||||
echo "Successfully configured eth0 with IP $ETH_IP"
|
||||
fi
|
||||
|
||||
# First add to main routing table
|
||||
/system/bin/ip route add $ETH_NETWORK/$ETH_NETMASK dev eth0 proto static scope link
|
||||
|
||||
# Then add to specified routing table
|
||||
/system/bin/ip route add $ETH_NETWORK/$ETH_NETMASK dev eth0 proto static scope link table $ROUTE_TABLE
|
||||
ADD_ROUTE_STATUS=$?
|
||||
|
||||
if [ $ADD_ROUTE_STATUS -eq 0 ]; then
|
||||
echo "Add route successfully"
|
||||
else
|
||||
echo "Failed to add route: $ADD_ROUTE_STATUS" >&2
|
||||
fi
|
||||
|
||||
# Only clear ARP and neighbor cache for eth0
|
||||
/system/bin/ip neigh flush dev eth0
|
||||
|
||||
# Add routing rules - only flush cache once after rule is added
|
||||
/system/bin/ip rule add from all to $ETH_NETWORK/$ETH_NETMASK lookup $ROUTE_TABLE prio 1000
|
||||
/system/bin/ip route flush cache dev eth0
|
||||
|
||||
# Only enable forwarding for eth0 interface
|
||||
echo 1 > /proc/sys/net/ipv4/conf/eth0/forwarding 2>/dev/null || true
|
||||
|
||||
# Wait for routing rules to take effect - using loop check instead of fixed wait
|
||||
WAITED=0
|
||||
while [ $WAITED -lt $MAX_ROUTE_WAIT ]; do
|
||||
if /system/bin/ip rule | grep -q "$ETH_NETWORK/$ETH_NETMASK"; then
|
||||
echo "Routing rules are now effective after $WAITED seconds"
|
||||
break
|
||||
fi
|
||||
echo "Waiting for routing rules to take effect... ($WAITED/$MAX_ROUTE_WAIT)"
|
||||
sleep 0.5
|
||||
WAITED=$((WAITED+1))
|
||||
done
|
||||
|
||||
# Display execution time
|
||||
SCRIPT_END=$(date +%s)
|
||||
TOTAL_TIME=$((SCRIPT_END - SCRIPT_START))
|
||||
echo "Total script execution time: $TOTAL_TIME seconds"
|
||||
exit 0
|
Binary file not shown.
Binary file not shown.
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,332 @@
|
||||
//#define LOG_NDEBUG 0
|
||||
#define LOG_TAG "DngCreator_JNI"
|
||||
#include <inttypes.h>
|
||||
#include <string.h>
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
#include <cmath>
|
||||
#include <algorithm>
|
||||
|
||||
#include <camera/NdkCameraMetadata.h>
|
||||
#include <img_utils/DngUtils.h>
|
||||
#include <img_utils/TagDefinitions.h>
|
||||
#include <img_utils/TiffIfd.h>
|
||||
#include <img_utils/TiffWriter.h>
|
||||
#include <img_utils/Output.h>
|
||||
#include <img_utils/Input.h>
|
||||
#include <img_utils/StripSource.h>
|
||||
#include <sys/system_properties.h>
|
||||
|
||||
// #include "core_jni_helpers.h"
|
||||
|
||||
// #include "android_runtime/AndroidRuntime.h"
|
||||
// #include "android_runtime/android_hardware_camera2_CameraMetadata.h"
|
||||
|
||||
#include <jni.h>
|
||||
// #include <nativehelper/JNIHelp.h>
|
||||
|
||||
using namespace android;
|
||||
using namespace img_utils;
|
||||
// using android::base::GetProperty;
|
||||
|
||||
|
||||
/**
|
||||
* Max width or height dimension for thumbnails.
|
||||
*/
|
||||
// max pixel dimension for TIFF/EP
|
||||
#define MAX_THUMBNAIL_DIMENSION 256
|
||||
|
||||
|
||||
// bytes per sample
|
||||
#define DEFAULT_PIXEL_STRIDE 2
|
||||
// byts per pixel
|
||||
#define BYTES_PER_RGB_PIX 3
|
||||
|
||||
|
||||
#define GPS_LAT_REF_NORTH "N"
|
||||
#define GPS_LAT_REF_SOUTH "S"
|
||||
#define GPS_LONG_REF_EAST "E"
|
||||
#define GPS_LONG_REF_WEST "W"
|
||||
|
||||
#define GPS_DATE_FORMAT_STR "yyyy:MM:dd"
|
||||
#define TIFF_DATETIME_FORMAT "yyyy:MM:dd kk:mm:ss"
|
||||
|
||||
class ByteVectorOutput : public Output {
|
||||
public:
|
||||
ByteVectorOutput(std::vector<uint8_t>& buf);
|
||||
virtual ~ByteVectorOutput();
|
||||
|
||||
virtual status_t open();
|
||||
|
||||
virtual status_t write(const uint8_t* buf, size_t offset, size_t count);
|
||||
|
||||
virtual status_t close();
|
||||
|
||||
protected:
|
||||
std::vector<uint8_t>& m_buf;
|
||||
};
|
||||
|
||||
class ByteVectorInput : public Input {
|
||||
public:
|
||||
ByteVectorInput(const std::vector<uint8_t>& buf);
|
||||
virtual ~ByteVectorInput();
|
||||
|
||||
/**
|
||||
* Open this Input.
|
||||
*
|
||||
* Returns OK on success, or a negative error code.
|
||||
*/
|
||||
status_t open();
|
||||
|
||||
/**
|
||||
* Read bytes into the given buffer. At most, the number of bytes given in the
|
||||
* count argument will be read. Bytes will be written into the given buffer starting
|
||||
* at the index given in the offset argument.
|
||||
*
|
||||
* Returns the number of bytes read, or NOT_ENOUGH_DATA if at the end of the file. If an
|
||||
* error has occurred, this will return a negative error code other than NOT_ENOUGH_DATA.
|
||||
*/
|
||||
ssize_t read(uint8_t* buf, size_t offset, size_t count);
|
||||
|
||||
/**
|
||||
* Skips bytes in the input.
|
||||
*
|
||||
* Returns the number of bytes skipped, or NOT_ENOUGH_DATA if at the end of the file. If an
|
||||
* error has occurred, this will return a negative error code other than NOT_ENOUGH_DATA.
|
||||
*/
|
||||
ssize_t skip(size_t count);
|
||||
|
||||
/**
|
||||
* Close the Input. It is not valid to call open on a previously closed Input.
|
||||
*
|
||||
* Returns OK on success, or a negative error code.
|
||||
*/
|
||||
status_t close();
|
||||
|
||||
protected:
|
||||
const std::vector<uint8_t>& m_buf;
|
||||
size_t m_offset;
|
||||
};
|
||||
|
||||
class ByteBufferInput : public Input {
|
||||
public:
|
||||
ByteBufferInput(const uint8_t* buf, size_t len);
|
||||
virtual ~ByteBufferInput();
|
||||
|
||||
/**
|
||||
* Open this Input.
|
||||
*
|
||||
* Returns OK on success, or a negative error code.
|
||||
*/
|
||||
status_t open();
|
||||
|
||||
/**
|
||||
* Read bytes into the given buffer. At most, the number of bytes given in the
|
||||
* count argument will be read. Bytes will be written into the given buffer starting
|
||||
* at the index given in the offset argument.
|
||||
*
|
||||
* Returns the number of bytes read, or NOT_ENOUGH_DATA if at the end of the file. If an
|
||||
* error has occurred, this will return a negative error code other than NOT_ENOUGH_DATA.
|
||||
*/
|
||||
ssize_t read(uint8_t* buf, size_t offset, size_t count);
|
||||
|
||||
/**
|
||||
* Skips bytes in the input.
|
||||
*
|
||||
* Returns the number of bytes skipped, or NOT_ENOUGH_DATA if at the end of the file. If an
|
||||
* error has occurred, this will return a negative error code other than NOT_ENOUGH_DATA.
|
||||
*/
|
||||
ssize_t skip(size_t count);
|
||||
|
||||
/**
|
||||
* Close the Input. It is not valid to call open on a previously closed Input.
|
||||
*
|
||||
* Returns OK on success, or a negative error code.
|
||||
*/
|
||||
status_t close();
|
||||
|
||||
protected:
|
||||
const uint8_t* m_buf;
|
||||
size_t m_len;
|
||||
size_t m_offset;
|
||||
};
|
||||
|
||||
struct SIZE
|
||||
{
|
||||
int width;
|
||||
int height;
|
||||
};
|
||||
|
||||
#define BAIL_IF_INVALID_RET_BOOL(expr, jnienv, tagId, writer) \
|
||||
if ((expr) != OK) { \
|
||||
return false; \
|
||||
}
|
||||
|
||||
#define BAIL_IF_INVALID_RET_NULL_SP(expr, jnienv, tagId, writer) \
|
||||
if ((expr) != OK) { \
|
||||
return nullptr; \
|
||||
}
|
||||
|
||||
#define BAIL_IF_INVALID_R(expr, jnienv, tagId, writer) \
|
||||
if ((expr) != OK) { \
|
||||
return -1; \
|
||||
}
|
||||
|
||||
#define BAIL_IF_EMPTY_RET_NULL_SP(entry, jnienv, tagId, writer) \
|
||||
if ((entry).count == 0) { \
|
||||
return nullptr; \
|
||||
}
|
||||
|
||||
#define BAIL_IF_EXPR_RET_NULL_SP(expr, jnienv, tagId, writer) \
|
||||
if (expr) { \
|
||||
return nullptr; \
|
||||
}
|
||||
|
||||
#define ANDROID_DNGCREATOR_CTX_JNI_ID "mNativeContext"
|
||||
|
||||
enum {
|
||||
BITS_PER_SAMPLE = 16,
|
||||
BYTES_PER_SAMPLE = 2,
|
||||
BYTES_PER_RGB_PIXEL = 3,
|
||||
BITS_PER_RGB_SAMPLE = 8,
|
||||
BYTES_PER_RGB_SAMPLE = 1,
|
||||
SAMPLES_PER_RGB_PIXEL = 3,
|
||||
SAMPLES_PER_RAW_PIXEL = 1,
|
||||
TIFF_IFD_0 = 0,
|
||||
TIFF_IFD_SUB1 = 1,
|
||||
TIFF_IFD_GPSINFO = 2,
|
||||
};
|
||||
|
||||
/**
|
||||
* POD container class for GPS tag data.
|
||||
*/
|
||||
class GpsData {
|
||||
public:
|
||||
enum {
|
||||
GPS_VALUE_LENGTH = 6,
|
||||
GPS_REF_LENGTH = 2,
|
||||
GPS_DATE_LENGTH = 11,
|
||||
};
|
||||
|
||||
uint32_t mLatitude[GPS_VALUE_LENGTH];
|
||||
uint32_t mLongitude[GPS_VALUE_LENGTH];
|
||||
uint32_t mTimestamp[GPS_VALUE_LENGTH];
|
||||
uint8_t mLatitudeRef[GPS_REF_LENGTH];
|
||||
uint8_t mLongitudeRef[GPS_REF_LENGTH];
|
||||
uint8_t mDate[GPS_DATE_LENGTH];
|
||||
};
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Container class for the persistent native context.
|
||||
*/
|
||||
|
||||
class NativeContext : public LightRefBase<NativeContext> {
|
||||
public:
|
||||
enum {
|
||||
DATETIME_COUNT = 20,
|
||||
};
|
||||
|
||||
NativeContext(ACameraMetadata* characteristics, ACameraMetadata* result);
|
||||
virtual ~NativeContext();
|
||||
|
||||
TiffWriter* getWriter();
|
||||
|
||||
ACameraMetadata* getCharacteristics() const;
|
||||
ACameraMetadata* getResult() const;
|
||||
|
||||
uint32_t getThumbnailWidth() const;
|
||||
uint32_t getThumbnailHeight() const;
|
||||
const uint8_t* getThumbnail() const;
|
||||
bool hasThumbnail() const;
|
||||
|
||||
bool setThumbnail(const std::vector<uint8_t>& buffer, uint32_t width, uint32_t height);
|
||||
|
||||
void setOrientation(uint16_t orientation);
|
||||
uint16_t getOrientation() const;
|
||||
|
||||
void setDescription(const std::string& desc);
|
||||
std::string getDescription() const;
|
||||
bool hasDescription() const;
|
||||
|
||||
void setGpsData(const GpsData& data);
|
||||
GpsData getGpsData() const;
|
||||
bool hasGpsData() const;
|
||||
|
||||
void setCaptureTime(const std::string& formattedCaptureTime);
|
||||
std::string getCaptureTime() const;
|
||||
bool hasCaptureTime() const;
|
||||
|
||||
protected:
|
||||
std::vector<uint8_t> mCurrentThumbnail;
|
||||
TiffWriter mWriter;
|
||||
ACameraMetadata* mCharacteristics;
|
||||
ACameraMetadata* mResult;
|
||||
uint32_t mThumbnailWidth;
|
||||
uint32_t mThumbnailHeight;
|
||||
uint16_t mOrientation;
|
||||
bool mThumbnailSet;
|
||||
bool mGpsSet;
|
||||
bool mDescriptionSet;
|
||||
bool mCaptureTimeSet;
|
||||
std::string mDescription;
|
||||
GpsData mGpsData;
|
||||
std::string mFormattedCaptureTime;
|
||||
};
|
||||
|
||||
class DngCreator : public NativeContext
|
||||
{
|
||||
|
||||
public:
|
||||
DngCreator(ACameraMetadata* characteristics, ACameraMetadata* result);
|
||||
|
||||
#if 0
|
||||
void setLocation(Location location);
|
||||
#endif
|
||||
|
||||
void writeInputStream(std::vector<uint8_t>& dngOutput, SIZE size, const std::vector<uint8_t>& pixels, long offset);
|
||||
void writeByteBuffer(std::vector<uint8_t>& dngOutput, SIZE size, const std::vector<uint8_t>& pixels, long offset);
|
||||
|
||||
#if 0
|
||||
void writeImage(OutputStream& dngOutput, AImage& pixels);
|
||||
#endif
|
||||
|
||||
void close();
|
||||
|
||||
// private static final DateFormat sExifGPSDateStamp = new SimpleDateFormat(GPS_DATE_FORMAT_STR);
|
||||
// private static final DateFormat sDateTimeStampFormat = new SimpleDateFormat(TIFF_DATETIME_FORMAT);
|
||||
#if 0
|
||||
static {
|
||||
sDateTimeStampFormat.setTimeZone(TimeZone.getDefault());
|
||||
sExifGPSDateStamp.setTimeZone(TimeZone.getTimeZone("UTC"));
|
||||
}
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Offset, rowStride, and pixelStride are given in bytes. Height and width are given in pixels.
|
||||
*/
|
||||
void writeByteBuffer(int width, int height, const std::vector<uint8_t>& pixels, std::vector<uint8_t>& dngOutput, int pixelStride, int rowStride, long offset);
|
||||
|
||||
/**
|
||||
* Generate a direct RGB {@link ByteBuffer} from a {@link Bitmap}.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Convert coordinate to EXIF GPS tag format.
|
||||
*/
|
||||
void toExifLatLong(double value, int data[6]);
|
||||
|
||||
void init(ACameraMetadata* characteristics, ACameraMetadata* result, const std::string& captureTime);
|
||||
sp<TiffWriter> setup(uint32_t imageWidth, uint32_t imageHeight);
|
||||
void destroy();
|
||||
void setGpsTags(const std::vector<int>& latTag, const std::string& latRef, const std::vector<int>& longTag, const std::string& longRef, const std::string& dateTag, const std::vector<int>& timeTag);
|
||||
void writeImage(std::vector<uint8_t>& out, uint32_t width, uint32_t height, const std::vector<uint8_t>& rawBuffer, int rowStride, int pixStride, uint64_t offset, bool isDirect);
|
||||
|
||||
void writeInputStream(std::vector<uint8_t>& out, const std::vector<uint8_t>& rawStream, uint32_t width, uint32_t height, long offset);
|
||||
|
||||
void writeInputBuffer(std::vector<uint8_t>& out, const uint8_t* rawBuffer, size_t bufferLen, uint32_t width, uint32_t height, long offset);
|
||||
|
||||
};
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -1,912 +0,0 @@
|
||||
#include "TerminalDevice.h"
|
||||
/*
|
||||
* Copyright 2018 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
#define LOG_TAG "CameraTestHelpers"
|
||||
|
||||
#include "PhoneDevice2.h"
|
||||
|
||||
#include <opencv2/opencv.hpp>
|
||||
#include <opencv2/core.hpp>
|
||||
#include <opencv2/imgproc.hpp>
|
||||
// #include <opencv2/objdetect.hpp>
|
||||
// #include <opencv2/features2d.hpp>
|
||||
|
||||
// #include <opencv2/core/types.hpp>
|
||||
#include <opencv2/core/core.hpp>
|
||||
#include <opencv2/imgproc/imgproc.hpp>
|
||||
|
||||
#include <android/log.h>
|
||||
|
||||
#include <AndroidHelper.h>
|
||||
|
||||
extern bool GetJniEnv(JavaVM *vm, JNIEnv **env, bool& didAttachThread);
|
||||
|
||||
|
||||
// This value is 2 ^ 18 - 1, and is used to clamp the RGB values before their
|
||||
// ranges
|
||||
// are normalized to eight bits.
|
||||
static const int kMaxChannelValue = 262143;
|
||||
|
||||
static inline uint32_t YUV2RGB(int nY, int nU, int nV) {
|
||||
nY -= 16;
|
||||
nU -= 128;
|
||||
nV -= 128;
|
||||
if (nY < 0) nY = 0;
|
||||
|
||||
// This is the floating point equivalent. We do the conversion in integer
|
||||
// because some Android devices do not have floating point in hardware.
|
||||
// nR = (int)(1.164 * nY + 1.596 * nV);
|
||||
// nG = (int)(1.164 * nY - 0.813 * nV - 0.391 * nU);
|
||||
// nB = (int)(1.164 * nY + 2.018 * nU);
|
||||
|
||||
int nR = (int)(1192 * nY + 1634 * nV);
|
||||
int nG = (int)(1192 * nY - 833 * nV - 400 * nU);
|
||||
int nB = (int)(1192 * nY + 2066 * nU);
|
||||
|
||||
nR = std::min(kMaxChannelValue, std::max(0, nR));
|
||||
nG = std::min(kMaxChannelValue, std::max(0, nG));
|
||||
nB = std::min(kMaxChannelValue, std::max(0, nB));
|
||||
|
||||
nR = (nR >> 10) & 0xff;
|
||||
nG = (nG >> 10) & 0xff;
|
||||
nB = (nB >> 10) & 0xff;
|
||||
|
||||
return 0xff000000 | (nR << 16) | (nG << 8) | nB;
|
||||
}
|
||||
|
||||
CPhoneDevice2::CPhoneDevice2(JavaVM* vm, jobject service)
|
||||
{
|
||||
m_vm = vm;
|
||||
JNIEnv* env = NULL;
|
||||
bool attached = false;
|
||||
bool res = GetJniEnv(m_vm, &env, attached);
|
||||
if (!res)
|
||||
{
|
||||
ALOGE("Failed to get JNI Env");
|
||||
}
|
||||
m_javaService = env->NewGlobalRef(service);
|
||||
|
||||
jclass classService = env->GetObjectClass(m_javaService);
|
||||
mRegisterTimerMid = env->GetMethodID(classService, "registerTimer", "(JI)Z");
|
||||
mRegisterHeartbeatMid = env->GetMethodID(classService, "registerHeartbeatTimer", "(I)V");
|
||||
mUnregisterTimerMid = env->GetMethodID(classService, "unregisterTimer", "(J)Z");
|
||||
mUpdateTimeMid = env->GetMethodID(classService, "updateTime", "(J)Z");
|
||||
|
||||
env->DeleteLocalRef(classService);
|
||||
|
||||
if (attached)
|
||||
{
|
||||
vm->DetachCurrentThread();
|
||||
}
|
||||
|
||||
m_timerUidFeed = time(NULL);
|
||||
presentRotation_ = 0;
|
||||
}
|
||||
|
||||
CPhoneDevice2::~CPhoneDevice2()
|
||||
{
|
||||
JNIEnv* env = NULL;
|
||||
bool attached = false;
|
||||
bool res = GetJniEnv(m_vm, &env, attached);
|
||||
if (!res)
|
||||
{
|
||||
ALOGE("Failed to get JNI Env");
|
||||
}
|
||||
env->DeleteGlobalRef(m_javaService);
|
||||
if (attached)
|
||||
{
|
||||
m_vm->DetachCurrentThread();
|
||||
}
|
||||
m_javaService = NULL;
|
||||
}
|
||||
|
||||
void CPhoneDevice2::SetListener(IListener* listener)
|
||||
{
|
||||
m_listener = listener;
|
||||
}
|
||||
|
||||
bool CPhoneDevice2::UpdateTime(time_t ts)
|
||||
{
|
||||
JNIEnv* env = NULL;
|
||||
jboolean ret = JNI_FALSE;
|
||||
bool attached = false;
|
||||
bool res = GetJniEnv(m_vm, &env, attached);
|
||||
if (!res)
|
||||
{
|
||||
ALOGE("Failed to get JNI Env");
|
||||
return false;
|
||||
}
|
||||
jlong timeInMillis = ((jlong)ts) * 1000;
|
||||
ret = env->CallBooleanMethod(m_javaService, mUpdateTimeMid, timeInMillis);
|
||||
if (attached)
|
||||
{
|
||||
m_vm->DetachCurrentThread();
|
||||
}
|
||||
|
||||
return (ret == JNI_TRUE);
|
||||
}
|
||||
|
||||
bool CPhoneDevice2::Reboot()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
IDevice::timer_uid_t CPhoneDevice2::RegisterTimer(unsigned int timerType, unsigned int timeout)
|
||||
{
|
||||
IDevice::timer_uid_t uid = m_timerUidFeed.fetch_add(1);
|
||||
|
||||
ALOGI("NDK RegTimer: uid=%lld Type=%u timeout=%u", uid, timerType, timeout);
|
||||
|
||||
JNIEnv* env = NULL;
|
||||
jboolean ret = JNI_FALSE;
|
||||
bool attached = false;
|
||||
bool res = GetJniEnv(m_vm, &env, attached);
|
||||
if (!res)
|
||||
{
|
||||
ALOGE("Failed to get JNI Env");
|
||||
return 0;
|
||||
}
|
||||
ret = env->CallBooleanMethod(m_javaService, mRegisterTimerMid, (jlong)uid, (jint)timeout);
|
||||
|
||||
if (attached)
|
||||
{
|
||||
m_vm->DetachCurrentThread();
|
||||
}
|
||||
|
||||
if (ret == JNI_TRUE)
|
||||
{
|
||||
unsigned long val = timerType;
|
||||
mTimers.insert(mTimers.end(), std::pair<IDevice::timer_uid_t, unsigned long>(uid, val));
|
||||
return uid;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool CPhoneDevice2::UnregisterTimer(IDevice::timer_uid_t uid)
|
||||
{
|
||||
JNIEnv* env = NULL;
|
||||
jboolean ret = JNI_FALSE;
|
||||
bool attached = false;
|
||||
bool res = GetJniEnv(m_vm, &env, attached);
|
||||
if (!res)
|
||||
{
|
||||
ALOGE("Failed to get JNI Env");
|
||||
return false;
|
||||
}
|
||||
ret = env->CallBooleanMethod(m_javaService, mUnregisterTimerMid, (jlong)uid);
|
||||
if (attached)
|
||||
{
|
||||
m_vm->DetachCurrentThread();
|
||||
}
|
||||
|
||||
if (ret == JNI_TRUE)
|
||||
{
|
||||
mTimers.erase(uid);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool CPhoneDevice2::FireTimer(timer_uid_t uid)
|
||||
{
|
||||
std::map<IDevice::timer_uid_t, unsigned long>::iterator it = mTimers.find(uid);
|
||||
if (it == mTimers.end())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
unsigned long timerType = it->second & 0xFFFFFFFF;
|
||||
unsigned long times = (it->second & 0xFFFFFFFF00000000) >> 32;
|
||||
times++;
|
||||
|
||||
if (timerType != 100)
|
||||
{
|
||||
int aa = 0;
|
||||
}
|
||||
it->second = timerType | (times << 32);
|
||||
|
||||
if (m_listener == NULL)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
m_listener->OnTimeout(uid, timerType, NULL, times);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
IDevice::timer_uid_t CPhoneDevice2::RegisterHeartbeat(unsigned int timerType, unsigned int timeout)
|
||||
{
|
||||
IDevice::timer_uid_t uid = m_timerUidFeed.fetch_add(1);
|
||||
|
||||
JNIEnv* env = NULL;
|
||||
jboolean ret = JNI_FALSE;
|
||||
bool attached = false;
|
||||
bool res = GetJniEnv(m_vm, &env, attached);
|
||||
if (!res)
|
||||
{
|
||||
ALOGE("Failed to get JNI Env");
|
||||
return 0;
|
||||
}
|
||||
env->CallVoidMethod(m_javaService, mRegisterHeartbeatMid, (jint)timeout);
|
||||
if (attached)
|
||||
{
|
||||
m_vm->DetachCurrentThread();
|
||||
}
|
||||
|
||||
return uid;
|
||||
}
|
||||
|
||||
bool CPhoneDevice2::TakePhoto(const IDevice::PHOTO_INFO& photoInfo, const vector<OSD_INFO>& osds, const string& path)
|
||||
{
|
||||
ALOGI("TAKE_PHOTO: CH=%u PR=%u\n", (unsigned int)photoInfo.channel, (unsigned int)photoInfo.preset);
|
||||
mPhotoInfo = photoInfo;
|
||||
mPath = path;
|
||||
|
||||
mDisplayDimension = DisplayDimension(photoInfo.width, photoInfo.height);
|
||||
|
||||
ALOGE("Image Buffer Size: %d", photoInfo.width * photoInfo.height * 4);
|
||||
imageBuffer_ = (uint8_t*)malloc(photoInfo.width * photoInfo.height * 4);
|
||||
AASSERT(imageBuffer_ != nullptr, "Failed to allocate imageBuffer_");
|
||||
|
||||
int cameraId = (int)photoInfo.channel - 1;
|
||||
|
||||
ACameraIdList *cameraIdList = NULL;
|
||||
ACameraMetadata *cameraMetadata = NULL;
|
||||
|
||||
const char *selectedCameraId = NULL;
|
||||
camera_status_t camera_status = ACAMERA_OK;
|
||||
ACameraManager *cameraManager = ACameraManager_create();
|
||||
|
||||
camera_status = ACameraManager_getCameraIdList(cameraManager, &cameraIdList);
|
||||
if (camera_status != ACAMERA_OK) {
|
||||
ALOGI("Failed to get camera id list (reason: %d)\n", camera_status);
|
||||
TakePhotoCb(false, photoInfo, path, 0);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (cameraIdList->numCameras < 1 ) {
|
||||
ALOGI("No camera device detected.\n");
|
||||
TakePhotoCb(false, photoInfo, path, 0);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (cameraIdList->numCameras <= cameraId ) {
|
||||
ALOGI("No required camera device %d detected.\n", cameraId);
|
||||
TakePhotoCb(false, photoInfo, path, 0);
|
||||
return false;
|
||||
}
|
||||
|
||||
selectedCameraId = cameraIdList->cameraIds[cameraId];
|
||||
|
||||
ALOGI("Trying to open Camera2 (id: %s, num of camera : %d)\n", selectedCameraId,
|
||||
cameraIdList->numCameras);
|
||||
|
||||
camera_status = ACameraManager_getCameraCharacteristics(cameraManager, selectedCameraId,
|
||||
&cameraMetadata);
|
||||
|
||||
if (camera_status != ACAMERA_OK) {
|
||||
ALOGI("Failed to get camera meta data of ID:%s\n", selectedCameraId);
|
||||
}
|
||||
|
||||
ACameraMetadata_const_entry face, orientation;
|
||||
camera_status = ACameraMetadata_getConstEntry(cameraMetadata, ACAMERA_LENS_FACING, &face);
|
||||
uint32_t cameraFacing_ = static_cast<int32_t>(face.data.u8[0]);
|
||||
|
||||
if (cameraFacing_ == ACAMERA_LENS_FACING_FRONT)
|
||||
{
|
||||
int aa = 0;
|
||||
}
|
||||
|
||||
camera_status = ACameraMetadata_getConstEntry(cameraMetadata, ACAMERA_SENSOR_ORIENTATION, &orientation);
|
||||
|
||||
ALOGI("====Current SENSOR_ORIENTATION: %8d", orientation.data.i32[0]);
|
||||
uint32_t cameraOrientation_ = orientation.data.i32[0];
|
||||
if (cameraOrientation_ == 90 || cameraOrientation_ == 270)
|
||||
{
|
||||
mDisplayDimension.Flip();
|
||||
}
|
||||
|
||||
ImageFormat resCap = {(int32_t)photoInfo.width, (int32_t)photoInfo.height, AIMAGE_FORMAT_YUV_420_888};
|
||||
MatchCaptureSizeRequest(cameraManager, selectedCameraId, photoInfo.width, photoInfo.height, cameraOrientation_, &resCap);
|
||||
|
||||
deviceStateCallbacks.onDisconnected = camera_device_on_disconnected;
|
||||
deviceStateCallbacks.onError = camera_device_on_error;
|
||||
|
||||
camera_status = ACameraManager_openCamera(cameraManager, selectedCameraId,
|
||||
&deviceStateCallbacks, &cameraDevice);
|
||||
|
||||
if (camera_status != ACAMERA_OK) {
|
||||
ALOGI("Failed to open camera device (id: %s)\n", selectedCameraId);
|
||||
}
|
||||
|
||||
camera_status = ACameraDevice_createCaptureRequest(cameraDevice, TEMPLATE_STILL_CAPTURE/*TEMPLATE_PREVIEW*/,
|
||||
&captureRequest);
|
||||
|
||||
if (camera_status != ACAMERA_OK) {
|
||||
ALOGI("Failed to create preview capture request (id: %s)\n", selectedCameraId);
|
||||
}
|
||||
|
||||
ACaptureSessionOutputContainer_create(&captureSessionOutputContainer);
|
||||
|
||||
captureSessionStateCallbacks.onReady = capture_session_on_ready;
|
||||
captureSessionStateCallbacks.onActive = capture_session_on_active;
|
||||
captureSessionStateCallbacks.onClosed = capture_session_on_closed;
|
||||
|
||||
ACameraMetadata_free(cameraMetadata);
|
||||
ACameraManager_deleteCameraIdList(cameraIdList);
|
||||
ACameraManager_delete(cameraManager);
|
||||
|
||||
media_status_t status;
|
||||
// status = AImageReader_new(1920, 1080, AIMAGE_FORMAT_YUV_420_888, 5, &mAImageReader);
|
||||
status = AImageReader_new(resCap.width, resCap.height, resCap.format, 5, &mAImageReader);
|
||||
if (status != AMEDIA_OK)
|
||||
{
|
||||
ALOGI("AImageReader_new error\n");
|
||||
TakePhotoCb(false, photoInfo, path, 0);
|
||||
return false;
|
||||
}
|
||||
|
||||
AImageReader_ImageListener listener{
|
||||
.context = this,
|
||||
.onImageAvailable = OnImageCallback,
|
||||
};
|
||||
AImageReader_setImageListener(mAImageReader, &listener);
|
||||
|
||||
//ANativeWindow *mNativeWindow;
|
||||
status = AImageReader_getWindow(mAImageReader, &theNativeWindow);
|
||||
if (status != AMEDIA_OK)
|
||||
{
|
||||
ALOGI("AImageReader_getWindow error\n");
|
||||
TakePhotoCb(false, photoInfo, path, 0);
|
||||
return false;
|
||||
}
|
||||
|
||||
ALOGI("Surface is prepared in %p.\n", theNativeWindow);
|
||||
// theNativeWindow
|
||||
|
||||
ACameraOutputTarget_create(theNativeWindow, &cameraOutputTarget);
|
||||
ACaptureRequest_addTarget(captureRequest, cameraOutputTarget);
|
||||
|
||||
ACaptureSessionOutput_create(theNativeWindow, &sessionOutput);
|
||||
ACaptureSessionOutputContainer_add(captureSessionOutputContainer, sessionOutput);
|
||||
|
||||
ACameraDevice_createCaptureSession(cameraDevice, captureSessionOutputContainer,
|
||||
&captureSessionStateCallbacks, &captureSession);
|
||||
|
||||
// ACameraCaptureSession_setRepeatingRequest(captureSession, NULL, 1, &captureRequest, NULL);
|
||||
ACameraCaptureSession_capture(captureSession, NULL, 1, &captureRequest, NULL);
|
||||
ALOGI("Surface is prepared in here.\n");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
ACameraCaptureSession_stateCallbacks* CPhoneDevice2::GetSessionListener()
|
||||
{
|
||||
static ACameraCaptureSession_stateCallbacks sessionListener = {
|
||||
.context = this,
|
||||
.onClosed = CPhoneDevice2::capture_session_on_closed,
|
||||
.onReady = CPhoneDevice2::capture_session_on_ready,
|
||||
.onActive = CPhoneDevice2::capture_session_on_active,
|
||||
};
|
||||
return &sessionListener;
|
||||
}
|
||||
|
||||
void CPhoneDevice2::ImageCallback(AImageReader *reader)
|
||||
{
|
||||
bool res = false;
|
||||
AImage *image = nullptr;
|
||||
media_status_t status = AImageReader_acquireNextImage(reader, &image);
|
||||
if (status == AMEDIA_OK && image)
|
||||
{
|
||||
int32_t srcFormat = -1;
|
||||
AImage_getFormat(image, &srcFormat);
|
||||
AASSERT(AIMAGE_FORMAT_YUV_420_888 == srcFormat, "Failed to get format");
|
||||
int32_t srcPlanes = 0;
|
||||
AImage_getNumberOfPlanes(image, &srcPlanes);
|
||||
AASSERT(srcPlanes == 3, "Is not 3 planes");
|
||||
|
||||
AImageCropRect srcRect;
|
||||
AImage_getCropRect(image, &srcRect);
|
||||
int32_t width = srcRect.right - srcRect.left;
|
||||
int32_t height = srcRect.bottom - srcRect.top;
|
||||
|
||||
// int32_t height = srcRect.right - srcRect.left;
|
||||
// int32_t width = srcRect.bottom - srcRect.top;
|
||||
|
||||
uint8_t *yPixel = nullptr;
|
||||
uint8_t *uPixel = nullptr;
|
||||
uint8_t *vPixel = nullptr;
|
||||
|
||||
int32_t yLen = 0;
|
||||
int32_t uLen = 0;
|
||||
int32_t vLen = 0;
|
||||
|
||||
AImage_getPlaneData(image, 0, &yPixel, &yLen);
|
||||
AImage_getPlaneData(image, 1, &uPixel, &uLen);
|
||||
AImage_getPlaneData(image, 2, &vPixel, &vLen);
|
||||
|
||||
uint8_t * data = new uint8_t[yLen + vLen + uLen];
|
||||
memcpy(data, yPixel, yLen);
|
||||
memcpy(data+yLen, vPixel, vLen);
|
||||
memcpy(data+yLen+vLen, uPixel, uLen);
|
||||
|
||||
cv::Mat mYUV = cv::Mat(((height * 3) >> 1), width, CV_8UC1, data);
|
||||
|
||||
// cv::cvtColor(mYUV, _yuv_rgb_img, cv::COLOR_YUV2RGB_NV21, 3);
|
||||
|
||||
// cv::Mat mYUV = cv::Mat(height, yStride, CV_8UC4, data);
|
||||
|
||||
cv::Mat _yuv_rgb_img(height, width, CV_8UC4), _yuv_gray_img;
|
||||
cv::cvtColor(mYUV, _yuv_rgb_img, cv::COLOR_YUV2RGB_NV21, 3);
|
||||
|
||||
cv::rotate(_yuv_rgb_img, _yuv_rgb_img, cv::ROTATE_180);
|
||||
|
||||
// cv::Mat rgbMat(height, width, CV_8UC3);
|
||||
// 通过cv::cvtColor将yuv420转换为rgb格式
|
||||
// cvtColor(_yuv_rgb_img, rgbMat, cv::COLOR_YUV2RGB_I420);
|
||||
|
||||
// cv::Mat mat = cv::Mat(buffer.height, buffer.stride, CV_8UC4, buffer.bits);
|
||||
|
||||
const char *str = "OSD";
|
||||
putText(_yuv_rgb_img, str, cv::Point(50, 50), cv::FONT_HERSHEY_COMPLEX, 1, cv::Scalar(0, 0, 0), 4,cv::LINE_AA);
|
||||
putText(_yuv_rgb_img, str, cv::Point(50, 50), cv::FONT_HERSHEY_COMPLEX, 1, cv::Scalar(255, 255, 255), 2,cv::LINE_AA);
|
||||
|
||||
vector <int> compression_params;
|
||||
compression_params.push_back(cv::IMWRITE_JPEG_QUALITY);
|
||||
compression_params.push_back(80);
|
||||
|
||||
res = cv::imwrite(mPath.c_str(), _yuv_rgb_img, compression_params);
|
||||
|
||||
// ANativeWindow_unlockAndPost(theNativeWindow);
|
||||
|
||||
if (res)
|
||||
{
|
||||
int aa = 0;
|
||||
}
|
||||
|
||||
// res = WriteFile(image, GetFileName() + ".org.jpg");
|
||||
AImage_delete(image);
|
||||
// delete pThis;
|
||||
|
||||
TakePhotoCb(res, mPhotoInfo, mPath, time(NULL));
|
||||
}
|
||||
}
|
||||
void CPhoneDevice2::OnImageCallback(void *ctx, AImageReader *reader)
|
||||
{
|
||||
CPhoneDevice2* pThis = reinterpret_cast<CPhoneDevice2*>(ctx);
|
||||
if (pThis != NULL)
|
||||
{
|
||||
pThis->ImageCallback(reader);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bool CPhoneDevice2::WriteFile(AImage *image, const string& path)
|
||||
{
|
||||
int planeCount = 0;
|
||||
media_status_t status = AImage_getNumberOfPlanes(image, &planeCount);
|
||||
|
||||
ALOGI("Info: getNumberOfPlanes() planeCount = %d", planeCount);
|
||||
if (!(status == AMEDIA_OK && planeCount == 1))
|
||||
{
|
||||
ALOGE("Error: getNumberOfPlanes() planeCount = %d", planeCount);
|
||||
return false;
|
||||
}
|
||||
|
||||
uint8_t *data = nullptr;
|
||||
int len = 0;
|
||||
AImage_getPlaneData(image, 0, &data, &len);
|
||||
|
||||
bool res = false;
|
||||
FILE *file = fopen(path.c_str(), "wb");
|
||||
if (file && data && len)
|
||||
{
|
||||
fwrite(data, 1, len, file);
|
||||
fclose(file);
|
||||
|
||||
ALOGI("Capture: %s", path.c_str());
|
||||
|
||||
|
||||
res = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (file)
|
||||
fclose(file);
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
bool CPhoneDevice2::WriteFile(CPhoneDevice2* pThis, AImage *image)
|
||||
{
|
||||
return pThis->WriteFile(image, pThis->GetFileName());
|
||||
}
|
||||
|
||||
std::string CPhoneDevice2::GetFileName() const
|
||||
{
|
||||
return mPath;
|
||||
}
|
||||
/*
|
||||
const char *selectedCameraId = NULL;
|
||||
|
||||
ACameraManager *cameraManager = ACameraManager_create();
|
||||
*/
|
||||
|
||||
bool CPhoneDevice2::MatchCaptureSizeRequest(ACameraManager *cameraManager, const char *selectedCameraId, unsigned int width, unsigned int height, uint32_t cameraOrientation_,
|
||||
ImageFormat* resCap) {
|
||||
DisplayDimension disp(resCap->width,resCap->height);
|
||||
if (cameraOrientation_ == 90 || cameraOrientation_ == 270) {
|
||||
disp.Flip();
|
||||
}
|
||||
|
||||
ACameraMetadata* metadata;
|
||||
camera_status_t camera_status = ACAMERA_OK;
|
||||
camera_status = ACameraManager_getCameraCharacteristics(cameraManager, selectedCameraId, &metadata);
|
||||
ACameraMetadata_const_entry entry;
|
||||
camera_status = ACameraMetadata_getConstEntry(metadata, ACAMERA_SCALER_AVAILABLE_STREAM_CONFIGURATIONS, &entry);
|
||||
// format of the data: format, width, height, input?, type int32
|
||||
bool foundIt = false;
|
||||
DisplayDimension foundRes(16384, 16384);
|
||||
DisplayDimension maxJPG(0, 0);
|
||||
|
||||
for (int i = 0; i < entry.count; i += 4) {
|
||||
int32_t input = entry.data.i32[i + 3];
|
||||
int32_t format = entry.data.i32[i + 0];
|
||||
if (input) continue;
|
||||
|
||||
if (format == AIMAGE_FORMAT_YUV_420_888 || format == AIMAGE_FORMAT_JPEG) {
|
||||
DisplayDimension res(entry.data.i32[i + 1], entry.data.i32[i + 2]);
|
||||
ALOGI("Camera Resolution: %d x %d fmt=%d", res.width(), res.height(), format);
|
||||
if (!disp.IsSameRatio(res)) continue;
|
||||
if (format == AIMAGE_FORMAT_YUV_420_888 && res > disp) {
|
||||
foundIt = true;
|
||||
foundRes = res;
|
||||
} else if (format == AIMAGE_FORMAT_JPEG && res > maxJPG) {
|
||||
maxJPG = res;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (foundIt) {
|
||||
// resView->width = foundRes.org_width();
|
||||
// resView->height = foundRes.org_height();
|
||||
resCap->width = foundRes.org_width();
|
||||
resCap->height = foundRes.org_height();
|
||||
} else {
|
||||
ALOGI("Did not find any compatible camera resolution, taking 640x480");
|
||||
resCap->width = disp.org_width();
|
||||
resCap->height = disp.org_height();
|
||||
// *resCap = *resView;
|
||||
}
|
||||
// resView->format = AIMAGE_FORMAT_YUV_420_888;
|
||||
// resCap->format = AIMAGE_FORMAT_JPEG;
|
||||
return foundIt;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert yuv image inside AImage into ANativeWindow_Buffer
|
||||
* ANativeWindow_Buffer format is guaranteed to be
|
||||
* WINDOW_FORMAT_RGBX_8888
|
||||
* WINDOW_FORMAT_RGBA_8888
|
||||
* @param buf a {@link ANativeWindow_Buffer } instance, destination of
|
||||
* image conversion
|
||||
* @param image a {@link AImage} instance, source of image conversion.
|
||||
* it will be deleted via {@link AImage_delete}
|
||||
*/
|
||||
bool CPhoneDevice2::DisplayImage(ANativeWindow_Buffer *buf, AImage *image) {
|
||||
AASSERT(buf->format == WINDOW_FORMAT_RGBX_8888 ||
|
||||
buf->format == WINDOW_FORMAT_RGBA_8888,
|
||||
"Not supported buffer format");
|
||||
|
||||
int32_t srcFormat = -1;
|
||||
AImage_getFormat(image, &srcFormat);
|
||||
AASSERT(AIMAGE_FORMAT_YUV_420_888 == srcFormat, "Failed to get format");
|
||||
int32_t srcPlanes = 0;
|
||||
AImage_getNumberOfPlanes(image, &srcPlanes);
|
||||
AASSERT(srcPlanes == 3, "Is not 3 planes");
|
||||
|
||||
switch (presentRotation_) {
|
||||
case 0:
|
||||
PresentImage(buf, image);
|
||||
break;
|
||||
case 90:
|
||||
PresentImage90(buf, image);
|
||||
break;
|
||||
case 180:
|
||||
PresentImage180(buf, image);
|
||||
break;
|
||||
case 270:
|
||||
PresentImage270(buf, image);
|
||||
break;
|
||||
default:
|
||||
AASSERT(0, "NOT recognized display rotation: %d", presentRotation_);
|
||||
}
|
||||
|
||||
AImage_delete(image);
|
||||
image = nullptr;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* PresentImage()
|
||||
* Converting yuv to RGB
|
||||
* No rotation: (x,y) --> (x, y)
|
||||
* Refer to:
|
||||
* https://mathbits.com/MathBits/TISection/Geometry/Transformations2.htm
|
||||
*/
|
||||
void CPhoneDevice2::PresentImage(ANativeWindow_Buffer *buf, AImage *image) {
|
||||
AImageCropRect srcRect;
|
||||
AImage_getCropRect(image, &srcRect);
|
||||
|
||||
AImage_getPlaneRowStride(image, 0, &yStride);
|
||||
AImage_getPlaneRowStride(image, 1, &uvStride);
|
||||
yPixel = imageBuffer_;
|
||||
AImage_getPlaneData(image, 0, &yPixel, &yLen);
|
||||
vPixel = imageBuffer_ + yLen;
|
||||
AImage_getPlaneData(image, 1, &vPixel, &vLen);
|
||||
uPixel = imageBuffer_ + yLen + vLen;
|
||||
AImage_getPlaneData(image, 2, &uPixel, &uLen);
|
||||
AImage_getPlanePixelStride(image, 1, &uvPixelStride);
|
||||
|
||||
int32_t rowStride;
|
||||
AImage_getPlaneRowStride(image, 0, &rowStride);
|
||||
|
||||
int32_t height = std::min(buf->height, (srcRect.bottom - srcRect.top));
|
||||
int32_t width = std::min(buf->width, (srcRect.right - srcRect.left));
|
||||
|
||||
uint32_t *out = static_cast<uint32_t *>(buf->bits);
|
||||
|
||||
for (int32_t y = 0; y < height; y++) {
|
||||
const uint8_t *pY = yPixel + yStride * (y + srcRect.top) + srcRect.left;
|
||||
|
||||
int32_t uv_row_start = uvStride * ((y + srcRect.top) >> 1);
|
||||
const uint8_t *pU = uPixel + uv_row_start + (srcRect.left >> 1);
|
||||
const uint8_t *pV = vPixel + uv_row_start + (srcRect.left >> 1);
|
||||
|
||||
for (int32_t x = 0; x < width; x++) {
|
||||
const int32_t uv_offset = (x >> 1) * uvPixelStride;
|
||||
out[x] = YUV2RGB(pY[x], pU[uv_offset], pV[uv_offset]);
|
||||
}
|
||||
out += buf->stride;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* PresentImage90()
|
||||
* Converting YUV to RGB
|
||||
* Rotation image anti-clockwise 90 degree -- (x, y) --> (-y, x)
|
||||
*/
|
||||
void CPhoneDevice2::PresentImage90(ANativeWindow_Buffer *buf, AImage *image) {
|
||||
AImageCropRect srcRect;
|
||||
AImage_getCropRect(image, &srcRect);
|
||||
|
||||
AImage_getPlaneRowStride(image, 0, &yStride);
|
||||
AImage_getPlaneRowStride(image, 1, &uvStride);
|
||||
yPixel = imageBuffer_;
|
||||
AImage_getPlaneData(image, 0, &yPixel, &yLen);
|
||||
vPixel = imageBuffer_ + yLen;
|
||||
AImage_getPlaneData(image, 1, &vPixel, &vLen);
|
||||
uPixel = imageBuffer_ + yLen + vLen;
|
||||
AImage_getPlaneData(image, 2, &uPixel, &uLen);
|
||||
AImage_getPlanePixelStride(image, 1, &uvPixelStride);
|
||||
|
||||
int32_t height = std::min(buf->width, (srcRect.bottom - srcRect.top));
|
||||
int32_t width = std::min(buf->height, (srcRect.right - srcRect.left));
|
||||
|
||||
uint32_t *out = static_cast<uint32_t *>(buf->bits);
|
||||
out += height - 1;
|
||||
for (int32_t y = 0; y < height; y++) {
|
||||
const uint8_t *pY = yPixel + yStride * (y + srcRect.top) + srcRect.left;
|
||||
|
||||
int32_t uv_row_start = uvStride * ((y + srcRect.top) >> 1);
|
||||
const uint8_t *pU = uPixel + uv_row_start + (srcRect.left >> 1);
|
||||
const uint8_t *pV = vPixel + uv_row_start + (srcRect.left >> 1);
|
||||
|
||||
for (int32_t x = 0; x < width; x++) {
|
||||
const int32_t uv_offset = (x >> 1) * uvPixelStride;
|
||||
// [x, y]--> [-y, x]
|
||||
int testb = pU[uv_offset];
|
||||
int testc = pV[uv_offset];
|
||||
int testA = pY[x];
|
||||
out[x * buf->stride] = YUV2RGB(testA, testb, testc);
|
||||
}
|
||||
out -= 1; // move to the next column
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* PresentImage180()
|
||||
* Converting yuv to RGB
|
||||
* Rotate image 180 degree: (x, y) --> (-x, -y)
|
||||
*/
|
||||
void CPhoneDevice2::PresentImage180(ANativeWindow_Buffer *buf, AImage *image) {
|
||||
AImageCropRect srcRect;
|
||||
AImage_getCropRect(image, &srcRect);
|
||||
|
||||
AImage_getPlaneRowStride(image, 0, &yStride);
|
||||
AImage_getPlaneRowStride(image, 1, &uvStride);
|
||||
yPixel = imageBuffer_;
|
||||
AImage_getPlaneData(image, 0, &yPixel, &yLen);
|
||||
vPixel = imageBuffer_ + yLen;
|
||||
AImage_getPlaneData(image, 1, &vPixel, &vLen);
|
||||
uPixel = imageBuffer_ + yLen + vLen;
|
||||
AImage_getPlaneData(image, 2, &uPixel, &uLen);
|
||||
AImage_getPlanePixelStride(image, 1, &uvPixelStride);
|
||||
|
||||
int32_t height = std::min(buf->height, (srcRect.bottom - srcRect.top));
|
||||
int32_t width = std::min(buf->width, (srcRect.right - srcRect.left));
|
||||
|
||||
uint32_t *out = static_cast<uint32_t *>(buf->bits);
|
||||
out += (height - 1) * buf->stride;
|
||||
for (int32_t y = 0; y < height; y++) {
|
||||
const uint8_t *pY = yPixel + yStride * (y + srcRect.top) + srcRect.left;
|
||||
|
||||
int32_t uv_row_start = uvStride * ((y + srcRect.top) >> 1);
|
||||
const uint8_t *pU = uPixel + uv_row_start + (srcRect.left >> 1);
|
||||
const uint8_t *pV = vPixel + uv_row_start + (srcRect.left >> 1);
|
||||
|
||||
for (int32_t x = 0; x < width; x++) {
|
||||
const int32_t uv_offset = (x >> 1) * uvPixelStride;
|
||||
// mirror image since we are using front camera
|
||||
out[width - 1 - x] = YUV2RGB(pY[x], pU[uv_offset], pV[uv_offset]);
|
||||
// out[x] = YUV2RGB(pY[x], pU[uv_offset], pV[uv_offset]);
|
||||
}
|
||||
out -= buf->stride;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* PresentImage270()
|
||||
* Converting image from YUV to RGB
|
||||
* Rotate Image counter-clockwise 270 degree: (x, y) --> (y, x)
|
||||
*/
|
||||
void CPhoneDevice2::PresentImage270(ANativeWindow_Buffer *buf, AImage *image) {
|
||||
AImageCropRect srcRect;
|
||||
AImage_getCropRect(image, &srcRect);
|
||||
|
||||
AImage_getPlaneRowStride(image, 0, &yStride);
|
||||
AImage_getPlaneRowStride(image, 1, &uvStride);
|
||||
yPixel = imageBuffer_;
|
||||
AImage_getPlaneData(image, 0, &yPixel, &yLen);
|
||||
vPixel = imageBuffer_ + yLen;
|
||||
AImage_getPlaneData(image, 1, &vPixel, &vLen);
|
||||
uPixel = imageBuffer_ + yLen + vLen;
|
||||
AImage_getPlaneData(image, 2, &uPixel, &uLen);
|
||||
AImage_getPlanePixelStride(image, 1, &uvPixelStride);
|
||||
|
||||
int32_t height = std::min(buf->width, (srcRect.bottom - srcRect.top));
|
||||
int32_t width = std::min(buf->height, (srcRect.right - srcRect.left));
|
||||
|
||||
uint32_t *out = static_cast<uint32_t *>(buf->bits);
|
||||
for (int32_t y = 0; y < height; y++) {
|
||||
const uint8_t *pY = yPixel + yStride * (y + srcRect.top) + srcRect.left;
|
||||
|
||||
int32_t uv_row_start = uvStride * ((y + srcRect.top) >> 1);
|
||||
const uint8_t *pU = uPixel + uv_row_start + (srcRect.left >> 1);
|
||||
const uint8_t *pV = vPixel + uv_row_start + (srcRect.left >> 1);
|
||||
|
||||
for (int32_t x = 0; x < width; x++) {
|
||||
const int32_t uv_offset = (x >> 1) * uvPixelStride;
|
||||
int testb = pU[uv_offset];
|
||||
int testc = pV[uv_offset];
|
||||
int testA = pY[x];
|
||||
out[(width - 1 - x) * buf->stride] =
|
||||
YUV2RGB(testA, testb, testc);
|
||||
}
|
||||
out += 1; // move to the next column
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
bool CPhoneDevice2::SendBroadcastMessage(String16 action, int value)
|
||||
{
|
||||
TM_INFO_LOG("sendBroadcastMessage(): Action: %s, Value: %d ", action.string(), value);
|
||||
sp <IServiceManager> sm = defaultServiceManager();
|
||||
sp <IBinder> am = sm->getService(String16("activity"));
|
||||
if (am != NULL) {
|
||||
Parcel data, reply;
|
||||
data.writeInterfaceToken(String16("android.app.IActivityManager"));
|
||||
data.writeStrongBinder(NULL);
|
||||
// intent begin
|
||||
data.writeString16(action); // action
|
||||
data.writeInt32(0); // URI data type
|
||||
data.writeString16(NULL, 0); // type
|
||||
data.writeInt32(0); // flags
|
||||
data.writeString16(NULL, 0); // package name
|
||||
data.writeString16(NULL, 0); // component name
|
||||
data.writeInt32(0); // source bound - size
|
||||
data.writeInt32(0); // categories - size
|
||||
data.writeInt32(0); // selector - size
|
||||
data.writeInt32(0); // clipData - size
|
||||
data.writeInt32(-2); // contentUserHint: -2 -> UserHandle.USER_CURRENT
|
||||
data.writeInt32(-1); // bundle extras length
|
||||
data.writeInt32(0x4C444E42); // 'B' 'N' 'D' 'L'
|
||||
int oldPos = data.dataPosition();
|
||||
data.writeInt32(1); // size
|
||||
// data.writeInt32(0); // VAL_STRING, need to remove because of analyze common intent
|
||||
data.writeString16(String16("type"));
|
||||
data.writeInt32(1); // VAL_INTEGER
|
||||
data.writeInt32(value);
|
||||
int newPos = data.dataPosition();
|
||||
data.setDataPosition(oldPos - 8);
|
||||
data.writeInt32(newPos - oldPos); // refill bundle extras length
|
||||
data.setDataPosition(newPos);
|
||||
// intent end
|
||||
data.writeString16(NULL, 0); // resolvedType
|
||||
data.writeStrongBinder(NULL); // resultTo
|
||||
data.writeInt32(0); // resultCode
|
||||
data.writeString16(NULL, 0); // resultData
|
||||
data.writeInt32(-1); // resultExtras
|
||||
data.writeString16(NULL, 0); // permission
|
||||
data.writeInt32(0); // appOp
|
||||
data.writeInt32(-1); // option
|
||||
data.writeInt32(1); // serialized: != 0 -> ordered
|
||||
data.writeInt32(0); // sticky
|
||||
data.writeInt32(-2); // userId: -2 -> UserHandle.USER_CURRENT
|
||||
|
||||
status_t ret = am->transact(IBinder::FIRST_CALL_TRANSACTION + 13, data,
|
||||
&reply); // BROADCAST_INTENT_TRANSACTION
|
||||
if (ret == NO_ERROR) {
|
||||
int exceptionCode = reply.readExceptionCode();
|
||||
if (exceptionCode) {
|
||||
TM_INFO_LOG("sendBroadcastMessage(%s) caught exception %d\n",
|
||||
action.string(), exceptionCode);
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
TM_INFO_LOG("getService() couldn't find activity service!\n");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
*/
|
||||
|
||||
void CPhoneDevice2::camera_device_on_disconnected(void *context, ACameraDevice *device)
|
||||
{
|
||||
ALOGI("Camera(id: %s) is diconnected.\n", ACameraDevice_getId(device));
|
||||
CPhoneDevice2* pThis = (CPhoneDevice2*)context;
|
||||
// delete pThis;
|
||||
}
|
||||
|
||||
void CPhoneDevice2::camera_device_on_error(void *context, ACameraDevice *device, int error)
|
||||
{
|
||||
ALOGI("Error(code: %d) on Camera(id: %s).\n", error, ACameraDevice_getId(device));
|
||||
}
|
||||
|
||||
void CPhoneDevice2::capture_session_on_ready(void *context, ACameraCaptureSession *session)
|
||||
{
|
||||
ALOGI("Session is ready. %p\n", session);
|
||||
}
|
||||
|
||||
void CPhoneDevice2::capture_session_on_active(void *context, ACameraCaptureSession *session)
|
||||
{
|
||||
ALOGI("Session is activated. %p\n", session);
|
||||
}
|
||||
|
||||
void CPhoneDevice2::capture_session_on_closed(void *context, ACameraCaptureSession *session)
|
||||
{
|
||||
ALOGI("Session is closed. %p\n", session);
|
||||
}
|
@ -1,124 +0,0 @@
|
||||
#ifndef __PHONE_DEVICE2_H__
|
||||
#define __PHONE_DEVICE2_H__
|
||||
|
||||
#include <stdio.h>
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
|
||||
#include <map>
|
||||
#include <atomic>
|
||||
|
||||
#include <camera/NdkCameraManager.h>
|
||||
#include <camera/NdkCameraError.h>
|
||||
#include <camera/NdkCameraDevice.h>
|
||||
#include <camera/NdkCameraMetadataTags.h>
|
||||
#include <media/NdkImageReader.h>
|
||||
#include <Client/Device.h>
|
||||
#include <string>
|
||||
|
||||
#include "camera2/Camera2Helper.h"
|
||||
|
||||
class CPhoneDevice2 : public IDevice
|
||||
{
|
||||
public:
|
||||
CPhoneDevice2(JavaVM* vm, jobject service);
|
||||
virtual ~CPhoneDevice2();
|
||||
|
||||
virtual void SetListener(IListener* listener);
|
||||
virtual bool UpdateTime(time_t ts);
|
||||
virtual bool Reboot();
|
||||
virtual timer_uid_t RegisterHeartbeat(unsigned int timerType, unsigned int timeout);
|
||||
virtual bool TakePhoto(const IDevice::PHOTO_INFO& photoInfo, const vector<OSD_INFO>& osds, const string& path);
|
||||
virtual timer_uid_t RegisterTimer(unsigned int timerType, unsigned int timeout);
|
||||
virtual bool UnregisterTimer(timer_uid_t uid);
|
||||
|
||||
virtual bool FireTimer(timer_uid_t uid);
|
||||
protected:
|
||||
|
||||
ACameraCaptureSession_stateCallbacks *GetSessionListener();
|
||||
std::string GetFileName() const;
|
||||
|
||||
bool SendBroadcastMessage(std::string action, int value);
|
||||
bool MatchCaptureSizeRequest(ACameraManager *cameraManager, const char *selectedCameraId, unsigned int width, unsigned int height, uint32_t cameraOrientation_,
|
||||
ImageFormat* resCap);
|
||||
bool DisplayImage(ANativeWindow_Buffer* buf, AImage* image);
|
||||
|
||||
void PresentImage(ANativeWindow_Buffer* buf, AImage* image);
|
||||
void PresentImage90(ANativeWindow_Buffer* buf, AImage* image);
|
||||
void PresentImage180(ANativeWindow_Buffer* buf, AImage* image);
|
||||
void PresentImage270(ANativeWindow_Buffer* buf, AImage* image);
|
||||
|
||||
static void camera_device_on_disconnected(void *context, ACameraDevice *device);
|
||||
static void camera_device_on_error(void *context, ACameraDevice *device, int error);
|
||||
static void capture_session_on_ready(void *context, ACameraCaptureSession *session);
|
||||
static void capture_session_on_active(void *context, ACameraCaptureSession *session);
|
||||
static void capture_session_on_closed(void *context, ACameraCaptureSession *session);
|
||||
|
||||
void ImageCallback(AImageReader *reader);
|
||||
static void OnImageCallback(void *ctx, AImageReader *reader);
|
||||
bool WriteFile(AImage *image, const string& path);
|
||||
static bool WriteFile(CPhoneDevice2* pThis, AImage *image);
|
||||
|
||||
inline bool TakePhotoCb(bool res, const IDevice::PHOTO_INFO& photoInfo, const string& path, time_t photoTime)
|
||||
{
|
||||
if (m_listener != NULL)
|
||||
{
|
||||
std::vector<IDevice::RECOG_OBJECT> objects;
|
||||
return m_listener->OnPhotoTaken(res, photoInfo, path, photoTime, objects);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
|
||||
protected:
|
||||
|
||||
JavaVM* m_vm;
|
||||
jobject m_javaService;
|
||||
|
||||
jmethodID mRegisterTimerMid;
|
||||
jmethodID mRegisterHeartbeatMid;
|
||||
jmethodID mUnregisterTimerMid;
|
||||
jmethodID mUpdateTimeMid;
|
||||
|
||||
std::string mPath;
|
||||
IDevice::PHOTO_INFO mPhotoInfo;
|
||||
IListener* m_listener;
|
||||
|
||||
atomic_ulong m_timerUidFeed;
|
||||
std::map<IDevice::timer_uid_t, unsigned long> mTimers;
|
||||
|
||||
AImageReader *mAImageReader;
|
||||
ANativeWindow *theNativeWindow;
|
||||
ACameraDevice *cameraDevice;
|
||||
ACaptureRequest *captureRequest;
|
||||
ACameraOutputTarget *cameraOutputTarget;
|
||||
ACaptureSessionOutput *sessionOutput;
|
||||
ACaptureSessionOutputContainer *captureSessionOutputContainer;
|
||||
ACameraCaptureSession *captureSession;
|
||||
|
||||
ACameraDevice_StateCallbacks deviceStateCallbacks;
|
||||
ACameraCaptureSession_stateCallbacks captureSessionStateCallbacks;
|
||||
|
||||
DisplayDimension mDisplayDimension;
|
||||
int32_t presentRotation_;
|
||||
|
||||
int32_t imageHeight_;
|
||||
int32_t imageWidth_;
|
||||
|
||||
uint8_t* imageBuffer_;
|
||||
int32_t yStride, uvStride;
|
||||
uint8_t *yPixel, *uPixel, *vPixel;
|
||||
int32_t yLen, uLen, vLen;
|
||||
int32_t uvPixelStride;
|
||||
|
||||
|
||||
};
|
||||
|
||||
|
||||
#endif // __PHONE_DEVICE2_H__
|
@ -0,0 +1,57 @@
|
||||
#ifndef __POSITION_HELPER_H__
|
||||
#define __POSITION_HELPER_H__
|
||||
|
||||
#include <cmath>
|
||||
#define _USE_MATH_DEFINES
|
||||
|
||||
inline double transformLat(double x, double y)
|
||||
{
|
||||
double ret = -100.0 + 2.0 * x + 3.0 * y + 0.2 * y * y + 0.1 * x * y + 0.2 * std::sqrt(std::abs(x));
|
||||
ret += (20.0 * std::sin(6.0 * x * M_PI) + 20.0 * std::sin(2.0 * x * M_PI)) * 2.0 / 3.0;
|
||||
ret += (20.0 * std::sin(y * M_PI) + 40.0 * std::sin(y / 3.0 * M_PI)) * 2.0 / 3.0;
|
||||
ret += (160.0 * std::sin(y / 12.0 * M_PI) + 320 * std::sin(y * M_PI / 30.0)) * 2.0 / 3.0;
|
||||
return ret;
|
||||
}
|
||||
|
||||
inline double transformLng(double x, double y)
|
||||
{
|
||||
double ret = 300.0 + x + 2.0 * y + 0.1 * x * x + 0.1 * x * y + 0.1 * std::sqrt(std::abs(x));
|
||||
ret += (20.0 * std::sin(6.0 * x * M_PI) + 20.0 * std::sin(2.0 * x * M_PI)) * 2.0 / 3.0;
|
||||
ret += (20.0 * std::sin(x * M_PI) + 40.0 * std::sin(x / 3.0 * M_PI)) * 2.0 / 3.0;
|
||||
ret += (150.0 * std::sin(x / 12.0 * M_PI) + 300.0 * std::sin(x / 30.0 * M_PI)) * 2.0 / 3.0;
|
||||
return ret;
|
||||
}
|
||||
|
||||
inline void transformPosition(double& lat, double& lng)
|
||||
{
|
||||
// 卫星椭球坐标投影到平面地图坐标系的投影因子
|
||||
#define AXIS 6378245.0
|
||||
// 椭球的偏心率(a^2 - b^2) / a^2
|
||||
#define OFFSET 0.00669342162296594323
|
||||
double dLat = transformLat(lng - 105.0, lat - 35.0);
|
||||
double dLon = transformLng(lng - 105.0, lat - 35.0);
|
||||
double radLat = lat / 180.0 * M_PI;
|
||||
double magic = std::sin(radLat);
|
||||
magic = 1 - OFFSET * magic * magic;
|
||||
double sqrtMagic = std::sqrt(magic);
|
||||
dLat = (dLat * 180.0) / ((AXIS * (1 - OFFSET)) / (magic * sqrtMagic) * M_PI);
|
||||
dLon = (dLon * 180.0) / (AXIS / sqrtMagic * std::cos(radLat) * M_PI);
|
||||
lat += dLat;
|
||||
lng += dLon;
|
||||
}
|
||||
|
||||
inline bool shouldConvertPosition(double lat, double lon)
|
||||
{
|
||||
if (lon < 72.004 || lon > 137.8347)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (lat < 0.8293 || lat > 55.8271)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
#endif // __POSITION_HELPER_H__
|
@ -0,0 +1,100 @@
|
||||
//
|
||||
// Created by Matthew on 2025/3/5.
|
||||
//
|
||||
|
||||
#ifndef MICROPHOTO_PTZCONTROLLER_H
|
||||
#define MICROPHOTO_PTZCONTROLLER_H
|
||||
|
||||
#include <Buffer.h>
|
||||
#include <thread>
|
||||
#include <vector>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <mutex>
|
||||
#include <SemaphoreEx.h>
|
||||
#include <Client/Device.h>
|
||||
|
||||
enum PROC_PTZ_STATE
|
||||
{
|
||||
PTZS_POWER_OFF = 0,
|
||||
PTZS_IDLE = 1,
|
||||
PTZS_SELF_TESTING = 2,
|
||||
PTZS_MOVING = 3,
|
||||
PTZS_TAKING_PHOTO = 4,
|
||||
PTZS_PHOTO_SELF_TESTING = 5,
|
||||
};
|
||||
|
||||
#define CAMERA_SELF_TEST_TIME 150 /* Camera self-test time (excluding PTZ self-test)*/
|
||||
#define MOVE_PRESET_WAIT_TIME 20 /* Waiting for the maximum time for the PTZ to move to the preset position*/
|
||||
#define CAMERA_CLOSE_DELAYTIME 360 /* Auto Power-Off Timer Setting After Manual Power-On (for Camera)*/
|
||||
#define PHOTO_OPEN_POWER 16000
|
||||
#define WAIT_TIME_AUTO_CLOSE 2 /* In order to automatically capture multiple preset point images at the same time and prevent the camera from self checking every time it takes a picture.*/
|
||||
|
||||
class PtzPhotoParams
|
||||
{
|
||||
public:
|
||||
PtzPhotoParams(const IDevice::PHOTO_INFO& photoInfo, const std::string& path, const std::vector<IDevice::OSD_INFO>& osds) :
|
||||
mPhotoInfo(photoInfo), mPath(path), mOsds(osds)
|
||||
{
|
||||
}
|
||||
|
||||
~PtzPhotoParams()
|
||||
{
|
||||
}
|
||||
|
||||
IDevice::PHOTO_INFO mPhotoInfo;
|
||||
std::string mPath;
|
||||
std::vector<IDevice::OSD_INFO> mOsds;
|
||||
};
|
||||
|
||||
struct SERIAL_CMD
|
||||
{
|
||||
uint8_t channel;
|
||||
uint8_t preset;
|
||||
time_t ts;
|
||||
int cmdidx;
|
||||
uint32_t delayTime;
|
||||
uint8_t bImageSize;
|
||||
char serfile[128];
|
||||
uint32_t baud;
|
||||
int addr;
|
||||
std::shared_ptr<PtzPhotoParams> photoParams;
|
||||
};
|
||||
|
||||
|
||||
class CPhoneDevice;
|
||||
class PtzController
|
||||
{
|
||||
public:
|
||||
PtzController(CPhoneDevice* pPhoneDevice);
|
||||
|
||||
void Startup();
|
||||
// ();
|
||||
void AddCommand(uint8_t channel, int cmdidx, uint8_t bImageSize, uint8_t preset, const char *serfile, uint32_t baud, int addr);
|
||||
void AddPhotoCommand(IDevice::PHOTO_INFO& photoInfo, const std::string& path, const std::vector<IDevice::OSD_INFO>& osds);
|
||||
|
||||
void ExitAndWait();
|
||||
|
||||
protected:
|
||||
static void PtzThreadProc(PtzController* pThis);
|
||||
|
||||
void PtzProc();
|
||||
|
||||
|
||||
protected:
|
||||
|
||||
|
||||
protected:
|
||||
std::mutex m_locker;
|
||||
std::vector<SERIAL_CMD> m_cmds;
|
||||
|
||||
CSemaphore m_sem;
|
||||
bool m_exit;
|
||||
|
||||
std::thread m_thread;
|
||||
|
||||
CPhoneDevice* m_pPhoneDevice;
|
||||
};
|
||||
|
||||
|
||||
#endif //MICROPHOTO_PTZCONTROLLER_H
|
File diff suppressed because it is too large
Load Diff
@ -1,79 +0,0 @@
|
||||
#include "TerminalDevice.h"
|
||||
#include <dlfcn.h>
|
||||
#include "Camera.h"
|
||||
#include <AndroidHelper.h>
|
||||
|
||||
typedef jbyteArray (*TakePhotoFunc)(int, int, int, int);
|
||||
|
||||
extern bool GetJniEnv(JavaVM *vm, JNIEnv **env, bool& didAttachThread);
|
||||
|
||||
CTerminalDevice::CTerminalDevice(JavaVM* vm, jobject service)
|
||||
{
|
||||
m_vm = vm;
|
||||
JNIEnv* env = NULL;
|
||||
bool attached = false;
|
||||
bool res = GetJniEnv(m_vm, &env, attached);
|
||||
if (!res)
|
||||
{
|
||||
ALOGE("Failed to get JNI Env");
|
||||
}
|
||||
m_javaService = env->NewGlobalRef(service);
|
||||
if (attached)
|
||||
{
|
||||
vm->DetachCurrentThread();
|
||||
}
|
||||
}
|
||||
|
||||
CTerminalDevice::~CTerminalDevice()
|
||||
{
|
||||
JNIEnv* env = NULL;
|
||||
bool attached = false;
|
||||
bool res = GetJniEnv(m_vm, &env, attached);
|
||||
if (!res)
|
||||
{
|
||||
ALOGE("Failed to get JNI Env");
|
||||
}
|
||||
env->DeleteGlobalRef(m_javaService);
|
||||
if (attached)
|
||||
{
|
||||
m_vm->DetachCurrentThread();
|
||||
}
|
||||
m_javaService = NULL;
|
||||
}
|
||||
|
||||
bool CTerminalDevice::TakePhoto(unsigned char channel, unsigned char preset, const string& path, bool photo)
|
||||
{
|
||||
jboolean res = JNI_FALSE;
|
||||
|
||||
CCamera camera;
|
||||
camera.initCamera(NULL);
|
||||
if (camera.isCameraReady())
|
||||
{
|
||||
camera.takePicture();
|
||||
}
|
||||
|
||||
camera.closeCamera();
|
||||
|
||||
#if 0
|
||||
JNIEnv* env = NULL;
|
||||
bool attached = GetJniEnv(m_vm, &env);
|
||||
jclass serviceClass = env->GetObjectClass(m_javaService);
|
||||
jmethodID mid = env->GetMethodID(serviceClass, "takePhoto", "(SSLjava/lang/String;)Z");
|
||||
jstring str = env->NewStringUTF(path.c_str());
|
||||
res = env->CallBooleanMethod (m_javaService, mid, (jint)channel, (jint)preset, str);
|
||||
env->ReleaseStringUTFChars(str, path.c_str());
|
||||
env->DeleteLocalRef(serviceClass);
|
||||
|
||||
if (!res)
|
||||
{
|
||||
int aa = 1;
|
||||
}
|
||||
if (attached)
|
||||
{
|
||||
m_vm->DetachCurrentThread();
|
||||
}
|
||||
#endif
|
||||
|
||||
return res == JNI_TRUE;
|
||||
}
|
||||
|
@ -1,21 +0,0 @@
|
||||
#ifndef __TERMINAL_DEVICE_H__
|
||||
#define __TERMINAL_DEVICE_H__
|
||||
|
||||
#include <Client/Device.h>
|
||||
#include <jni.h>
|
||||
|
||||
class CTerminalDevice : public IDevice
|
||||
{
|
||||
public:
|
||||
CTerminalDevice(JavaVM* vm, jobject service);
|
||||
~CTerminalDevice();
|
||||
|
||||
virtual bool TakePhoto(unsigned char channel, unsigned char preset, const string& path, bool photo);
|
||||
|
||||
private:
|
||||
JavaVM* m_vm;
|
||||
jobject m_javaService;
|
||||
};
|
||||
|
||||
|
||||
#endif // __TERMINAL_DEVICE_H__
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,724 @@
|
||||
/* Copyright Statement:
|
||||
*
|
||||
* This software/firmware and related documentation ("MediaTek Software") are
|
||||
* protected under relevant copyright laws. The information contained herein is
|
||||
* confidential and proprietary to MediaTek Inc. and/or its licensors. Without
|
||||
* the prior written permission of MediaTek inc. and/or its licensors, any
|
||||
* reproduction, modification, use or disclosure of MediaTek Software, and
|
||||
* information contained herein, in whole or in part, shall be strictly
|
||||
* prohibited.
|
||||
*
|
||||
* MediaTek Inc. (C) 2010. All rights reserved.
|
||||
*
|
||||
* BY OPENING THIS FILE, RECEIVER HEREBY UNEQUIVOCALLY ACKNOWLEDGES AND AGREES
|
||||
* THAT THE SOFTWARE/FIRMWARE AND ITS DOCUMENTATIONS ("MEDIATEK SOFTWARE")
|
||||
* RECEIVED FROM MEDIATEK AND/OR ITS REPRESENTATIVES ARE PROVIDED TO RECEIVER
|
||||
* ON AN "AS-IS" BASIS ONLY. MEDIATEK EXPRESSLY DISCLAIMS ANY AND ALL
|
||||
* WARRANTIES, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE OR
|
||||
* NONINFRINGEMENT. NEITHER DOES MEDIATEK PROVIDE ANY WARRANTY WHATSOEVER WITH
|
||||
* RESPECT TO THE SOFTWARE OF ANY THIRD PARTY WHICH MAY BE USED BY,
|
||||
* INCORPORATED IN, OR SUPPLIED WITH THE MEDIATEK SOFTWARE, AND RECEIVER AGREES
|
||||
* TO LOOK ONLY TO SUCH THIRD PARTY FOR ANY WARRANTY CLAIM RELATING THERETO.
|
||||
* RECEIVER EXPRESSLY ACKNOWLEDGES THAT IT IS RECEIVER'S SOLE RESPONSIBILITY TO
|
||||
* OBTAIN FROM ANY THIRD PARTY ALL PROPER LICENSES CONTAINED IN MEDIATEK
|
||||
* SOFTWARE. MEDIATEK SHALL ALSO NOT BE RESPONSIBLE FOR ANY MEDIATEK SOFTWARE
|
||||
* RELEASES MADE TO RECEIVER'S SPECIFICATION OR TO CONFORM TO A PARTICULAR
|
||||
* STANDARD OR OPEN FORUM. RECEIVER'S SOLE AND EXCLUSIVE REMEDY AND MEDIATEK'S
|
||||
* ENTIRE AND CUMULATIVE LIABILITY WITH RESPECT TO THE MEDIATEK SOFTWARE
|
||||
* RELEASED HEREUNDER WILL BE, AT MEDIATEK'S OPTION, TO REVISE OR REPLACE THE
|
||||
* MEDIATEK SOFTWARE AT ISSUE, OR REFUND ANY SOFTWARE LICENSE FEES OR SERVICE
|
||||
* CHARGE PAID BY RECEIVER TO MEDIATEK FOR SUCH MEDIATEK SOFTWARE AT ISSUE.
|
||||
*
|
||||
* The following software/firmware and/or related documentation ("MediaTek
|
||||
* Software") have been modified by MediaTek Inc. All revisions are subject to
|
||||
* any receiver's applicable license agreements with MediaTek Inc.
|
||||
*/
|
||||
|
||||
#ifndef _MTK_HARDWARE_MTKCAM_INCLUDE_MTKCAM_UTILS_METADATA_HAL_MTKPLATFORMMETADATATAG_H_
|
||||
#define _MTK_HARDWARE_MTKCAM_INCLUDE_MTKCAM_UTILS_METADATA_HAL_MTKPLATFORMMETADATATAG_H_
|
||||
|
||||
/******************************************************************************
|
||||
*
|
||||
******************************************************************************/
|
||||
typedef enum mtk_platform_metadata_section {
|
||||
MTK_HAL_REQUEST = 0xC000, // MTK HAL internal metadata become from 0xC000 0000
|
||||
MTK_P1NODE,
|
||||
MTK_P2NODE,
|
||||
MTK_3A_TUNINING,
|
||||
MTK_3A_EXIF,
|
||||
MTK_MF_EXIF,
|
||||
MTK_EIS,
|
||||
MTK_STEREO,
|
||||
MTK_FRAMESYNC,
|
||||
MTK_VHDR,
|
||||
MTK_PIPELINE,
|
||||
MTK_NR,
|
||||
MTK_PLUGIN,
|
||||
MTK_DUALZOOM,
|
||||
MTK_FEATUREPIPE,
|
||||
MTK_POSTPROC,
|
||||
MTK_FEATURE,
|
||||
MTK_FSC,
|
||||
} mtk_platform_metadata_section_t;
|
||||
|
||||
|
||||
/******************************************************************************
|
||||
*
|
||||
******************************************************************************/
|
||||
typedef enum mtk_platform_metadata_section_start {
|
||||
MTK_HAL_REQUEST_START = MTK_HAL_REQUEST << 16,
|
||||
MTK_P1NODE_START = MTK_P1NODE << 16,
|
||||
MTK_P2NODE_START = MTK_P2NODE << 16,
|
||||
MTK_3A_TUNINING_START = MTK_3A_TUNINING << 16,
|
||||
MTK_3A_EXIF_START = MTK_3A_EXIF << 16,
|
||||
MTK_EIS_START = MTK_EIS << 16,
|
||||
MTK_STEREO_START = MTK_STEREO << 16,
|
||||
MTK_FRAMESYNC_START = MTK_FRAMESYNC << 16,
|
||||
MTK_VHDR_START = MTK_VHDR << 16,
|
||||
MTK_PIPELINE_START = MTK_PIPELINE << 16,
|
||||
MTK_NR_START = MTK_NR << 16,
|
||||
MTK_PLUGIN_START = MTK_PLUGIN << 16,
|
||||
MTK_DUALZOOM_START = MTK_DUALZOOM << 16,
|
||||
MTK_FEATUREPIPE_START = MTK_FEATUREPIPE << 16,
|
||||
MTK_POSTPROC_START = MTK_POSTPROC << 16,
|
||||
MTK_FEATURE_START = MTK_FEATURE << 16,
|
||||
MTK_FSC_START = MTK_FSC << 16,
|
||||
} mtk_platform_metadata_section_start_t;
|
||||
|
||||
|
||||
/******************************************************************************
|
||||
*
|
||||
******************************************************************************/
|
||||
typedef enum mtk_platform_metadata_tag {
|
||||
MTK_HAL_REQUEST_REQUIRE_EXIF = MTK_HAL_REQUEST_START, //MUINT8
|
||||
MTK_HAL_REQUEST_DUMP_EXIF, //MUINT8
|
||||
MTK_HAL_REQUEST_REPEAT, //MUINT8
|
||||
MTK_HAL_REQUEST_DUMMY, //MUINT8
|
||||
MTK_HAL_REQUEST_SENSOR_SIZE, //MSize
|
||||
MTK_HAL_REQUEST_SENSOR_ID, //MINT32
|
||||
MTK_HAL_REQUEST_DEVICE_ID, //MINT32
|
||||
MTK_HAL_REQUEST_HIGH_QUALITY_CAP, //MUINT8
|
||||
MTK_HAL_REQUEST_ISO_SPEED, //MINT32
|
||||
MTK_HAL_REQUEST_BRIGHTNESS_MODE, //MINT32
|
||||
MTK_HAL_REQUEST_CONTRAST_MODE, //MINT32
|
||||
MTK_HAL_REQUEST_HUE_MODE, //MINT32
|
||||
MTK_HAL_REQUEST_SATURATION_MODE, //MINT32
|
||||
MTK_HAL_REQUEST_EDGE_MODE, //MINT32
|
||||
MTK_HAL_REQUEST_PASS1_DISABLE, //MINT32
|
||||
MTK_HAL_REQUEST_ERROR_FRAME, // used for error handling //MUINT8
|
||||
MTK_HAL_REQUEST_PRECAPTURE_START, // 4cell //MUINT8
|
||||
MTK_HAL_REQUEST_AF_TRIGGER_START, // 4cell //MUINT8
|
||||
MTK_HAL_REQUEST_IMG_IMGO_FORMAT, //MINT32
|
||||
MTK_HAL_REQUEST_IMG_RRZO_FORMAT, //MINT32
|
||||
MTK_HAL_REQUEST_INDEX, //MINT32
|
||||
MTK_HAL_REQUEST_COUNT, //MINT32
|
||||
MTK_HAL_REQUEST_SMVR_FPS, //MUINT8 // 0: NOT batch request
|
||||
MTK_HAL_REQUEST_REMOSAIC_ENABLE, //MUINT8 // 0: preview mode 1: capture mode
|
||||
MTK_HAL_REQUEST_INDEX_BSS, //MINT32
|
||||
MTK_HAL_REQUEST_ZSD_CAPTURE_INTENT, //MUINT8
|
||||
MTK_HAL_REQUEST_REAL_CAPTURE_SIZE, //MSize
|
||||
MTK_HAL_REQUEST_VIDEO_SIZE, //MSize
|
||||
MTK_HAL_REQUEST_RAW_IMAGE_INFO, //MINT32 // index[0]: raw fmt, index[1]: raw stride, index[2]: raw size(width), index[3]: raw size(height)
|
||||
MTK_HAL_REQUEST_ISP_PIPELINE_MODE, //MINT32
|
||||
MTK_P1NODE_SCALAR_CROP_REGION = MTK_P1NODE_START, //MRect
|
||||
MTK_P1NODE_BIN_CROP_REGION, //MRect
|
||||
MTK_P1NODE_DMA_CROP_REGION, //MRect
|
||||
MTK_P1NODE_BIN_SIZE, //MSize
|
||||
MTK_P1NODE_RESIZER_SIZE, //MSize
|
||||
MTK_P1NODE_RESIZER_SET_SIZE, //MSize
|
||||
MTK_P1NODE_CTRL_RESIZE_FLUSH, //MBOOL
|
||||
MTK_P1NODE_CTRL_READOUT_FLUSH, //MBOOL
|
||||
MTK_P1NODE_CTRL_RECONFIG_SENSOR_SETTING, //MBOOL
|
||||
MTK_P1NODE_PROCESSOR_MAGICNUM, //MINT32
|
||||
MTK_P1NODE_MIN_FRM_DURATION, //MINT64
|
||||
MTK_P1NODE_RAW_TYPE, //MINT32
|
||||
MTK_P1NODE_SENSOR_CROP_REGION, //MRect
|
||||
MTK_P1NODE_YUV_RESIZER1_CROP_REGION, //MRect
|
||||
MTK_P1NODE_YUV_RESIZER2_CROP_REGION, //MRect
|
||||
MTK_P1NODE_YUV_RESIZER1_SIZE, //MSize
|
||||
MTK_P1NODE_SENSOR_MODE, //MINT32
|
||||
MTK_P1NODE_SENSOR_VHDR_MODE, //MINT32
|
||||
MTK_P1NODE_METADATA_TAG_INDEX, //MINT32
|
||||
MTK_P1NODE_RSS_SIZE, //MSize
|
||||
MTK_P1NODE_SENSOR_STATUS, //MINT32
|
||||
MTK_P1NODE_SENSOR_RAW_ORDER, //MINT32
|
||||
MTK_P1NODE_TWIN_SWITCH, //MINT32
|
||||
MTK_P1NODE_TWIN_STATUS, //MINT32
|
||||
MTK_P1NODE_RESIZE_QUALITY_SWITCH, //MINT32
|
||||
MTK_P1NODE_RESIZE_QUALITY_STATUS, //MINT32
|
||||
MTK_P1NODE_RESIZE_QUALITY_LEVEL, //MINT32
|
||||
MTK_P1NODE_RESIZE_QUALITY_SWITCHING, //MBOOL
|
||||
MTK_P1NODE_RESUME_SHUTTER_TIME_US, //MINT32
|
||||
MTK_P1NODE_FRAME_START_TIMESTAMP, //MINT64
|
||||
MTK_P1NODE_FRAME_START_TIMESTAMP_BOOT, //MINT64
|
||||
MTK_P1NODE_REQUEST_PROCESSED_WITHOUT_WB, //MBOOL
|
||||
MTK_P1NODE_ISNEED_GMV, //MBOOL
|
||||
MTK_P2NODE_HIGH_SPEED_VDO_FPS = MTK_P2NODE_START, //MINT32
|
||||
MTK_P2NODE_HIGH_SPEED_VDO_SIZE, //MSize
|
||||
MTK_P2NODE_CTRL_CALTM_ENABLE, //MBOOL
|
||||
MTK_P2NODE_FD_CROP_REGION, //MRect
|
||||
MTK_P2NODE_CROP_REGION, //MRect // for removing black edge
|
||||
MTK_P2NODE_DSDN_ENABLE, //MBOOL // for DSDN on/off controled by Policy
|
||||
MTK_P2NODE_SENSOR_CROP_REGION, //MRect
|
||||
MTK_3A_AE_HIGH_ISO_BINNING, //MBOOL // for 3HDR high iso binning mode
|
||||
MTK_SENSOR_SCALER_CROP_REGION, //MRect
|
||||
MTK_PROCESSOR_CAMINFO = MTK_3A_TUNINING_START, //IMemory
|
||||
MTK_ISP_ATMS_MAPPING_INFO, //IMemory
|
||||
MTK_3A_ISP_PROFILE, //MUINT8
|
||||
MTK_3A_ISP_P1_PROFILE, //MUINT8
|
||||
MTK_CAMINFO_LCSOUT_INFO, //IMemory
|
||||
MTK_3A_ISP_BYPASS_LCE, //MBOOL
|
||||
MTK_3A_ISP_DISABLE_NR, //MBOOL
|
||||
MTK_3A_ISP_NR3D_SW_PARAMS, //MINT32[14] //GMVX, GMVY, confX, confY, MAX_GMV, frameReset, GMV_Status,ISO_cutoff
|
||||
MTK_3A_ISP_NR3D_HW_PARAMS, //IMemory
|
||||
MTK_3A_ISP_LCE_GAIN, //MINT32, bits[0:15]: LCE gain, bits[16:31]: LCE gain confidence ratio (0-100)
|
||||
MTK_3A_ISP_FUS_NUM, //MINT32
|
||||
MTK_3A_AE_CAP_PARAM, //IMemory
|
||||
MTK_3A_AE_CAP_SINGLE_FRAME_HDR, //MUINT8
|
||||
MTK_3A_AE_BV_TRIGGER, //MBOOL
|
||||
MTK_3A_AF_LENS_POSITION, //MINT32
|
||||
MTK_3A_FLICKER_RESULT, //MINT32
|
||||
MTK_3A_DUMMY_BEFORE_REQUEST_FRAME, //MBOOL // Dummy frame before capture, only for capture intent, preview don't use
|
||||
MTK_3A_DUMMY_AFTER_REQUEST_FRAME, //MBOOL // Dummy frame after capture, only for capture intent, preview don't use
|
||||
MTK_3A_MANUAL_AWB_COLORTEMPERATURE_MAX, //MINT32
|
||||
MTK_3A_MANUAL_AWB_COLORTEMPERATURE_MIN, //MINT32
|
||||
MTK_3A_MANUAL_AWB_COLORTEMPERATURE, //MINT32
|
||||
MTK_3A_HDR_MODE, //MUINT8
|
||||
MTK_3A_AE_HDR_MIXED_ISO, //MUINT32
|
||||
MTK_3A_AE_ZSL_STABLE, //MINT32 ( MBOOL )
|
||||
MTK_3A_PGN_ENABLE, //MUINT8
|
||||
MTK_3A_SKIP_HIGH_QUALITY_CAPTURE, //MUINT8
|
||||
MTK_3A_AI_SHUTTER, //MBOOL
|
||||
MTK_3A_FEATURE_AE_EXPOSURE_LEVEL, //MINT32
|
||||
MTK_3A_FEATURE_AE_TARGET_MODE, //MINT32
|
||||
MTK_3A_OPEN_ID, //MINT32
|
||||
MTK_LSC_TBL_DATA, //IMemory
|
||||
MTK_LSC_TSF_DATA, //IMemory
|
||||
MTK_LSC_TSF_DUMP_NO, //IMemory
|
||||
MTK_ISP_P2_ORIGINAL_SIZE, //MSize
|
||||
MTK_ISP_P2_CROP_REGION, //MRect
|
||||
MTK_ISP_P2_RESIZER_SIZE, //MSize
|
||||
MTK_ISP_P2_IN_IMG_FMT, //MINT32, 0 or not exist: RAW->YUV, 1: YUV->YUV
|
||||
MTK_ISP_P2_TUNING_UPDATE_MODE, //MUINT8, [0 or not exist]: as default; [1]: keep existed parameters but some parts will be updated; [2]: keep all existed parameters (force mode) [3] LPCNR Pass1 [4] LPCNR Pass2
|
||||
MTK_ISP_P2_IN_IMG_RES_REVISED, //MINT32, describes P2 input image revised resolution. bit[0:15] width in pixel, bit[16:31] height in pixel. May be not exist.
|
||||
MTK_ISP_APP_TARGET_SIZE, //MINT32, describes APP Target resolution. bit[0:15] width in pixel, bit[16:31] height in pixel. May be not exist.
|
||||
MTK_MSF_SCALE_INDEX, //MINT32, which scale stage index, would only exist with scaling flow
|
||||
MTK_MSF_FRAME_NUM, //MINT32, After BSS which frame number is this stage using
|
||||
MTK_TOTAL_MULTI_FRAME_NUM, //MINT32, MSYUV fuction used this input to know frame nunber
|
||||
MTK_TOTAL_MULTI_FRAME_NUM_CAPTURED, //MINT32, MSF function used
|
||||
MTK_SW_DSDN_VERSION, //MINT32, distinguish different dsdn version
|
||||
MTK_ISP_COLOR_SPACE, //MINT32
|
||||
MTK_ISP_DRC_CURVE, //IMemory
|
||||
MTK_ISP_DRC_CURVE_SIZE, //MINT32
|
||||
MTK_ISP_FEO_DATA, //IMemory
|
||||
MTK_ISP_FEO_ENABLE, //MINT32
|
||||
MTK_ISP_FEO_INFO, //IMemory
|
||||
MTK_ISP_HLR_RATIO, //MINT32, which is a HDR ratio applied in HLR
|
||||
MTK_ISP_STAGE, //MINT32
|
||||
MTK_FOCUS_AREA_POSITION, //MINT32
|
||||
MTK_FOCUS_AREA_SIZE, //MSize
|
||||
MTK_FOCUS_AREA_RESULT, //MUINT8
|
||||
MTK_FOCUS_PAUSE, //MUINT8
|
||||
MTK_FOCUS_MZ_ON, //MUINT8
|
||||
MTK_3A_AF_FOCUS_VALUE, //MINT64
|
||||
MTK_3A_PRV_CROP_REGION, //MRect
|
||||
MTK_3A_ISP_MDP_TARGET_SIZE, //MSize
|
||||
MTK_3A_REPEAT_RESULT, //MUINT8
|
||||
MTK_3A_SKIP_PRECAPTURE, //MBOOL //if CUST_ENABLE_FLASH_DURING_TOUCH is true, MW can skip precapture
|
||||
MTK_3A_SKIP_BAD_FRAME, //MBOOL
|
||||
MTK_3A_FLARE_IN_MANUAL_CTRL_ENABLE, //MBOOL
|
||||
MTK_3A_DYNAMIC_SUBSAMPLE_COUNT, //MINT32 30fps = 1, 60fps = 2, ... , 120fps = 4
|
||||
MTK_3A_AE_LV_VALUE, //MINT32
|
||||
MTK_APP_CONTROL, //MINT32
|
||||
MTK_3A_CUST_PARAMS, //IMemory
|
||||
MTK_3A_SETTING_CUST_PARAMS, //IMemory
|
||||
MTK_3A_PERFRAME_INFO, //IMemory
|
||||
MTK_SENSOR_MODE_INFO_ACTIVE_ARRAY_CROP_REGION, //MRect
|
||||
MTK_3A_AE_BV, //MINT32
|
||||
MTK_3A_AE_CWV, //MINT32
|
||||
MTK_ISP_P2_PROCESSED_RAW, //MINT32
|
||||
MTK_3A_EXIF_METADATA = MTK_3A_EXIF_START, //IMetadata
|
||||
MTK_EIS_REGION = MTK_EIS_START, //MINT32
|
||||
MTK_EIS_INFO, //MINT64
|
||||
MTK_EIS_VIDEO_SIZE, //MRect
|
||||
MTK_EIS_NEED_OVERRIDE_TIMESTAMP, //MBOOL
|
||||
MTK_EIS_LMV_DATA, //IMemory
|
||||
MTK_STEREO_JPS_MAIN1_CROP = MTK_STEREO_START, //MRect
|
||||
MTK_STEREO_JPS_MAIN2_CROP, //MRect
|
||||
MTK_STEREO_SYNC2A_MODE, //MINT32
|
||||
MTK_STEREO_SYNCAF_MODE, //MINT32
|
||||
MTK_STEREO_HW_FRM_SYNC_MODE, //MINT32
|
||||
MTK_STEREO_NOTIFY, //MINT32
|
||||
MTK_STEREO_SYNC2A_MASTER_SLAVE, //MINT32[2]
|
||||
MTK_STEREO_SYNC2A_STATUS, //IMemory
|
||||
MTK_JPG_ENCODE_TYPE, //MINT8
|
||||
MTK_CONVERGENCE_DEPTH_OFFSET, //MFLOAT
|
||||
MTK_N3D_WARPING_MATRIX_SIZE, //MUINT32
|
||||
MTK_P1NODE_MAIN2_HAL_META, //IMetadata
|
||||
MTK_P2NODE_BOKEH_ISP_PROFILE, //MUINT8
|
||||
MTK_STEREO_FEATURE_DENOISE_MODE, //MINT32
|
||||
MTK_STEREO_FEATURE_SENSOR_PROFILE, //MINT32
|
||||
MTK_P1NODE_MAIN2_APP_META, //IMetadata
|
||||
MTK_STEREO_FEATURE_OPEN_ID, //MINT32
|
||||
MTK_STEREO_FRAME_PER_CAPTURE, //MINT32
|
||||
MTK_STEREO_ENABLE_MFB, //MINT32
|
||||
MTK_STEREO_BSS_RESULT, //MINT32
|
||||
MTK_STEREO_FEATURE_FOV_CROP_REGION, //MINT32[6] // p.x, p.y, p.w, p.h, srcW, srcH
|
||||
MTK_STEREO_DCMF_FEATURE_MODE, //MINT32 // mtk_platform_metadata_enum_dcmf_feature_mode
|
||||
MTK_STEREO_HDR_EV, //MINT32
|
||||
MTK_STEREO_DELAY_FRAME_COUNT, //MINT32
|
||||
MTK_STEREO_DCMF_DEPTHMAP_SIZE, //MSize
|
||||
MTK_STEREO_WITH_CAMSV, //MBOOL
|
||||
MTK_FRAMESYNC_ID = MTK_FRAMESYNC_START, //MINT32
|
||||
MTK_FRAMESYNC_TOLERANCE, //MINT64
|
||||
MTK_FRAMESYNC_FAILHANDLE, //MINT32
|
||||
MTK_FRAMESYNC_RESULT, //MINT64
|
||||
MTK_FRAMESYNC_TYPE, //MINT32
|
||||
MTK_FRAMESYNC_MODE, //MUINT8
|
||||
MTK_VHDR_LCEI_DATA = MTK_VHDR_START, //Memory
|
||||
MTK_VHDR_IMGO_3A_ISP_PROFILE, //MUINT8
|
||||
MTK_HDR_FEATURE_HDR_HAL_MODE,
|
||||
MTK_3A_FEATURE_AE_VALID_EXPOSURE_NUM,
|
||||
MTK_VHDR_MULTIFRAME_TIMESTAMP, //MINT64
|
||||
MTK_VHDR_MULTIFRAME_EXPOSURE_TIME, //MINT64
|
||||
MTK_PIPELINE_UNIQUE_KEY = MTK_PIPELINE_START, //MINT32
|
||||
MTK_PIPELINE_FRAME_NUMBER, //MINT32
|
||||
MTK_PIPELINE_REQUEST_NUMBER, //MINT32
|
||||
MTK_PIPELINE_EV_VALUE, //MINT32
|
||||
MTK_PIPELINE_DUMP_UNIQUE_KEY, //MINT32
|
||||
MTK_PIPELINE_DUMP_FRAME_NUMBER, //MINT32
|
||||
MTK_PIPELINE_DUMP_REQUEST_NUMBER, //MINT32
|
||||
MTK_PIPELINE_VIDEO_RECORD, //MINT32
|
||||
MTK_NR_MODE = MTK_NR_START, //MINT32
|
||||
MTK_NR_MNR_THRESHOLD_ISO, //MINT32
|
||||
MTK_NR_SWNR_THRESHOLD_ISO, //MINT32
|
||||
MTK_REAL_LV, //MINT32
|
||||
MTK_ANALOG_GAIN, //MUINT32
|
||||
MTK_AWB_RGAIN, //MINT32
|
||||
MTK_AWB_GGAIN, //MINT32
|
||||
MTK_AWB_BGAIN, //MINT32
|
||||
MTK_PLUGIN_MODE = MTK_PLUGIN_START, //MINT64
|
||||
MTK_PLUGIN_COMBINATION_KEY, //MINT64
|
||||
MTK_PLUGIN_P2_COMBINATION, //MINT64
|
||||
MTK_PLUGIN_PROCESSED_FRAME_COUNT, //MINT32
|
||||
MTK_PLUGIN_CUSTOM_HINT, //MINT32
|
||||
MTK_PLUGIN_DETACT_JOB_SYNC_TOKEN, //MINT64, may be not exists.
|
||||
MTK_PLUGIN_UNIQUEKEY,
|
||||
MTK_DUALZOOM_DROP_REQ = MTK_DUALZOOM_START, //MINT32
|
||||
MTK_DUALZOOM_FORCE_ENABLE_P2, //MINT32
|
||||
MTK_DUALZOOM_DO_FRAME_SYNC, //MINT32
|
||||
MTK_DUALZOOM_ZOOM_FACTOR, //MINT32
|
||||
MTK_DUALZOOM_DO_FOV, //MINT32
|
||||
MTK_DUALZOOM_FOV_RECT_INFO, //MINT32
|
||||
MTK_DUALZOOM_FOV_CALB_INFO, //MINT32
|
||||
MTK_DUALZOOM_FOV_MARGIN_PIXEL, //MSize
|
||||
MTK_DUALCAM_AF_STATE, //MUINT8
|
||||
MTK_DUALCAM_LENS_STATE, //MUINT8
|
||||
MTK_DUALCAM_TIMESTAMP, //MINT64
|
||||
MTK_DUALZOOM_3DNR_MODE, //MINT32
|
||||
MTK_DUALZOOM_ZOOMRATIO, //MINT32
|
||||
MTK_DUALZOOM_CENTER_SHIFT, //MINT32
|
||||
MTK_DUALZOOM_FOV_RATIO, //MFLOAT
|
||||
MTK_DUALZOOM_REAL_MASTER, //MINT32
|
||||
MTK_DUALZOOM_FD_TARGET_MASTER, //MINT32
|
||||
MTK_DUALZOOM_FD_REAL_MASTER, //MINT32 // maybe not set
|
||||
MTK_LMV_SEND_SWITCH_OUT, //MINT32
|
||||
MTK_LMV_SWITCH_OUT_RESULT, //MINT32
|
||||
MTK_LMV_VALIDITY, //MINT32
|
||||
MTK_VSDOF_P1_MAIN1_ISO, //MINT32
|
||||
MTK_DUALZOOM_IS_STANDBY, //MBOOL
|
||||
MTK_DUALZOOM_CAP_CROP, //MRect
|
||||
MTK_DUALZOOM_MASTER_UPDATE_MODE, //MBOOL
|
||||
MTK_DUALZOOM_STREAMING_NR, //MINT32
|
||||
MTK_FEATUREPIPE_APP_MODE = MTK_FEATUREPIPE_START, //MINT32
|
||||
MTK_POSTPROC_TYPE = MTK_POSTPROC_START, //MINT32
|
||||
MTK_FEATURE_STREAMING = MTK_FEATURE_START, //MINT64
|
||||
MTK_FEATURE_CAPTURE, //MINT64
|
||||
MTK_FEATURE_CAPTURE_PHYSICAL, //MINT64
|
||||
MTK_FEATURE_FREE_MEMORY_MBYTE, //MINT32
|
||||
MTK_FEATURE_MFNR_NVRAM_QUERY_INDEX, //MINT32
|
||||
MTK_FEATURE_MFNR_NVRAM_DECISION_ISO, //MINT32
|
||||
MTK_FEATURE_MFNR_TUNING_INDEX_HINT, //MINT64
|
||||
MTK_FEATURE_MFNR_FINAL_EXP, //MINT32
|
||||
MTK_FEATURE_MFNR_OPEN_ID, //MINT32
|
||||
MTK_FEATURE_AINR_MDLA_MODE, //MINT32
|
||||
MTK_ISP_AINR_MDLA_MODE, //MINT32
|
||||
MTK_ISP_LTM_BIT_MODE, //MINT32
|
||||
MTK_FEATURE_BSS_SELECTED_FRAME_COUNT, //MINT32
|
||||
MTK_FEATURE_BSS_FORCE_DROP_NUM, //MINT32
|
||||
MTK_FEATURE_BSS_FIXED_LSC_TBL_DATA, //MUINT8
|
||||
MTK_FEATURE_BSS_PROCESS, //MINT32
|
||||
MTK_FEATURE_BSS_ISGOLDEN, //MBOOL
|
||||
MTK_FEATURE_BSS_REORDER, //MBOOL
|
||||
MTK_FEATURE_BSS_MANUAL_ORDER, //MUINT8
|
||||
MTK_FEATURE_BSS_RRZO_DATA, //MUINT8
|
||||
MTK_FEATURE_BSS_DOWNSAMPLE, //MBOOL
|
||||
MTK_FEATURE_PACK_RRZO, //MUINT8
|
||||
MTK_FEATURE_FACE_RECTANGLES, //MRect array
|
||||
MTK_FEATURE_FACE_POSE_ORIENTATIONS, //MINT32[n*3] array, each struct include: xAsix, yAsix, zAsix
|
||||
MTK_FEATURE_CAP_YUV_PROCESSING, //MUINT8
|
||||
MTK_FEATURE_CAP_PIPE_DCE_CONTROL, //MUINT8
|
||||
MTK_FEATURE_MULTIFRAMENODE_BYPASSED, //MUINT8
|
||||
MTK_FEATURE_FACE_APPLIED_GAMMA, //MINT32
|
||||
MTK_FEATURE_CAP_PQ_USERID, //MINT64
|
||||
MTK_FEATURE_FLIP_IN_P2A, //MINT32
|
||||
MTK_FSC_CROP_DATA = MTK_FSC_START, //IMemory
|
||||
MTK_FSC_WARP_DATA, //IMemory
|
||||
MTK_STAGGER_ME_META, //IMetadata
|
||||
MTK_STAGGER_SE_META, //IMetadata
|
||||
MTK_STAGGER_BLOB_IMGO_ORDER //MUINT8
|
||||
} mtk_platform_metadata_tag_t;
|
||||
|
||||
|
||||
/******************************************************************************
|
||||
*
|
||||
******************************************************************************/
|
||||
typedef enum mtk_platform_3a_exif_metadata_tag {
|
||||
MTK_3A_EXIF_FNUMBER, //MINT32
|
||||
MTK_3A_EXIF_FOCAL_LENGTH, //MINT32
|
||||
MTK_3A_EXIF_FOCAL_LENGTH_35MM, //MINT32
|
||||
MTK_3A_EXIF_SCENE_MODE, //MINT32
|
||||
MTK_3A_EXIF_AWB_MODE, //MINT32
|
||||
MTK_3A_EXIF_LIGHT_SOURCE, //MINT32
|
||||
MTK_3A_EXIF_EXP_PROGRAM, //MINT32
|
||||
MTK_3A_EXIF_SCENE_CAP_TYPE, //MINT32
|
||||
MTK_3A_EXIF_FLASH_LIGHT_TIME_US, //MINT32
|
||||
MTK_3A_EXIF_AE_METER_MODE, //MINT32
|
||||
MTK_3A_EXIF_AE_EXP_BIAS, //MINT32
|
||||
MTK_3A_EXIF_CAP_EXPOSURE_TIME, //MINT32
|
||||
MTK_3A_EXIF_AE_ISO_SPEED, //MINT32
|
||||
MTK_3A_EXIF_REAL_ISO_VALUE, //MINT32
|
||||
MTK_3A_EXIF_AE_BRIGHTNESS_VALUE, //MINT32
|
||||
MTK_3A_EXIF_FLASH_FIRING_STATUS, //MINT32
|
||||
MTK_3A_EXIF_FLASH_RETURN_DETECTION, //MINT32
|
||||
MTK_3A_EXIF_FLASH_MODE, //MINT32
|
||||
MTK_3A_EXIF_FLASH_FUNCTION, //MINT32
|
||||
MTK_3A_EXIF_FLASH_REDEYE, //MINT32
|
||||
MTK_3A_EXIF_DEBUGINFO_BEGIN, // debug info begin
|
||||
// key: MINT32
|
||||
MTK_3A_EXIF_DBGINFO_AAA_KEY = MTK_3A_EXIF_DEBUGINFO_BEGIN, //MINT32
|
||||
MTK_3A_EXIF_DBGINFO_AAA_DATA,
|
||||
MTK_3A_EXIF_DBGINFO_SDINFO_KEY,
|
||||
MTK_3A_EXIF_DBGINFO_SDINFO_DATA,
|
||||
MTK_3A_EXIF_DBGINFO_ISP_KEY,
|
||||
MTK_3A_EXIF_DBGINFO_ISP_DATA,
|
||||
//
|
||||
MTK_CMN_EXIF_DBGINFO_KEY,
|
||||
MTK_CMN_EXIF_DBGINFO_DATA,
|
||||
//
|
||||
MTK_MF_EXIF_DBGINFO_MF_KEY,
|
||||
MTK_MF_EXIF_DBGINFO_MF_DATA,
|
||||
//
|
||||
MTK_N3D_EXIF_DBGINFO_KEY,
|
||||
MTK_N3D_EXIF_DBGINFO_DATA,
|
||||
//
|
||||
MTK_POSTNR_EXIF_DBGINFO_NR_KEY,
|
||||
MTK_POSTNR_EXIF_DBGINFO_NR_DATA,
|
||||
//
|
||||
MTK_RESVB_EXIF_DBGINFO_KEY,
|
||||
MTK_RESVB_EXIF_DBGINFO_DATA,
|
||||
//
|
||||
MTK_RESVC_EXIF_DBGINFO_KEY,
|
||||
MTK_RESVC_EXIF_DBGINFO_DATA,
|
||||
// data: Memory
|
||||
MTK_3A_EXIF_DEBUGINFO_END, // debug info end
|
||||
} mtk_platform_3a_exif_metadata_tag_t;
|
||||
|
||||
// MTK_3A_FEATURE_AE_EXPOSURE_LEVEL
|
||||
typedef enum mtk_camera_metadata_enum_ae_exposure_level {
|
||||
MTK_3A_FEATURE_AE_EXPOSURE_LEVEL_NONE = 0,
|
||||
MTK_3A_FEATURE_AE_EXPOSURE_LEVEL_SHORT,
|
||||
MTK_3A_FEATURE_AE_EXPOSURE_LEVEL_NORMAL,
|
||||
MTK_3A_FEATURE_AE_EXPOSURE_LEVEL_LONG,
|
||||
} mtk_camera_metadata_enum_ae_exposure_level_t;
|
||||
|
||||
// MTK_3A_FEATURE_AE_TARGET_MODE
|
||||
typedef enum mtk_camera_metadata_enum_ae_target_mode {
|
||||
MTK_3A_FEATURE_AE_TARGET_MODE_NORMAL = 0,
|
||||
MTK_3A_FEATURE_AE_TARGET_MODE_IVHDR,
|
||||
MTK_3A_FEATURE_AE_TARGET_MODE_MVHDR,
|
||||
MTK_3A_FEATURE_AE_TARGET_MODE_ZVHDR,
|
||||
MTK_3A_FEATURE_AE_TARGET_MODE_LE_FIX,
|
||||
MTK_3A_FEATURE_AE_TARGET_MODE_SE_FIX,
|
||||
MTK_3A_FEATURE_AE_TARGET_MODE_4CELL_MVHDR,
|
||||
MTK_3A_FEATURE_AE_TARGET_MODE_MSTREAM_VHDR,
|
||||
MTK_3A_FEATURE_AE_TARGET_MODE_MSTREAM_VHDR_RTO1X,
|
||||
MTK_3A_FEATURE_AE_TARGET_MODE_STAGGER_2EXP,
|
||||
MTK_3A_FEATURE_AE_TARGET_MODE_STAGGER_3EXP,
|
||||
} mtk_camera_metadata_enum_ae_target_mode_t;
|
||||
|
||||
//MTK_3A_FEATURE_AE_VALID_EXPOSURE_NUM
|
||||
typedef enum mtk_camera_metadata_enum_stagger_valid_exposure_num {
|
||||
MTK_STAGGER_VALID_EXPOSURE_NON = 0,
|
||||
MTK_STAGGER_VALID_EXPOSURE_1 = 1,
|
||||
MTK_STAGGER_VALID_EXPOSURE_2 = 2,
|
||||
MTK_STAGGER_VALID_EXPOSURE_3 = 3
|
||||
} mtk_camera_metadata_enum_stagger_valid_exposure_num_t;
|
||||
|
||||
//MTK_3A_ISP_FUS_NUM
|
||||
typedef enum mtk_camera_metadata_enum_3a_isp_fus_num {
|
||||
MTK_3A_ISP_FUS_NUM_NON = 0,
|
||||
MTK_3A_ISP_FUS_NUM_1 = 1,
|
||||
MTK_3A_ISP_FUS_NUM_2 = 2,
|
||||
MTK_3A_ISP_FUS_NUM_3 = 3,
|
||||
} mtk_camera_metadata_enum_3a_isp_fus_num_t;
|
||||
|
||||
/******************************************************************************
|
||||
*
|
||||
******************************************************************************/
|
||||
typedef enum mtk_platform_metadata_enum_nr_mode {
|
||||
MTK_NR_MODE_OFF = 0,
|
||||
MTK_NR_MODE_MNR,
|
||||
MTK_NR_MODE_SWNR,
|
||||
MTK_NR_MODE_AUTO
|
||||
} mtk_platform_metadata_enum_nr_mode_t;
|
||||
|
||||
typedef enum mtk_platform_metadata_enum_mfb_mode {
|
||||
MTK_MFB_MODE_OFF = 0,
|
||||
MTK_MFB_MODE_MFLL,
|
||||
MTK_MFB_MODE_AIS,
|
||||
MTK_MFB_MODE_NUM,
|
||||
} mtk_platform_metadata_enum_mfb_mode_t;
|
||||
|
||||
typedef enum mtk_platform_metadata_enum_custom_hint {
|
||||
MTK_CUSTOM_HINT_0 = 0,
|
||||
MTK_CUSTOM_HINT_1,
|
||||
MTK_CUSTOM_HINT_2,
|
||||
MTK_CUSTOM_HINT_3,
|
||||
MTK_CUSTOM_HINT_4,
|
||||
MTK_CUSTOM_HINT_NUM,
|
||||
} mtk_platform_metadata_enum_custom_hint_t;
|
||||
|
||||
typedef enum mtk_platform_metadata_enum_plugin_mode {
|
||||
MTK_PLUGIN_MODE_COMBINATION = 1 << 0,
|
||||
MTK_PLUGIN_MODE_NR = 1 << 1,
|
||||
MTK_PLUGIN_MODE_HDR = 1 << 2,
|
||||
MTK_PLUGIN_MODE_MFNR = 1 << 3,
|
||||
MTK_PLUGIN_MODE_COPY = 1 << 4,
|
||||
MTK_PLUGIN_MODE_TEST_PRV = 1 << 5,
|
||||
MTK_PLUGIN_MODE_BMDN = 1 << 6,
|
||||
MTK_PLUGIN_MODE_MFHR = 1 << 7,
|
||||
MTK_PLUGIN_MODE_BMDN_3rdParty = 1 << 8,
|
||||
MTK_PLUGIN_MODE_MFHR_3rdParty = 1 << 9,
|
||||
MTK_PLUGIN_MODE_FUSION_3rdParty = 1 << 10,
|
||||
MTK_PLUGIN_MODE_VSDOF_3rdParty = 1 << 11,
|
||||
MTK_PLUGIN_MODE_COLLECT = 1 << 12,
|
||||
MTK_PLUGIN_MODE_HDR_3RD_PARTY = 1 << 13,
|
||||
MTK_PLUGIN_MODE_MFNR_3RD_PARTY = 1 << 14,
|
||||
MTK_PLUGIN_MODE_BOKEH_3RD_PARTY = 1 << 15,
|
||||
MTK_PLUGIN_MODE_DCMF_3RD_PARTY = 1 << 16,
|
||||
} mtk_platform_metadata_enum_plugin_mode_t;
|
||||
|
||||
typedef enum mtk_platform_metadata_enum_p2_plugin_combination {
|
||||
MTK_P2_RAW_PROCESSOR = 1 << 0,
|
||||
MTK_P2_ISP_PROCESSOR = 1 << 1,
|
||||
MTK_P2_YUV_PROCESSOR = 1 << 2,
|
||||
MTK_P2_MDP_PROCESSOR = 1 << 3,
|
||||
MTK_P2_CAPTURE_REQUEST = 1 << 4,
|
||||
MTK_P2_PREVIEW_REQUEST = 1 << 5
|
||||
} mtk_platform_metadata_enum_p2_plugin_combination;
|
||||
|
||||
typedef enum mtk_platform_metadata_enum_isp_color_space {
|
||||
MTK_ISP_COLOR_SPACE_SRGB = 0 ,
|
||||
MTK_ISP_COLOR_SPACE_DISPLAY_P3 = 1 ,
|
||||
MTK_ISP_COLOR_SPACE_CUSTOM_1 = 2
|
||||
} mtk_platform_metadata_enum_isp_color_space;
|
||||
|
||||
typedef enum mtk_platform_metadata_enum_dualzoom_drop_req {
|
||||
MTK_DUALZOOM_DROP_NEVER_DROP = 0,
|
||||
MTK_DUALZOOM_DROP_NONE = 1,
|
||||
MTK_DUALZOOM_DROP_DIRECTLY = 2,
|
||||
MTK_DUALZOOM_DROP_NEED_P1,
|
||||
MTK_DUALZOOM_DROP_NEED_SYNCMGR,
|
||||
MTK_DUALZOOM_DROP_NEED_SYNCMGR_NEED_STREAM_F_PIPE,
|
||||
} mtk_platform_metadata_enum_dualzoom_drop_req_t;
|
||||
|
||||
typedef enum mtk_platform_metadata_enum_p1_sensor_status {
|
||||
MTK_P1_SENSOR_STATUS_NONE = 0,
|
||||
MTK_P1_SENSOR_STATUS_STREAMING = 1,
|
||||
MTK_P1_SENSOR_STATUS_SW_STANDBY = 2,
|
||||
MTK_P1_SENSOR_STATUS_HW_STANDBY = 3,
|
||||
} mtk_platform_metadata_enum_p1_sensor_status_t;
|
||||
|
||||
typedef enum mtk_platform_metadata_enum_p1_twin_switch {
|
||||
MTK_P1_TWIN_SWITCH_NONE = 0,
|
||||
MTK_P1_TWIN_SWITCH_ONE_TG = 1,
|
||||
MTK_P1_TWIN_SWITCH_TWO_TG = 2
|
||||
} mtk_platform_metadata_enum_p1_twin_switch_t;
|
||||
|
||||
typedef enum mtk_platform_metadata_enum_p1_twin_status {
|
||||
MTK_P1_TWIN_STATUS_NONE = 0,
|
||||
MTK_P1_TWIN_STATUS_TG_MODE_1 = 1,
|
||||
MTK_P1_TWIN_STATUS_TG_MODE_2 = 2,
|
||||
MTK_P1_TWIN_STATUS_TG_MODE_3 = 3,
|
||||
} mtk_platform_metadata_enum_p1_twin_status_t;
|
||||
|
||||
typedef enum mtk_platform_metadata_enum_p1_resize_quality_switch {
|
||||
MTK_P1_RESIZE_QUALITY_SWITCH_NONE = 0,
|
||||
MTK_P1_RESIZE_QUALITY_SWITCH_L_L = 1,
|
||||
MTK_P1_RESIZE_QUALITY_SWITCH_L_H = 2,
|
||||
MTK_P1_RESIZE_QUALITY_SWITCH_H_L = 3,
|
||||
MTK_P1_RESIZE_QUALITY_SWITCH_H_H = 4,
|
||||
} mtk_platform_metadata_enum_p1_resize_quality_switch_t;
|
||||
|
||||
typedef enum mtk_platform_metadata_enum_p1_resize_quality_status {
|
||||
MTK_P1_RESIZE_QUALITY_STATUS_NONE = 0,
|
||||
MTK_P1_RESIZE_QUALITY_STATUS_ACCEPT = 1,
|
||||
MTK_P1_RESIZE_QUALITY_STATUS_IGNORE = 2,
|
||||
MTK_P1_RESIZE_QUALITY_STATUS_REJECT = 3,
|
||||
MTK_P1_RESIZE_QUALITY_STATUS_ILLEGAL = 4,
|
||||
} mtk_platform_metadata_enum_p1_resize_quality_status_t;
|
||||
|
||||
typedef enum mtk_platform_metadata_enum_p1_resize_quality_level {
|
||||
MTK_P1_RESIZE_QUALITY_LEVEL_UNKNOWN = 0,
|
||||
MTK_P1_RESIZE_QUALITY_LEVEL_L = 1,
|
||||
MTK_P1_RESIZE_QUALITY_LEVEL_H = 2,
|
||||
} mtk_platform_metadata_enum_p1_resize_quality_level_t;
|
||||
|
||||
typedef enum mtk_platform_metadata_enum_lmv_result {
|
||||
MTK_LMV_RESULT_OK = 0,
|
||||
MTK_LMV_RESULT_FAILED,
|
||||
MTK_LMV_RESULT_SWITCHING
|
||||
} mtk_platform_metadata_enum_lmv_result_t;
|
||||
|
||||
typedef enum mtk_platform_metadata_enum_featurepipe_app_mode {
|
||||
MTK_FEATUREPIPE_PHOTO_PREVIEW = 0,
|
||||
MTK_FEATUREPIPE_VIDEO_PREVIEW = 1,
|
||||
MTK_FEATUREPIPE_VIDEO_RECORD = 2,
|
||||
MTK_FEATUREPIPE_VIDEO_STOP = 3,
|
||||
} mtk_platform_metadata_enum_featurepipe_app_mode_t;
|
||||
|
||||
typedef enum mtk_platform_metadata_enum_dcmf_feature_mode {
|
||||
MTK_DCMF_FEATURE_BOKEH = 0,
|
||||
MTK_DCMF_FEATURE_MFNR_BOKEH = 1,
|
||||
MTK_DCMF_FEATURE_HDR_BOKEH = 2,
|
||||
} mtk_platform_metadata_enum_dcmf_feature_mode_t;
|
||||
|
||||
typedef enum mtk_platform_metadata_enum_smvr_fps {
|
||||
MTK_SMVR_FPS_30 = 0,
|
||||
MTK_SMVR_FPS_120 = 1,
|
||||
MTK_SMVR_FPS_240 = 2,
|
||||
MTK_SMVR_FPS_480 = 3,
|
||||
MTK_SMVR_FPS_960 = 4,
|
||||
} mtk_platform_metadata_enum_smvr_fps_t;
|
||||
|
||||
//MTK_FRAMESYNC_FAILHANDLE
|
||||
typedef enum mtk_platform_metadata_enum_fremesync_failhandle {
|
||||
MTK_FRAMESYNC_FAILHANDLE_CONTINUE,
|
||||
MTK_FRAMESYNC_FAILHANDLE_DROP,
|
||||
} mtk_platform_metadata_enum_fremesync_failhandle_t;
|
||||
|
||||
//MTK_FRAMESYNC_RESULT
|
||||
typedef enum mtk_platform_metadata_enum_fremesync_result {
|
||||
MTK_FRAMESYNC_RESULT_PASS,
|
||||
MTK_FRAMESYNC_RESULT_FAIL_CONTINUE,
|
||||
MTK_FRAMESYNC_RESULT_FAIL_DROP,
|
||||
} mtk_platform_metadata_enum_fremesync_result_t;
|
||||
|
||||
//MTK_FRAMESYNC_MODE
|
||||
typedef enum mtk_platform_metadata_enum_fremesync_mode {
|
||||
MTK_FRAMESYNC_MODE_VSYNC_ALIGNMENT,
|
||||
MTK_FRAMESYNC_MODE_READOUT_CENTER_ALIGNMENT,
|
||||
} mtk_platform_metadata_enum_fremesync_mode_t;
|
||||
|
||||
//MTK_FEATURE_MULTIFRAMENODE_BYPASSED
|
||||
typedef enum mtk_platform_metadata_enum_multiframenode_bypassed {
|
||||
MTK_FEATURE_MULTIFRAMENODE_NOT_BYPASSED = 0,
|
||||
MTK_FEATURE_MULTIFRAMENODE_TO_BE_BYPASSED = 1
|
||||
} mtk_platform_metadata_enum_mfllnode_bypassed_t;
|
||||
|
||||
//MTK_FEATURE_BSS_PROCESS
|
||||
typedef enum mtk_platform_metadata_enum_bss_processing {
|
||||
MTK_FEATURE_BSS_PROCESS_ENABLE = 0,
|
||||
MTK_FEATURE_BSS_PROCESS_DISABLE = 1
|
||||
} mtk_platform_metadata_enum_bss_processing_t;
|
||||
|
||||
//MTK_FEATURE_BSS_MANUAL_ORDER
|
||||
typedef enum mtk_platform_metadata_enum_bss_manual_order {
|
||||
MTK_FEATURE_BSS_MANUAL_ORDER_OFF = 0,
|
||||
MTK_FEATURE_BSS_MANUAL_ORDER_GOLDEN = 1
|
||||
} mtk_platform_metadata_enum_bss_manual_order_t;
|
||||
|
||||
//MTK_FEATURE_CAP_YUV_PROCESSING
|
||||
typedef enum mtk_platform_metadata_enum_cap_yuv_processing {
|
||||
MTK_FEATURE_CAP_YUV_PROCESSING_NOT_NEEDED = 0,
|
||||
MTK_FEATURE_CAP_YUV_PROCESSING_NEEDED = 1
|
||||
} mtk_platform_metadata_enum_cap_yuv_processing_t;
|
||||
|
||||
//MTK_FEATURE_CAP_PIPE_DCE_CONTROL
|
||||
typedef enum mtk_platform_metadata_enum_cap_pipe_control {
|
||||
MTK_FEATURE_CAP_PIPE_DCE_ENABLE_BUT_NOT_APPLY = 2,
|
||||
MTK_FEATURE_CAP_PIPE_DCE_MANUAL_DISABLE = 1,
|
||||
MTK_FEATURE_CAP_PIPE_DCE_DEFAULT_APPLY = 0
|
||||
} mtk_platform_metadata_enum_cap_pipe_dce_control_t;
|
||||
|
||||
// MTK_FEATURE_AINR_MDLA_MODE, MTK_ISP_AINR_MDLA_MODE
|
||||
typedef enum mtk_platform_metadata_enum_ainr_mdla_mode {
|
||||
MTK_FEATURE_AINR_MDLA_MODE_NONE = 0,
|
||||
MTK_FEATURE_AINR_MDLA_MODE_DRCOUT_16BIT = 1,
|
||||
MTK_FEATURE_AINR_MDLA_MODE_NNOUT_12BIT = 2,
|
||||
MTK_FEATURE_AINR_MDLA_MODE_NNOUT_16BIT = 3,
|
||||
} mtk_platform_metadata_enum_ainr_mdla_mode_t;
|
||||
|
||||
//MTK_ISP_P2_PROCESSED_RAW
|
||||
typedef enum mtk_platform_metadata_enum_p2_processed_raw {
|
||||
MTK_ISP_P2_PROCESSED_RAW_NOT_NEEDED = 0,
|
||||
MTK_ISP_P2_PROCESSED_RAW_NEEDED = 1
|
||||
} mtk_platform_metadata_enum_p2_processed_raw_t;
|
||||
|
||||
//MTK_DUALZOOM_STREAMING_NR
|
||||
typedef enum mtk_platform_metadata_enum_dualzoom_streaming_nr {
|
||||
MTK_DUALZOOM_STREAMING_NR_AUTO = 0,
|
||||
MTK_DUALZOOM_STREAMING_NR_OFF = 1
|
||||
} mtk_platform_metadata_enum_dualzoom_streaming_nr_t;
|
||||
|
||||
//MTK_STAGGER_BLOB_IMGO_ORDER
|
||||
typedef enum mtk_platform_metadata_enum_stagger_blob_imgo_order {
|
||||
MTK_STAGGER_IMGO_NONE = 0,
|
||||
MTK_STAGGER_IMGO_NE = 1,
|
||||
MTK_STAGGER_IMGO_ME = 2,
|
||||
MTK_STAGGER_IMGO_SE = 3
|
||||
} mtk_platform_metadata_enum_stagger_blob_imgo_order_t;
|
||||
|
||||
//MTK_3A_EXIF_FLASH_FIRING_STATUS
|
||||
typedef enum mtk_platform_metadata_enum_3a_exif_flash_firing_status_t {
|
||||
MTK_3A_EXIF_FLASH_FIRING_STATUS_NOT_FIRED = 0,
|
||||
MTK_3A_EXIF_FLASH_FIRING_STATUS_FIRED = 1,
|
||||
} mtk_platform_metadata_enum_3a_exif_flash_firing_status_t;
|
||||
|
||||
//MTK_3A_EXIF_FLASH_RETURN_DETECTION
|
||||
typedef enum mtk_platform_metadata_enum_3a_exif_flash_return_detection_t {
|
||||
MTK_3A_EXIF_FLASH_RETURN_DETECTION_NOT_SUPPORT = 0,
|
||||
MTK_3A_EXIF_FLASH_RETURN_DETECTION_RESERVED = 1,
|
||||
MTK_3A_EXIF_FLASH_RETURN_DETECTION_STROBE_NOT_DETECTED = 2,
|
||||
MTK_3A_EXIF_FLASH_RETURN_DETECTION_STROBE_DETECTED = 3,
|
||||
} mtk_platform_metadata_enum_3a_exif_flash_return_detection_t;
|
||||
|
||||
//MTK_3A_EXIF_FLASH_MODE
|
||||
typedef enum mtk_platform_metadata_enum_3a_exif_flash_mode_t {
|
||||
MTK_3A_EXIF_FLASH_MODE_UNKNOWN = 0,
|
||||
MTK_3A_EXIF_FLASH_MODE_COMPULSORY_FIRING = 1,
|
||||
MTK_3A_EXIF_FLASH_MODE_COMPULSORY_SUPPRESSION = 2,
|
||||
MTK_3A_EXIF_FLASH_MODE_AUTO = 3,
|
||||
} mtk_platform_metadata_enum_3a_exif_flash_mode_t;
|
||||
|
||||
//MTK_3A_EXIF_FLASH_FUNCTION
|
||||
typedef enum mtk_platform_metadata_enum_3a_exif_flash_function_t {
|
||||
MTK_3A_EXIF_FLASH_FUNCTION_SUPPORT = 0,
|
||||
MTK_3A_EXIF_FLASH_FUNCTION_NOT_SUPPORT = 1,
|
||||
} mtk_platform_metadata_enum_3a_exif_flash_function_t;
|
||||
|
||||
//MTK_3A_EXIF_FLASH_REDEYE
|
||||
typedef enum mtk_platform_metadata_enum_3a_exif_flash_redeye_t {
|
||||
MTK_3A_EXIF_FLASH_REDEYE_NOT_SUPPORT = 0,
|
||||
MTK_3A_EXIF_FLASH_REDEYE_SUPPORT = 1,
|
||||
} mtk_platform_metadata_enum_3a_exif_flash_redeye_t;
|
||||
|
||||
//MTK_FEATURE_ABF
|
||||
typedef enum mtk_platform_metadata_enum_abf_mode {
|
||||
MTK_ABF_MODE_OFF = 0,
|
||||
MTK_ABF_MODE_ON,
|
||||
} mtk_platform_metadata_enum_abf_mode_t;
|
||||
|
||||
#endif
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,72 @@
|
||||
#include "hdrplus/hdrplus_pipeline.h"
|
||||
|
||||
int main( int argc, char** argv )
|
||||
{
|
||||
int rotation = atoi(argv[1]);
|
||||
bool frontCamera = atoi(argv[2]) != 0;
|
||||
|
||||
std::vector<std::string> paths;
|
||||
for (int idx = 4; idx < argc; idx++)
|
||||
{
|
||||
paths.push_back(argv[idx]);
|
||||
}
|
||||
|
||||
cv::Mat mat;
|
||||
|
||||
hdrplus::hdrplus_pipeline pipeline;
|
||||
pipeline.run_pipeline( paths, 0, mat);
|
||||
|
||||
if (mat.empty())
|
||||
{
|
||||
printf("run_pipeline return empty mat");
|
||||
}
|
||||
mat = hdrplus::convert16bit2_8bit_(mat.clone());
|
||||
|
||||
if (rotation > 0)
|
||||
{
|
||||
if (rotation == 1) // 0
|
||||
{
|
||||
cv::Mat tempPic;
|
||||
cv::transpose(mat, tempPic);
|
||||
cv::flip(tempPic, mat, 0);
|
||||
}
|
||||
else if (rotation == 2) // 90
|
||||
{
|
||||
cv::Mat tempPic;
|
||||
cv::transpose(mat, tempPic);
|
||||
cv::flip(tempPic, mat, 1);
|
||||
}
|
||||
else if (rotation == 3) // 180
|
||||
{
|
||||
if (frontCamera)
|
||||
{
|
||||
cv::flip(mat, mat, 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
cv::flip(mat, mat, -1);
|
||||
}
|
||||
}
|
||||
else if (rotation == 4) // 270
|
||||
{
|
||||
cv::Mat tempPic;
|
||||
cv::transpose(mat, tempPic);
|
||||
cv::flip(tempPic, mat, 0);
|
||||
}
|
||||
|
||||
}
|
||||
cv::cvtColor(mat, mat, cv::COLOR_RGB2BGR);
|
||||
|
||||
if (mat.empty())
|
||||
{
|
||||
printf("mat is empty before save");
|
||||
}
|
||||
|
||||
bool res = cv::imwrite(argv[3], mat);
|
||||
if (!res)
|
||||
{
|
||||
printf("Failed to write file %s err=%d", argv[3], errno);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
@ -0,0 +1,38 @@
|
||||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
#include <utility> // std::pair
|
||||
#include <opencv2/opencv.hpp> // all opencv header
|
||||
#include "hdrplus/burst.h"
|
||||
|
||||
namespace hdrplus
|
||||
{
|
||||
|
||||
class align
|
||||
{
|
||||
public:
|
||||
align() = default;
|
||||
~align() = default;
|
||||
|
||||
/**
|
||||
* @brief Run alignment on burst of images
|
||||
*
|
||||
* @param burst_images collection of burst images
|
||||
* @param aligements alignment in pixel value pair.
|
||||
* Outer most vector is per alternative image.
|
||||
* Inner most two vector is for horizontle & verticle tiles
|
||||
*/
|
||||
void process( const hdrplus::burst& burst_images, \
|
||||
std::vector<std::vector<std::vector<std::pair<int, int>>>>& aligements );
|
||||
|
||||
private:
|
||||
// From original image to coarse image
|
||||
const std::vector<int> inv_scale_factors = { 1, 2, 4, 4 };
|
||||
const std::vector<int> distances = { 1, 2, 2, 2 }; // L1 / L2 distance
|
||||
const std::vector<int> grayimg_search_radious = { 1, 4, 4, 4 };
|
||||
const std::vector<int> grayimg_tile_sizes = { 16, 16, 16, 8 };
|
||||
const int num_levels = 4;
|
||||
};
|
||||
|
||||
|
||||
} // namespace hdrplus
|
@ -0,0 +1,54 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <utility> // std::pair
|
||||
#include <memory> // std::shared_ptr
|
||||
#include <opencv2/opencv.hpp> // all opencv header
|
||||
#include <libraw/libraw.h>
|
||||
|
||||
namespace hdrplus
|
||||
{
|
||||
|
||||
class MemFile
|
||||
{
|
||||
public:
|
||||
std::vector<uint8_t> content;
|
||||
|
||||
const std::vector<uint8_t> GetConstData() const
|
||||
{
|
||||
return content;
|
||||
}
|
||||
|
||||
std::vector<uint8_t> GetData()
|
||||
{
|
||||
return content;
|
||||
}
|
||||
};
|
||||
|
||||
class bayer_image
|
||||
{
|
||||
public:
|
||||
explicit bayer_image( const std::string& bayer_image_path );
|
||||
explicit bayer_image( const std::vector<uint8_t>& bayer_image_content );
|
||||
explicit bayer_image( std::shared_ptr<MemFile> bayer_image_file );
|
||||
|
||||
~bayer_image() = default;
|
||||
|
||||
std::pair<double, double> get_noise_params() const;
|
||||
|
||||
std::shared_ptr<LibRaw> libraw_processor;
|
||||
cv::Mat raw_image;
|
||||
cv::Mat grayscale_image;
|
||||
int width;
|
||||
int height;
|
||||
int white_level;
|
||||
std::vector<int> black_level_per_channel;
|
||||
float iso;
|
||||
|
||||
private:
|
||||
float baseline_lambda_shot = 3.24 * pow( 10, -4 );
|
||||
float baseline_lambda_read = 4.3 * pow( 10, -6 );
|
||||
};
|
||||
|
||||
} // namespace hdrplus
|
@ -0,0 +1,46 @@
|
||||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <opencv2/opencv.hpp> // all opencv header
|
||||
#include "hdrplus/bayer_image.h"
|
||||
|
||||
namespace hdrplus
|
||||
{
|
||||
|
||||
|
||||
class burst
|
||||
{
|
||||
public:
|
||||
explicit burst( const std::string& burst_path, const std::string& reference_image_path );
|
||||
explicit burst(const std::vector<std::string>& burst_paths, int reference_image_index);
|
||||
explicit burst( const std::vector<std::vector<uint8_t> >& bayer_image_contents, int reference_image_index );
|
||||
explicit burst( const std::vector<std::shared_ptr<MemFile> >& bayer_image_files, int reference_image_index );
|
||||
|
||||
~burst() = default;
|
||||
|
||||
// Reference image index in the array
|
||||
int reference_image_idx;
|
||||
|
||||
// Source bayer images & grayscale unpadded image
|
||||
std::vector<hdrplus::bayer_image> bayer_images;
|
||||
|
||||
// Image padded to upper level tile size (16*2)
|
||||
// Use for alignment, merging, and finishing
|
||||
std::vector<cv::Mat> bayer_images_pad;
|
||||
|
||||
// Padding information
|
||||
std::vector<int> padding_info_bayer;
|
||||
|
||||
// Image padded to upper level tile size (16)
|
||||
// Use for alignment, merging, and finishing
|
||||
std::vector<cv::Mat> grayscale_images_pad;
|
||||
|
||||
// number of image (including reference) in burst
|
||||
int num_images;
|
||||
|
||||
// Bayer image after merging, stored as cv::Mat
|
||||
cv::Mat merged_bayer_image;
|
||||
};
|
||||
|
||||
} // namespace hdrplus
|
@ -0,0 +1,251 @@
|
||||
#pragma once
|
||||
|
||||
#include <opencv2/opencv.hpp> // all opencv header
|
||||
#include <string>
|
||||
#include <fstream>
|
||||
#include <sstream>
|
||||
#include <iostream>
|
||||
#include <unordered_map>
|
||||
#include <hdrplus/bayer_image.h>
|
||||
#include <dirent.h>
|
||||
#include <hdrplus/params.h>
|
||||
#include <hdrplus/burst.h>
|
||||
|
||||
namespace hdrplus
|
||||
{
|
||||
uint16_t uGammaCompress_1pix(float x, float threshold,float gainMin,float gainMax,float exponent);
|
||||
uint16_t uGammaDecompress_1pix(float x, float threshold,float gainMin,float gainMax,float exponent);
|
||||
cv::Mat uGammaCompress_(cv::Mat m,float threshold,float gainMin,float gainMax,float exponent);
|
||||
cv::Mat uGammaDecompress_(cv::Mat m,float threshold,float gainMin,float gainMax,float exponent);
|
||||
cv::Mat gammasRGB(cv::Mat img, bool mode);
|
||||
|
||||
|
||||
class finish
|
||||
{
|
||||
public:
|
||||
cv::Mat mergedBayer; // merged image from Merge Module
|
||||
std::string burstPath; // path to burst images
|
||||
std::vector<std::string> rawPathList; // a list or array of the path to all burst imgs under burst Path
|
||||
int refIdx; // index of the reference img
|
||||
Parameters params;
|
||||
cv::Mat rawReference;
|
||||
// LibRaw libraw_processor_finish;
|
||||
bayer_image* refBayer;
|
||||
|
||||
std::string mergedImgPath;
|
||||
finish()
|
||||
{
|
||||
refBayer = NULL;
|
||||
}
|
||||
|
||||
// please use this initialization after merging part finish
|
||||
finish(std::string burstPath, cv::Mat mergedBayer,int refIdx) {
|
||||
refBayer = NULL;
|
||||
this->refIdx = refIdx;
|
||||
this->burstPath = burstPath;
|
||||
this->mergedBayer = mergedBayer;
|
||||
}
|
||||
|
||||
// for local testing only
|
||||
finish(std::string burstPath, std::string mergedBayerPath,int refIdx){
|
||||
this->refIdx = refIdx;
|
||||
this->burstPath = burstPath;
|
||||
this->mergedBayer = loadFromCSV(mergedBayerPath, CV_16UC1);//
|
||||
load_rawPathList(burstPath);
|
||||
refBayer= new bayer_image(this->rawPathList[refIdx]);
|
||||
this->rawReference = refBayer->raw_image;//;grayscale_image
|
||||
|
||||
// initialize parameters in libraw_processor_finish
|
||||
setLibRawParams();
|
||||
showParams();
|
||||
|
||||
std::cout<<"Finish init() finished!"<<std::endl;
|
||||
}
|
||||
|
||||
|
||||
|
||||
~finish()
|
||||
{
|
||||
if (refBayer != NULL)
|
||||
{
|
||||
delete refBayer;
|
||||
refBayer = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
// finish pipeline func
|
||||
// void process(std::string burstPath, cv::Mat mergedBayer,int refIdx);
|
||||
void process(const hdrplus::burst& burst_images, cv::Mat& finalOutputImage);
|
||||
|
||||
// replace Mat a with Mat b
|
||||
void copy_mat_16U(cv::Mat& A, cv::Mat B);
|
||||
void copy_rawImg2libraw(std::shared_ptr<LibRaw>& libraw_ptr, cv::Mat B);
|
||||
|
||||
// postprocess
|
||||
// cv::Mat postprocess(std::shared_ptr<LibRaw>& libraw_ptr);
|
||||
|
||||
void showImg(cv::Mat img)
|
||||
{
|
||||
int ch = CV_MAT_CN(CV_8UC1);
|
||||
|
||||
// cv::Mat tmp(4208,3120,CV_16UC1);
|
||||
cv::Mat tmp(img);
|
||||
// u_int16_t* ptr_tmp = (u_int16_t*)tmp.data;
|
||||
// u_int16_t* ptr_img = (u_int16_t*)img.data;
|
||||
// // col major to row major
|
||||
// for(int r = 0; r < tmp.rows; r++) {
|
||||
// for(int c = 0; c < tmp.cols; c++) {
|
||||
// *(ptr_tmp+r*tmp.cols+c) = *(ptr_img+c*tmp.rows+r)/2048.0*255.0;
|
||||
// }
|
||||
// }
|
||||
// std::cout<<"height="<<tmp.rows<<std::endl;
|
||||
// std::cout<<"width="<<tmp.cols<<std::endl;
|
||||
// cv::transpose(tmp, tmp);
|
||||
|
||||
u_int16_t* ptr = (u_int16_t*)tmp.data;
|
||||
for(int r = 0; r < tmp.rows; r++) {
|
||||
for(int c = 0; c < tmp.cols; c++) {
|
||||
*(ptr+r*tmp.cols+c) = *(ptr+r*tmp.cols+c)/2048.0*255.0;
|
||||
}
|
||||
}
|
||||
|
||||
tmp = tmp.reshape(ch);
|
||||
tmp.convertTo(tmp, CV_8UC1);
|
||||
cv::imshow("test",tmp);
|
||||
cv::imwrite("test2.jpg", tmp);
|
||||
cv::waitKey(0);
|
||||
std::cout<< this->mergedBayer.size()<<std::endl;
|
||||
}
|
||||
|
||||
void showMat(cv::Mat img){
|
||||
std::cout<<"size="<<img.size()<<std::endl;
|
||||
std::cout<<"type="<<img.type()<<std::endl;
|
||||
}
|
||||
|
||||
void showParams()
|
||||
{
|
||||
std::cout<<"Parameters:"<<std::endl;
|
||||
std::cout<<"tuning_ltmGain = "<<this->params.tuning.ltmGain<<std::endl;
|
||||
std::cout<<"tuning_gtmContrast = "<<this->params.tuning.gtmContrast<<std::endl;
|
||||
for(auto key_val:this->params.flags){
|
||||
std::cout<<key_val.first<<","<<key_val.second<<std::endl;
|
||||
}
|
||||
std::cout<<"demosaic_algorithm = "<<refBayer->libraw_processor->imgdata.params.user_qual<<std::endl;
|
||||
std::cout<<"half_size = "<<refBayer->libraw_processor->imgdata.params.half_size<<std::endl;
|
||||
std::cout<<"use_camera_wb = "<<refBayer->libraw_processor->imgdata.params.use_camera_wb<<std::endl;
|
||||
std::cout<<"use_auto_wb = "<<refBayer->libraw_processor->imgdata.params.use_auto_wb<<std::endl;
|
||||
std::cout<<"no_auto_bright = "<<refBayer->libraw_processor->imgdata.params.no_auto_bright<<std::endl;
|
||||
std::cout<<"output_color = "<<refBayer->libraw_processor->imgdata.params.output_color <<std::endl;
|
||||
std::cout<<"gamma[0] = "<<refBayer->libraw_processor->imgdata.params.gamm[0]<<std::endl;
|
||||
std::cout<<"gamma[1] = "<<refBayer->libraw_processor->imgdata.params.gamm[1]<<std::endl;
|
||||
std::cout<<"output_bps = "<<refBayer->libraw_processor->imgdata.params.output_bps<<std::endl;
|
||||
|
||||
// std::cout<<"demosaic_algorithm = "<<libraw_processor_finish.imgdata.params.user_qual<<std::endl;
|
||||
// std::cout<<"half_size = "<<libraw_processor_finish.imgdata.params.half_size<<std::endl;
|
||||
// std::cout<<"use_camera_wb = "<<libraw_processor_finish.imgdata.params.use_camera_wb<<std::endl;
|
||||
// std::cout<<"use_auto_wb = "<<libraw_processor_finish.imgdata.params.use_auto_wb<<std::endl;
|
||||
// std::cout<<"no_auto_bright = "<<libraw_processor_finish.imgdata.params.no_auto_bright<<std::endl;
|
||||
// std::cout<<"output_color = "<<libraw_processor_finish.imgdata.params.output_color <<std::endl;
|
||||
// std::cout<<"gamma[0] = "<<libraw_processor_finish.imgdata.params.gamm[0]<<std::endl;
|
||||
// std::cout<<"gamma[1] = "<<libraw_processor_finish.imgdata.params.gamm[1]<<std::endl;
|
||||
// std::cout<<"output_bps = "<<libraw_processor_finish.imgdata.params.output_bps<<std::endl;
|
||||
|
||||
std::cout<<"===================="<<std::endl;
|
||||
}
|
||||
|
||||
void showRawPathList(){
|
||||
std::cout<<"RawPathList:"<<std::endl;
|
||||
for(auto pth:this->rawPathList){
|
||||
std::cout<<pth<<std::endl;
|
||||
}
|
||||
std::cout<<"===================="<<std::endl;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
private:
|
||||
cv::Mat loadFromCSV(const std::string& path, int opencv_type)
|
||||
{
|
||||
cv::Mat m;
|
||||
std::ifstream csvFile (path);
|
||||
|
||||
std::string line;
|
||||
|
||||
while (getline(csvFile, line))
|
||||
{
|
||||
std::vector<int> dvals;
|
||||
std::stringstream ss(line);
|
||||
std::string val;
|
||||
// int count=0;
|
||||
while (getline(ss, val, ','))
|
||||
{
|
||||
dvals.push_back(stod(val));//*255.0/2048.0
|
||||
// count++;
|
||||
}
|
||||
// std::cout<<count<<std::endl;
|
||||
cv::Mat mline(dvals, true);
|
||||
cv::transpose(mline, mline);
|
||||
|
||||
m.push_back(mline);
|
||||
}
|
||||
int ch = CV_MAT_CN(opencv_type);
|
||||
|
||||
m = m.reshape(ch);
|
||||
m.convertTo(m, opencv_type);
|
||||
|
||||
return m;
|
||||
}
|
||||
|
||||
void load_rawPathList(std::string burstPath){
|
||||
DIR *pDir; // pointer to root
|
||||
struct dirent *ptr;
|
||||
if (!(pDir = opendir(burstPath.c_str()))) {
|
||||
std::cout<<"root dir not found!"<<std::endl;
|
||||
return;
|
||||
}
|
||||
while ((ptr = readdir(pDir)) != nullptr) {
|
||||
// ptr will move to the next file automatically
|
||||
std::string sub_file = burstPath + "/" + ptr->d_name; // current filepath that ptr points to
|
||||
if (ptr->d_type != 8 && ptr->d_type != 4) { // not normal file or dir
|
||||
return;
|
||||
}
|
||||
// only need normal files
|
||||
if (ptr->d_type == 8) {
|
||||
if (strcmp(ptr->d_name, ".") != 0 && strcmp(ptr->d_name, "..") != 0) {
|
||||
if (strstr(ptr->d_name, ".dng")) {
|
||||
rawPathList.emplace_back(sub_file);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// close root dir
|
||||
closedir(pDir);
|
||||
}
|
||||
|
||||
void setLibRawParams(){
|
||||
refBayer->libraw_processor->imgdata.params.user_qual = params.rawpyArgs.demosaic_algorithm;
|
||||
refBayer->libraw_processor->imgdata.params.half_size = params.rawpyArgs.half_size;
|
||||
refBayer->libraw_processor->imgdata.params.use_camera_wb = params.rawpyArgs.use_camera_wb;
|
||||
refBayer->libraw_processor->imgdata.params.use_auto_wb = params.rawpyArgs.use_auto_wb;
|
||||
refBayer->libraw_processor->imgdata.params.no_auto_bright = params.rawpyArgs.no_auto_bright;
|
||||
refBayer->libraw_processor->imgdata.params.output_color = params.rawpyArgs.output_color;
|
||||
refBayer->libraw_processor->imgdata.params.gamm[0] = params.rawpyArgs.gamma[0];
|
||||
refBayer->libraw_processor->imgdata.params.gamm[1] = params.rawpyArgs.gamma[1];
|
||||
refBayer->libraw_processor->imgdata.params.output_bps = params.rawpyArgs.output_bps;
|
||||
|
||||
// libraw_processor_finish.imgdata.params.user_qual = params.rawpyArgs.demosaic_algorithm;
|
||||
// libraw_processor_finish.imgdata.params.half_size = params.rawpyArgs.half_size;
|
||||
// libraw_processor_finish.imgdata.params.use_camera_wb = params.rawpyArgs.use_camera_wb;
|
||||
// libraw_processor_finish.imgdata.params.use_auto_wb = params.rawpyArgs.use_auto_wb;
|
||||
// libraw_processor_finish.imgdata.params.no_auto_bright = params.rawpyArgs.no_auto_bright;
|
||||
// libraw_processor_finish.imgdata.params.output_color = params.rawpyArgs.output_color;
|
||||
// libraw_processor_finish.imgdata.params.gamm[0] = params.rawpyArgs.gamma[0];
|
||||
// libraw_processor_finish.imgdata.params.gamm[1] = params.rawpyArgs.gamma[1];
|
||||
// libraw_processor_finish.imgdata.params.output_bps = params.rawpyArgs.output_bps;
|
||||
}
|
||||
|
||||
|
||||
};
|
||||
|
||||
} // namespace hdrplus
|
@ -0,0 +1,54 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <opencv2/opencv.hpp> // all opencv header
|
||||
#include "hdrplus/burst.h"
|
||||
#include "hdrplus/align.h"
|
||||
#include "hdrplus/merge.h"
|
||||
#include "hdrplus/finish.h"
|
||||
|
||||
namespace hdrplus
|
||||
{
|
||||
|
||||
inline cv::Mat convert16bit2_8bit_(cv::Mat ans) {
|
||||
if(ans.type()==CV_16UC3){
|
||||
cv::MatIterator_<cv::Vec3w> it, end;
|
||||
for( it = ans.begin<cv::Vec3w>(), end = ans.end<cv::Vec3w>(); it != end; ++it)
|
||||
{
|
||||
// std::cout<<sizeof (*it)[0] <<std::endl;
|
||||
(*it)[0] *=(255.0/USHRT_MAX);
|
||||
(*it)[1] *=(255.0/USHRT_MAX);
|
||||
(*it)[2] *=(255.0/USHRT_MAX);
|
||||
}
|
||||
ans.convertTo(ans, CV_8UC3);
|
||||
}else if(ans.type()==CV_16UC1){
|
||||
u_int16_t* ptr = (u_int16_t*)ans.data;
|
||||
int end = ans.rows*ans.cols;
|
||||
for(int i=0;i<end;i++){
|
||||
*(ptr+i) *=(255.0/USHRT_MAX);
|
||||
}
|
||||
ans.convertTo(ans, CV_8UC1);
|
||||
}else{
|
||||
// std::cout<<"Unsupported Data Type"<<std::endl;
|
||||
}
|
||||
return ans;
|
||||
}
|
||||
|
||||
class hdrplus_pipeline
|
||||
{
|
||||
private:
|
||||
hdrplus::align align_module;
|
||||
hdrplus::merge merge_module;
|
||||
hdrplus::finish finish_module;
|
||||
|
||||
public:
|
||||
void run_pipeline( const std::string& burst_path, const std::string& reference_image_path );
|
||||
bool run_pipeline( const std::vector<std::string>& burst_paths, int reference_image_index, cv::Mat& finalImg );
|
||||
bool run_pipeline( const std::vector<std::vector<uint8_t> >& burst_contents, int reference_image_index, cv::Mat& finalImg );
|
||||
bool run_pipeline( const std::vector<std::shared_ptr<MemFile> >& burst_contents, int reference_image_index, cv::Mat& finalImg );
|
||||
|
||||
hdrplus_pipeline() = default;
|
||||
~hdrplus_pipeline() = default;
|
||||
};
|
||||
|
||||
} // namespace hdrplus
|
@ -0,0 +1,184 @@
|
||||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
#include <opencv2/opencv.hpp> // all opencv header
|
||||
#include <cmath>
|
||||
#include "hdrplus/burst.h"
|
||||
|
||||
#define TILE_SIZE 16
|
||||
#define TEMPORAL_FACTOR 75
|
||||
#define SPATIAL_FACTOR 0.1
|
||||
|
||||
namespace hdrplus
|
||||
{
|
||||
|
||||
class merge
|
||||
{
|
||||
public:
|
||||
int offset = TILE_SIZE / 2;
|
||||
float baseline_lambda_shot = 3.24 * pow( 10, -4 );
|
||||
float baseline_lambda_read = 4.3 * pow( 10, -6 );
|
||||
|
||||
merge() = default;
|
||||
~merge() = default;
|
||||
|
||||
/**
|
||||
* @brief Run alignment on burst of images
|
||||
*
|
||||
* @param burst_images collection of burst images
|
||||
* @param alignments alignment in pixel value pair.
|
||||
* Outer most vector is per alternative image.
|
||||
* Inner most two vector is for horizontal & vertical tiles
|
||||
*/
|
||||
void process( hdrplus::burst& burst_images, \
|
||||
std::vector<std::vector<std::vector<std::pair<int, int>>>>& alignments);
|
||||
|
||||
|
||||
/*
|
||||
std::vector<cv::Mat> get_other_tiles(); //return the other tile list T_1 to T_n
|
||||
|
||||
std::vector<cv::Mat> vector_math(string operation, reference_tile, other_tile_list); //for loop allowing operations across single element and list
|
||||
|
||||
std::vector<cv::Mat> scalar_vector_math(string operation, scalar num, std::vector<cv::Mat> tile_list); //for loop allowing operations across single element and list
|
||||
|
||||
std::vector<cv::Mat> average_vector(std::vector<cv::Mat> tile_list); //take average of vector elements
|
||||
|
||||
*/
|
||||
|
||||
private:
|
||||
float tileRMS(cv::Mat tile) {
|
||||
cv::Mat squared;
|
||||
cv::multiply(tile, tile, squared);
|
||||
return sqrt(cv::mean(squared)[0]);
|
||||
}
|
||||
|
||||
std::vector<float> getNoiseVariance(std::vector<cv::Mat> tiles, float lambda_shot, float lambda_read) {
|
||||
std::vector<float> noise_variance;
|
||||
for (auto tile : tiles) {
|
||||
noise_variance.push_back(lambda_shot * tileRMS(tile) + lambda_read);
|
||||
}
|
||||
return noise_variance;
|
||||
}
|
||||
|
||||
cv::Mat cosineWindow1D(cv::Mat input, int window_size = TILE_SIZE) {
|
||||
cv::Mat output = input.clone();
|
||||
for (int i = 0; i < input.cols; ++i) {
|
||||
output.at<float>(0, i) = 1. / 2. - 1. / 2. * cos(2 * M_PI * (input.at<float>(0, i) + 1 / 2.) / window_size);
|
||||
}
|
||||
return output;
|
||||
}
|
||||
|
||||
cv::Mat cosineWindow2D(cv::Mat tile) {
|
||||
int window_size = tile.rows; // Assuming square tile
|
||||
cv::Mat output_tile = tile.clone();
|
||||
|
||||
cv::Mat window = cv::Mat::zeros(1, window_size, CV_32F);
|
||||
for(int i = 0; i < window_size; ++i) {
|
||||
window.at<float>(i) = i;
|
||||
}
|
||||
|
||||
cv::Mat window_x = cosineWindow1D(window, window_size);
|
||||
window_x = cv::repeat(window_x, window_size, 1);
|
||||
cv::Mat window_2d = window_x.mul(window_x.t());
|
||||
|
||||
cv::Mat window_applied;
|
||||
cv::multiply(tile, window_2d, window_applied, 1, CV_32F);
|
||||
return window_applied;
|
||||
}
|
||||
|
||||
cv::Mat cat2Dtiles(std::vector<std::vector<cv::Mat>> tiles) {
|
||||
std::vector<cv::Mat> rows;
|
||||
for (auto row_tiles : tiles) {
|
||||
cv::Mat row;
|
||||
cv::hconcat(row_tiles, row);
|
||||
rows.push_back(row);
|
||||
}
|
||||
cv::Mat img;
|
||||
cv::vconcat(rows, img);
|
||||
return img;
|
||||
}
|
||||
|
||||
void circshift(cv::Mat &out, const cv::Point &delta)
|
||||
{
|
||||
cv::Size sz = out.size();
|
||||
|
||||
// error checking
|
||||
assert(sz.height > 0 && sz.width > 0);
|
||||
|
||||
// no need to shift
|
||||
if ((sz.height == 1 && sz.width == 1) || (delta.x == 0 && delta.y == 0))
|
||||
return;
|
||||
|
||||
// delta transform
|
||||
int x = delta.x;
|
||||
int y = delta.y;
|
||||
if (x > 0) x = x % sz.width;
|
||||
if (y > 0) y = y % sz.height;
|
||||
if (x < 0) x = x % sz.width + sz.width;
|
||||
if (y < 0) y = y % sz.height + sz.height;
|
||||
|
||||
|
||||
// in case of multiple dimensions
|
||||
std::vector<cv::Mat> planes;
|
||||
split(out, planes);
|
||||
|
||||
for (size_t i = 0; i < planes.size(); i++)
|
||||
{
|
||||
// vertical
|
||||
cv::Mat tmp0, tmp1, tmp2, tmp3;
|
||||
cv::Mat q0(planes[i], cv::Rect(0, 0, sz.width, sz.height - y));
|
||||
cv::Mat q1(planes[i], cv::Rect(0, sz.height - y, sz.width, y));
|
||||
q0.copyTo(tmp0);
|
||||
q1.copyTo(tmp1);
|
||||
tmp0.copyTo(planes[i](cv::Rect(0, y, sz.width, sz.height - y)));
|
||||
tmp1.copyTo(planes[i](cv::Rect(0, 0, sz.width, y)));
|
||||
|
||||
// horizontal
|
||||
cv::Mat q2(planes[i], cv::Rect(0, 0, sz.width - x, sz.height));
|
||||
cv::Mat q3(planes[i], cv::Rect(sz.width - x, 0, x, sz.height));
|
||||
q2.copyTo(tmp2);
|
||||
q3.copyTo(tmp3);
|
||||
tmp2.copyTo(planes[i](cv::Rect(x, 0, sz.width - x, sz.height)));
|
||||
tmp3.copyTo(planes[i](cv::Rect(0, 0, x, sz.height)));
|
||||
}
|
||||
|
||||
cv::merge(planes, out);
|
||||
}
|
||||
|
||||
void fftshift(cv::Mat &out)
|
||||
{
|
||||
cv::Size sz = out.size();
|
||||
cv::Point pt(0, 0);
|
||||
pt.x = (int) floor(sz.width / 2.0);
|
||||
pt.y = (int) floor(sz.height / 2.0);
|
||||
circshift(out, pt);
|
||||
}
|
||||
|
||||
void ifftshift(cv::Mat &out)
|
||||
{
|
||||
cv::Size sz = out.size();
|
||||
cv::Point pt(0, 0);
|
||||
pt.x = (int) ceil(sz.width / 2.0);
|
||||
pt.y = (int) ceil(sz.height / 2.0);
|
||||
circshift(out, pt);
|
||||
}
|
||||
|
||||
std::vector<cv::Mat> getReferenceTiles(cv::Mat reference_image);
|
||||
|
||||
cv::Mat mergeTiles(std::vector<cv::Mat> tiles, int rows, int cols);
|
||||
|
||||
cv::Mat processChannel( hdrplus::burst& burst_images, \
|
||||
std::vector<std::vector<std::vector<std::pair<int, int>>>>& alignments, \
|
||||
cv::Mat channel_image, \
|
||||
std::vector<cv::Mat> alternate_channel_i_list,\
|
||||
float lambda_shot, \
|
||||
float lambda_read);
|
||||
|
||||
//temporal denoise
|
||||
std::vector<cv::Mat> temporal_denoise(std::vector<cv::Mat> tiles, std::vector<std::vector<cv::Mat>> alt_tiles, std::vector<float> noise_variance, float temporal_factor);
|
||||
std::vector<cv::Mat> spatial_denoise(std::vector<cv::Mat> tiles, int num_alts, std::vector<float> noise_variance, float spatial_factor);
|
||||
|
||||
|
||||
};
|
||||
|
||||
} // namespace hdrplus
|
@ -0,0 +1,69 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <memory> // std::shared_ptr
|
||||
#include <opencv2/opencv.hpp> // all opencv header
|
||||
#include <libraw/libraw.h>
|
||||
|
||||
namespace hdrplus
|
||||
{
|
||||
class RawpyArgs{
|
||||
public:
|
||||
int demosaic_algorithm = 3;// 3 - AHD interpolation <->int user_qual
|
||||
bool half_size = false;
|
||||
bool use_camera_wb = true;
|
||||
bool use_auto_wb = false;
|
||||
bool no_auto_bright = true;
|
||||
int output_color = LIBRAW_COLORSPACE_sRGB;
|
||||
int gamma[2] = {1,1}; //# gamma correction not applied by rawpy (not quite understand)
|
||||
int output_bps = 16;
|
||||
};
|
||||
|
||||
class Options{
|
||||
public:
|
||||
std::string input = "";
|
||||
std::string output = "";
|
||||
std::string mode = "full"; //'full' 'align' 'merge' 'finish'
|
||||
int reference = 0;
|
||||
float temporalfactor=75.0;
|
||||
float spatialfactor = 0.1;
|
||||
int ltmGain=-1;
|
||||
double gtmContrast=0.075;
|
||||
int verbose=2; // (0, 1, 2, 3, 4, 5)
|
||||
|
||||
};
|
||||
|
||||
class Tuning{
|
||||
public:
|
||||
std::string ltmGain = "auto";
|
||||
double gtmContrast = 0.075;
|
||||
std::vector<float> sharpenAmount{1,0.5,0.5};
|
||||
std::vector<float> sharpenSigma{1,2,4};
|
||||
std::vector<float> sharpenThreshold{0.02,0.04,0.06};
|
||||
};
|
||||
|
||||
class Parameters{
|
||||
public:
|
||||
std::unordered_map<std::string,bool> flags;
|
||||
|
||||
RawpyArgs rawpyArgs;
|
||||
Options options;
|
||||
Tuning tuning;
|
||||
|
||||
Parameters()
|
||||
{
|
||||
const char* keys[] = {"writeReferenceImage", "writeGammaReference", "writeMergedImage", "writeGammaMerged",
|
||||
"writeShortExposure", "writeLongExposure", "writeFusedExposure", "writeLTMImage",
|
||||
"writeLTMGamma", "writeGTMImage", "writeReferenceFinal", "writeFinalImage"};
|
||||
for (int idx = 0; idx < sizeof(keys) / sizeof(const char*); idx++) {
|
||||
flags[keys[idx]] = true;
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
cv::Mat postprocess(std::shared_ptr<LibRaw>& libraw_ptr, RawpyArgs rawpyArgs);
|
||||
void setParams(std::shared_ptr<LibRaw>& libraw_ptr, RawpyArgs rawpyArgs);
|
||||
|
||||
} // namespace hdrplus
|
@ -0,0 +1,326 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <stdexcept> // std::runtime_error
|
||||
#include <opencv2/opencv.hpp> // all opencv header
|
||||
#include <omp.h>
|
||||
|
||||
// https://stackoverflow.com/questions/63404539/portable-loop-unrolling-with-template-parameter-in-c-with-gcc-icc
|
||||
/// Helper macros for stringification
|
||||
#define TO_STRING_HELPER(X) #X
|
||||
#define TO_STRING(X) TO_STRING_HELPER(X)
|
||||
|
||||
// Define loop unrolling depending on the compiler
|
||||
#if defined(__ICC) || defined(__ICL)
|
||||
#define UNROLL_LOOP(n) _Pragma(TO_STRING(unroll (n)))
|
||||
#elif defined(__clang__)
|
||||
#define UNROLL_LOOP(n) _Pragma(TO_STRING(unroll (n)))
|
||||
#elif defined(__GNUC__) && !defined(__clang__)
|
||||
#define UNROLL_LOOP(n) _Pragma(TO_STRING(GCC unroll (16)))
|
||||
#elif defined(_MSC_BUILD)
|
||||
#pragma message ("Microsoft Visual C++ (MSVC) detected: Loop unrolling not supported!")
|
||||
#define UNROLL_LOOP(n)
|
||||
#else
|
||||
#warning "Unknown compiler: Loop unrolling not supported!"
|
||||
#define UNROLL_LOOP(n)
|
||||
#endif
|
||||
|
||||
|
||||
namespace hdrplus
|
||||
{
|
||||
|
||||
|
||||
template <typename T, int kernel>
|
||||
cv::Mat box_filter_kxk( const cv::Mat& src_image )
|
||||
{
|
||||
const T* src_image_ptr = (T*)src_image.data;
|
||||
int src_height = src_image.size().height;
|
||||
int src_width = src_image.size().width;
|
||||
int src_step = src_image.step1();
|
||||
|
||||
if ( kernel <= 0 )
|
||||
{
|
||||
#ifdef __ANDROID__
|
||||
return cv::Mat();
|
||||
#else
|
||||
throw std::runtime_error(std::string( __FILE__ ) + "::" + __func__ + " box filter only support kernel size >= 1");
|
||||
#endif
|
||||
}
|
||||
|
||||
// int(src_height / kernel) = floor(src_height / kernel)
|
||||
// When input size is not multiplier of kernel, take floor
|
||||
cv::Mat dst_image( src_height / kernel, src_width / kernel, src_image.type() );
|
||||
T* dst_image_ptr = (T*)dst_image.data;
|
||||
int dst_height = dst_image.size().height;
|
||||
int dst_width = dst_image.size().width;
|
||||
int dst_step = dst_image.step1();
|
||||
|
||||
for ( int row_i = 0; row_i < dst_height; ++row_i )
|
||||
{
|
||||
for ( int col_i = 0; col_i < dst_width; col_i++ )
|
||||
{
|
||||
// Take ceiling for rounding
|
||||
T box_sum = T( 0 );
|
||||
|
||||
UNROLL_LOOP( kernel )
|
||||
for ( int kernel_row_i = 0; kernel_row_i < kernel; ++kernel_row_i )
|
||||
{
|
||||
UNROLL_LOOP( kernel )
|
||||
for ( int kernel_col_i = 0; kernel_col_i < kernel; ++kernel_col_i )
|
||||
{
|
||||
box_sum += src_image_ptr[ ( row_i * kernel + kernel_row_i ) * src_step + ( col_i * kernel + kernel_col_i ) ];
|
||||
}
|
||||
}
|
||||
|
||||
// Average by taking ceiling
|
||||
T box_avg = box_sum / T( kernel * kernel );
|
||||
dst_image_ptr[ row_i * dst_step + col_i ] = box_avg;
|
||||
}
|
||||
}
|
||||
|
||||
return dst_image;
|
||||
}
|
||||
|
||||
|
||||
template <typename T, int kernel>
|
||||
cv::Mat downsample_nearest_neighbour( const cv::Mat& src_image )
|
||||
{
|
||||
const T* src_image_ptr = (T*)src_image.data;
|
||||
int src_height = src_image.size().height;
|
||||
int src_width = src_image.size().width;
|
||||
int src_step = src_image.step1();
|
||||
|
||||
// int(src_height / kernel) = floor(src_height / kernel)
|
||||
// When input size is not multiplier of kernel, take floor
|
||||
cv::Mat dst_image = cv::Mat( src_height / kernel, src_width / kernel, src_image.type() );
|
||||
T* dst_image_ptr = (T*)dst_image.data;
|
||||
int dst_height = dst_image.size().height;
|
||||
int dst_width = dst_image.size().width;
|
||||
int dst_step = dst_image.step1();
|
||||
|
||||
// -03 should be enough to optimize below code
|
||||
for ( int row_i = 0; row_i < dst_height; row_i++ )
|
||||
{
|
||||
UNROLL_LOOP( 32 )
|
||||
for ( int col_i = 0; col_i < dst_width; col_i++ )
|
||||
{
|
||||
dst_image_ptr[ row_i * dst_step + col_i ] = \
|
||||
src_image_ptr[ (row_i * kernel) * src_step + (col_i * kernel) ];
|
||||
}
|
||||
}
|
||||
|
||||
return dst_image;
|
||||
}
|
||||
|
||||
|
||||
template< typename T >
|
||||
void print_cvmat( cv::Mat image )
|
||||
{
|
||||
const T* img_ptr = (const T*)image.data;
|
||||
int height = image.size().height;
|
||||
int width = image.size().width;
|
||||
int step = image.step1();
|
||||
int chns = image.channels();
|
||||
|
||||
printf("print_cvmat()::Image of size height = %d, width = %d, step = %d\n", \
|
||||
height, width, step );
|
||||
|
||||
if ( chns == 1 )
|
||||
{
|
||||
for ( int row_i = 0; row_i < height; ++row_i )
|
||||
{
|
||||
int row_i_offset = row_i * step;
|
||||
for ( int col_i = 0; col_i < width; ++col_i )
|
||||
{
|
||||
printf("%3.d ", img_ptr[ row_i_offset + col_i ]);
|
||||
//printf("%3.d ", int( image.at<T>( row_i, col_i ) ) );
|
||||
}
|
||||
printf("\n");
|
||||
}
|
||||
}
|
||||
else if ( chns == 3 )
|
||||
{
|
||||
for ( int row_i = 0; row_i < height; ++row_i )
|
||||
{
|
||||
int row_i_offset = row_i * step;
|
||||
for ( int col_i = 0; col_i < width; ++col_i )
|
||||
{
|
||||
printf("[%3.d, %3.d, %3.d] ", img_ptr[ row_i_offset + col_i * 3 + 0 ], \
|
||||
img_ptr[ row_i_offset + col_i * 3 + 1 ], \
|
||||
img_ptr[ row_i_offset + col_i * 3 + 2 ] );
|
||||
}
|
||||
printf("\n");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
#ifdef __ANDROID__
|
||||
#else
|
||||
throw std::runtime_error("cv::Mat number of channel currently not support to print\n");
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @brief Extract RGB channel seprately from bayer image
|
||||
*
|
||||
* @tparam T data tyoe of bayer image.
|
||||
* @return vector of RGB image. OpenCV internally maintain reference count.
|
||||
* Thus this step won't create deep copy overhead.
|
||||
*
|
||||
* @example extract_rgb_from_bayer<uint16_t>( bayer_img, rgb_vector_container );
|
||||
*/
|
||||
template <typename T>
|
||||
void extract_rgb_from_bayer( const cv::Mat& bayer_img, \
|
||||
cv::Mat& img_ch1, cv::Mat& img_ch2, cv::Mat& img_ch3, cv::Mat& img_ch4 )
|
||||
{
|
||||
const T* bayer_img_ptr = (const T*)bayer_img.data;
|
||||
int bayer_width = bayer_img.size().width;
|
||||
int bayer_height = bayer_img.size().height;
|
||||
int bayer_step = bayer_img.step1();
|
||||
|
||||
if ( bayer_width % 2 != 0 || bayer_height % 2 != 0 )
|
||||
{
|
||||
#ifdef __ANDROID__
|
||||
#else
|
||||
throw std::runtime_error("Bayer image data size incorrect, must be multiplier of 2\n");
|
||||
#endif
|
||||
}
|
||||
|
||||
// RGB image is half the size of bayer image
|
||||
int rgb_width = bayer_width / 2;
|
||||
int rgb_height = bayer_height / 2;
|
||||
img_ch1.create( rgb_height, rgb_width, bayer_img.type() );
|
||||
img_ch2.create( rgb_height, rgb_width, bayer_img.type() );
|
||||
img_ch3.create( rgb_height, rgb_width, bayer_img.type() );
|
||||
img_ch4.create( rgb_height, rgb_width, bayer_img.type() );
|
||||
int rgb_step = img_ch1.step1();
|
||||
|
||||
T* img_ch1_ptr = (T*)img_ch1.data;
|
||||
T* img_ch2_ptr = (T*)img_ch2.data;
|
||||
T* img_ch3_ptr = (T*)img_ch3.data;
|
||||
T* img_ch4_ptr = (T*)img_ch4.data;
|
||||
|
||||
#pragma omp parallel for
|
||||
for ( int rgb_row_i = 0; rgb_row_i < rgb_height; rgb_row_i++ )
|
||||
{
|
||||
int rgb_row_i_offset = rgb_row_i * rgb_step;
|
||||
|
||||
// Every RGB row corresbonding to two Bayer image row
|
||||
int bayer_row_i_offset0 = ( rgb_row_i * 2 + 0 ) * bayer_step; // For RG
|
||||
int bayer_row_i_offset1 = ( rgb_row_i * 2 + 1 ) * bayer_step; // For GB
|
||||
|
||||
for ( int rgb_col_j = 0; rgb_col_j < rgb_width; rgb_col_j++ )
|
||||
{
|
||||
// img_ch1/2/3/4 : (0,0), (1,0), (0,1), (1,1)
|
||||
int bayer_col_i_offset0 = rgb_col_j * 2 + 0;
|
||||
int bayer_col_i_offset1 = rgb_col_j * 2 + 1;
|
||||
|
||||
img_ch1_ptr[ rgb_row_i_offset + rgb_col_j ] = bayer_img_ptr[ bayer_row_i_offset0 + bayer_col_i_offset0 ];
|
||||
img_ch3_ptr[ rgb_row_i_offset + rgb_col_j ] = bayer_img_ptr[ bayer_row_i_offset0 + bayer_col_i_offset1 ];
|
||||
img_ch2_ptr[ rgb_row_i_offset + rgb_col_j ] = bayer_img_ptr[ bayer_row_i_offset1 + bayer_col_i_offset0 ];
|
||||
img_ch4_ptr[ rgb_row_i_offset + rgb_col_j ] = bayer_img_ptr[ bayer_row_i_offset1 + bayer_col_i_offset1 ];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @brief Convert RGB image to gray image through same weight linear combination.
|
||||
* Also support implicit data type conversion.
|
||||
*
|
||||
* @tparam RGB_DTYPE rgb image type (e.g. uint16_t)
|
||||
* @tparam GRAY_DTYPE gray image type (e.g. uint16_t)
|
||||
* @tparam GRAY_CVTYPE opencv gray image type
|
||||
*/
|
||||
template< typename RGB_DTYPE, typename GRAY_DTYPE, int GRAY_CVTYPE >
|
||||
cv::Mat rgb_2_gray( const cv::Mat& rgb_img )
|
||||
{
|
||||
const RGB_DTYPE* rgb_img_ptr = (const RGB_DTYPE*)rgb_img.data;
|
||||
int img_width = rgb_img.size().width;
|
||||
int img_height = rgb_img.size().height;
|
||||
int rgb_img_step = rgb_img.step1();
|
||||
|
||||
// Create output gray cv::Mat
|
||||
cv::Mat gray_img( img_height, img_width, GRAY_CVTYPE );
|
||||
GRAY_DTYPE* gray_img_ptr = (GRAY_DTYPE*)gray_img.data;
|
||||
int gray_img_step = gray_img.step1();
|
||||
|
||||
#pragma omp parallel for
|
||||
for ( int row_i = 0; row_i < img_height; row_i++ )
|
||||
{
|
||||
int rgb_row_i_offset = row_i * rgb_img_step;
|
||||
int gray_row_i_offset = row_i * gray_img_step;
|
||||
|
||||
UNROLL_LOOP( 32 ) // multiplier of cache line size
|
||||
for ( int col_j = 0; col_j < img_width; col_j++ )
|
||||
{
|
||||
GRAY_DTYPE avg_ij(0);
|
||||
|
||||
avg_ij += rgb_img_ptr[ rgb_row_i_offset + (col_j * 3 + 0) ];
|
||||
avg_ij += rgb_img_ptr[ rgb_row_i_offset + (col_j * 3 + 1) ];
|
||||
avg_ij += rgb_img_ptr[ rgb_row_i_offset + (col_j * 3 + 2) ];
|
||||
|
||||
avg_ij /= 3;
|
||||
|
||||
gray_img_ptr[ gray_row_i_offset + col_j ] = avg_ij;
|
||||
}
|
||||
}
|
||||
|
||||
// OpenCV use reference count. Thus return won't create deep copy
|
||||
return gray_img;
|
||||
}
|
||||
|
||||
|
||||
template <typename T>
|
||||
void print_tile( const cv::Mat& img, int tile_size, int start_idx_row, int start_idx_col )
|
||||
{
|
||||
const T* img_ptr = (T*)img.data;
|
||||
int src_step = img.step1();
|
||||
|
||||
for ( int row = start_idx_row; row < tile_size + start_idx_row; ++row )
|
||||
{
|
||||
const T* img_ptr_row = img_ptr + row * src_step;
|
||||
for ( int col = start_idx_col; col < tile_size + start_idx_col; ++col )
|
||||
{
|
||||
printf("%u ", img_ptr_row[ col ] );
|
||||
}
|
||||
printf("\n");
|
||||
}
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
|
||||
template< typename T>
|
||||
void print_img( const cv::Mat& img, int img_height = -1, int img_width = -1 )
|
||||
{
|
||||
const T* img_ptr = (T*)img.data;
|
||||
if ( img_height == -1 && img_width == -1 )
|
||||
{
|
||||
img_height = img.size().height;
|
||||
img_width = img.size().width;
|
||||
}
|
||||
else
|
||||
{
|
||||
img_height = std::min( img.size().height, img_height );
|
||||
img_width = std::min( img.size().width, img_width );
|
||||
}
|
||||
printf("Image size (h=%d, w=%d), Print range (h=0-%d, w=0-%d)]\n", \
|
||||
img.size().height, img.size().width, img_height, img_width );
|
||||
|
||||
int img_step = img.step1();
|
||||
|
||||
for ( int row = 0; row < img_height; ++row )
|
||||
{
|
||||
const T* img_ptr_row = img_ptr + row * img_step;
|
||||
for ( int col = 0; col < img_width; ++col )
|
||||
{
|
||||
printf("%u ", img_ptr_row[ col ]);
|
||||
}
|
||||
printf("\n");
|
||||
}
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
} // namespace hdrplus
|
@ -0,0 +1,994 @@
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <limits>
|
||||
#include <cstdio>
|
||||
#include <utility> // std::make_pair
|
||||
#include <stdexcept> // std::runtime_error
|
||||
#include <opencv2/opencv.hpp> // all opencv header
|
||||
#include <omp.h>
|
||||
#include "hdrplus/align.h"
|
||||
#include "hdrplus/burst.h"
|
||||
#include "hdrplus/utility.h"
|
||||
|
||||
namespace hdrplus
|
||||
{
|
||||
|
||||
// Function declration
|
||||
static void build_per_grayimg_pyramid( \
|
||||
std::vector<cv::Mat>& images_pyramid, \
|
||||
const cv::Mat& src_image, \
|
||||
const std::vector<int>& inv_scale_factors );
|
||||
|
||||
|
||||
template< int pyramid_scale_factor_prev_curr, int tilesize_scale_factor_prev_curr, int tile_size >
|
||||
static void build_upsampled_prev_aligement( \
|
||||
const std::vector<std::vector<std::pair<int, int>>>& src_alignment, \
|
||||
std::vector<std::vector<std::pair<int, int>>>& dst_alignment, \
|
||||
int num_tiles_h, int num_tiles_w, \
|
||||
const cv::Mat& ref_img, const cv::Mat& alt_img, \
|
||||
bool consider_nbr = false );
|
||||
|
||||
|
||||
template< typename data_type, typename return_type, int tile_size >
|
||||
static unsigned long long l1_distance( const cv::Mat& img1, const cv::Mat& img2, \
|
||||
int img1_tile_row_start_idx, int img1_tile_col_start_idx, \
|
||||
int img2_tile_row_start_idx, int img2_tile_col_start_idx );
|
||||
|
||||
|
||||
template< typename data_type, typename return_type, int tile_size >
|
||||
static return_type l2_distance( const cv::Mat& img1, const cv::Mat& img2, \
|
||||
int img1_tile_row_start_idx, int img1_tile_col_start_idx, \
|
||||
int img2_tile_row_start_idx, int img2_tile_col_start_idx );
|
||||
|
||||
|
||||
static void align_image_level( \
|
||||
const cv::Mat& ref_img, \
|
||||
const cv::Mat& alt_img, \
|
||||
std::vector<std::vector<std::pair<int, int>>>& prev_aligement, \
|
||||
std::vector<std::vector<std::pair<int, int>>>& curr_alignment, \
|
||||
int scale_factor_prev_curr, \
|
||||
int curr_tile_size, \
|
||||
int prev_tile_size, \
|
||||
int search_radiou, \
|
||||
int distance_type );
|
||||
|
||||
|
||||
// Function Implementations
|
||||
|
||||
|
||||
// static function only visible within file
|
||||
static void build_per_grayimg_pyramid( \
|
||||
std::vector<cv::Mat>& images_pyramid, \
|
||||
const cv::Mat& src_image, \
|
||||
const std::vector<int>& inv_scale_factors )
|
||||
{
|
||||
// #ifndef NDEBUG
|
||||
// printf("%s::%s build_per_grayimg_pyramid start with scale factor : ", __FILE__, __func__ );
|
||||
// for ( int i = 0; i < inv_scale_factors.size(); ++i )
|
||||
// {
|
||||
// printf("%d ", inv_scale_factors.at( i ));
|
||||
// }
|
||||
// printf("\n");
|
||||
// #endif
|
||||
|
||||
images_pyramid.resize( inv_scale_factors.size() );
|
||||
|
||||
for ( size_t i = 0; i < inv_scale_factors.size(); ++i )
|
||||
{
|
||||
cv::Mat blur_image;
|
||||
cv::Mat downsample_image;
|
||||
|
||||
switch ( inv_scale_factors[ i ] )
|
||||
{
|
||||
case 1:
|
||||
images_pyramid[ i ] = src_image.clone();
|
||||
// cv::Mat use reference count, will not create deep copy
|
||||
downsample_image = src_image;
|
||||
break;
|
||||
case 2:
|
||||
// printf("(2) downsample with gaussian sigma %.2f", inv_scale_factors[ i ] * 0.5 );
|
||||
// // Gaussian blur
|
||||
cv::GaussianBlur( images_pyramid.at( i-1 ), blur_image, cv::Size(0, 0), inv_scale_factors[ i ] * 0.5 );
|
||||
|
||||
// // Downsample
|
||||
downsample_image = downsample_nearest_neighbour<uint16_t, 2>( blur_image );
|
||||
// downsample_image = downsample_nearest_neighbour<uint16_t, 2>( images_pyramid.at( i-1 ) );
|
||||
|
||||
// Add
|
||||
images_pyramid.at( i ) = downsample_image.clone();
|
||||
|
||||
break;
|
||||
case 4:
|
||||
// printf("(4) downsample with gaussian sigma %.2f", inv_scale_factors[ i ] * 0.5 );
|
||||
cv::GaussianBlur( images_pyramid.at( i-1 ), blur_image, cv::Size(0, 0), inv_scale_factors[ i ] * 0.5 );
|
||||
downsample_image = downsample_nearest_neighbour<uint16_t, 4>( blur_image );
|
||||
// downsample_image = downsample_nearest_neighbour<uint16_t, 4>( images_pyramid.at( i-1 ) );
|
||||
images_pyramid.at( i ) = downsample_image.clone();
|
||||
break;
|
||||
default:
|
||||
#ifdef __ANDROID__
|
||||
break;
|
||||
#else
|
||||
throw std::runtime_error("inv scale factor " + std::to_string( inv_scale_factors[ i ]) + "invalid" );
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static bool operator!=( const std::pair<int, int>& lhs, const std::pair<int, int>& rhs )
|
||||
{
|
||||
return lhs.first != rhs.first || lhs.second != rhs.second;
|
||||
}
|
||||
|
||||
|
||||
template< int pyramid_scale_factor_prev_curr, int tilesize_scale_factor_prev_curr, int tile_size >
|
||||
static void build_upsampled_prev_aligement( \
|
||||
const std::vector<std::vector<std::pair<int, int>>>& src_alignment, \
|
||||
std::vector<std::vector<std::pair<int, int>>>& dst_alignment, \
|
||||
int num_tiles_h, int num_tiles_w, \
|
||||
const cv::Mat& ref_img, const cv::Mat& alt_img, \
|
||||
bool consider_nbr )
|
||||
{
|
||||
int src_num_tiles_h = src_alignment.size();
|
||||
int src_num_tiles_w = src_alignment[ 0 ].size();
|
||||
|
||||
constexpr int repeat_factor = pyramid_scale_factor_prev_curr / tilesize_scale_factor_prev_curr;
|
||||
|
||||
// printf("build_upsampled_prev_aligement with scale factor %d, repeat factor %d, tile size factor %d\n", \
|
||||
// pyramid_scale_factor_prev_curr, repeat_factor, tilesize_scale_factor_prev_curr );
|
||||
|
||||
int dst_num_tiles_main_h = src_num_tiles_h * repeat_factor;
|
||||
int dst_num_tiles_main_w = src_num_tiles_w * repeat_factor;
|
||||
|
||||
if ( dst_num_tiles_main_h > num_tiles_h || dst_num_tiles_main_w > num_tiles_w )
|
||||
{
|
||||
#ifdef __ANDROID__
|
||||
return;
|
||||
#else
|
||||
throw std::runtime_error("current level number of tiles smaller than upsampled tiles\n");
|
||||
#endif
|
||||
}
|
||||
|
||||
// Allocate data for dst_alignment
|
||||
// NOTE: number of tiles h, number of tiles w might be different from dst_num_tiles_main_h, dst_num_tiles_main_w
|
||||
// For tiles between num_tile_h and dst_num_tiles_main_h, use (0,0)
|
||||
dst_alignment.resize( num_tiles_h, std::vector<std::pair<int, int>>( num_tiles_w, std::pair<int, int>(0, 0) ) );
|
||||
|
||||
// Upsample alignment
|
||||
#pragma omp parallel for collapse(2)
|
||||
for ( int row_i = 0; row_i < src_num_tiles_h; row_i++ )
|
||||
{
|
||||
for ( int col_i = 0; col_i < src_num_tiles_w; col_i++ )
|
||||
{
|
||||
// Scale alignment
|
||||
std::pair<int, int> align_i = src_alignment[ row_i ][ col_i ];
|
||||
align_i.first *= pyramid_scale_factor_prev_curr;
|
||||
align_i.second *= pyramid_scale_factor_prev_curr;
|
||||
|
||||
// repeat
|
||||
UNROLL_LOOP( repeat_factor )
|
||||
for ( int repeat_row_i = 0; repeat_row_i < repeat_factor; ++repeat_row_i )
|
||||
{
|
||||
int repeat_row_i_offset = row_i * repeat_factor + repeat_row_i;
|
||||
UNROLL_LOOP( repeat_factor )
|
||||
for ( int repeat_col_i = 0; repeat_col_i < repeat_factor; ++repeat_col_i )
|
||||
{
|
||||
int repeat_col_i_offset = col_i * repeat_factor + repeat_col_i;
|
||||
dst_alignment[ repeat_row_i_offset ][ repeat_col_i_offset ] = align_i;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ( consider_nbr )
|
||||
{
|
||||
// Copy consurtctor
|
||||
std::vector<std::vector<std::pair<int, int>>> upsampled_alignment{ dst_alignment };
|
||||
|
||||
// Distance function
|
||||
unsigned long long (*distance_func_ptr)(const cv::Mat&, const cv::Mat&, int, int, int, int) = \
|
||||
&l1_distance<uint16_t, unsigned long long, tile_size>;
|
||||
|
||||
#pragma omp parallel for collapse(2)
|
||||
for ( int tile_row_i = 0; tile_row_i < num_tiles_h; tile_row_i++ )
|
||||
{
|
||||
for ( int tile_col_i = 0; tile_col_i < num_tiles_w; tile_col_i++ )
|
||||
{
|
||||
const auto& curr_align_i = upsampled_alignment[ tile_row_i ][ tile_col_i ];
|
||||
|
||||
// Container for nbr alignment pair
|
||||
std::vector<std::pair<int, int>> nbrs_align_i;
|
||||
|
||||
// Consider 4 neighbour's alignment
|
||||
// Only compute distance if alignment is different
|
||||
if ( tile_col_i > 0 )
|
||||
{
|
||||
const auto& nbr1_align_i = upsampled_alignment[ tile_row_i + 0 ][ tile_col_i - 1 ];
|
||||
if ( curr_align_i != nbr1_align_i ) nbrs_align_i.emplace_back( nbr1_align_i );
|
||||
}
|
||||
|
||||
if ( tile_col_i < num_tiles_w - 1 )
|
||||
{
|
||||
const auto& nbr2_align_i = upsampled_alignment[ tile_row_i + 0 ][ tile_col_i + 1 ];
|
||||
if ( curr_align_i != nbr2_align_i ) nbrs_align_i.emplace_back( nbr2_align_i );
|
||||
}
|
||||
|
||||
if ( tile_row_i > 0 )
|
||||
{
|
||||
const auto& nbr3_align_i = upsampled_alignment[ tile_row_i - 1 ][ tile_col_i + 0 ];
|
||||
if ( curr_align_i != nbr3_align_i ) nbrs_align_i.emplace_back( nbr3_align_i );
|
||||
}
|
||||
|
||||
if ( tile_row_i < num_tiles_h - 1 )
|
||||
{
|
||||
const auto& nbr4_align_i = upsampled_alignment[ tile_row_i + 1 ][ tile_col_i + 0 ];
|
||||
if ( curr_align_i != nbr4_align_i ) nbrs_align_i.emplace_back( nbr4_align_i );
|
||||
}
|
||||
|
||||
// If there is a nbr alignment that need to be considered. Compute distance
|
||||
if ( ! nbrs_align_i.empty() )
|
||||
{
|
||||
int ref_tile_row_start_idx_i = tile_row_i * tile_size / 2;
|
||||
int ref_tile_col_start_idx_i = tile_col_i * tile_size / 2;
|
||||
|
||||
// curr_align_i's distance
|
||||
auto curr_align_i_distance = distance_func_ptr(
|
||||
ref_img, alt_img, \
|
||||
ref_tile_row_start_idx_i, \
|
||||
ref_tile_col_start_idx_i, \
|
||||
ref_tile_row_start_idx_i + curr_align_i.first, \
|
||||
ref_tile_col_start_idx_i + curr_align_i.second );
|
||||
|
||||
for ( const auto& nbr_align_i : nbrs_align_i )
|
||||
{
|
||||
auto nbr_align_i_distance = distance_func_ptr(
|
||||
ref_img, alt_img, \
|
||||
ref_tile_row_start_idx_i, \
|
||||
ref_tile_col_start_idx_i, \
|
||||
ref_tile_row_start_idx_i + nbr_align_i.first, \
|
||||
ref_tile_col_start_idx_i + nbr_align_i.second );
|
||||
|
||||
if ( nbr_align_i_distance < curr_align_i_distance )
|
||||
{
|
||||
#ifdef NDEBUG
|
||||
printf("tile [%d, %d] update align, prev align (%d, %d) curr align (%d, %d), prev distance %d curr distance %d\n", \
|
||||
tile_row_i, tile_col_i, \
|
||||
curr_align_i.first, curr_align_i.second, \
|
||||
nbr_align_i.first, nbr_align_i.second, \
|
||||
int(curr_align_i_distance), int(nbr_align_i_distance) );
|
||||
#endif
|
||||
|
||||
dst_alignment[ tile_row_i ][ tile_col_i ] = nbr_align_i;
|
||||
curr_align_i_distance = nbr_align_i_distance;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Set tilesize as template argument for better compiler optimization result.
|
||||
template< typename data_type, typename return_type, int tile_size >
|
||||
static unsigned long long l1_distance( const cv::Mat& img1, const cv::Mat& img2, \
|
||||
int img1_tile_row_start_idx, int img1_tile_col_start_idx, \
|
||||
int img2_tile_row_start_idx, int img2_tile_col_start_idx )
|
||||
{
|
||||
#define CUSTOME_ABS( x ) ( x ) > 0 ? ( x ) : - ( x )
|
||||
|
||||
const data_type* img1_ptr = (const data_type*)img1.data;
|
||||
const data_type* img2_ptr = (const data_type*)img2.data;
|
||||
|
||||
int img1_step = img1.step1();
|
||||
int img2_step = img2.step1();
|
||||
|
||||
int img1_width = img1.size().width;
|
||||
int img1_height = img1.size().height;
|
||||
|
||||
int img2_width = img2.size().width;
|
||||
int img2_height = img2.size().height;
|
||||
|
||||
// Range check for safety
|
||||
if ( img1_tile_row_start_idx < 0 || img1_tile_row_start_idx > img1_height - tile_size )
|
||||
{
|
||||
#ifdef __ANDROID__
|
||||
return 0;
|
||||
#else
|
||||
throw std::runtime_error("l1 distance img1_tile_row_start_idx" + std::to_string( img1_tile_row_start_idx ) + \
|
||||
" out of valid range (0, " + std::to_string( img1_height - tile_size ) + ")\n" );
|
||||
#endif
|
||||
}
|
||||
|
||||
if ( img1_tile_col_start_idx < 0 || img1_tile_col_start_idx > img1_width - tile_size )
|
||||
{
|
||||
#ifdef __ANDROID__
|
||||
return 0;
|
||||
#else
|
||||
throw std::runtime_error("l1 distance img1_tile_col_start_idx" + std::to_string( img1_tile_col_start_idx ) + \
|
||||
" out of valid range (0, " + std::to_string( img1_width - tile_size ) + ")\n" );
|
||||
#endif
|
||||
}
|
||||
|
||||
if ( img2_tile_row_start_idx < 0 || img2_tile_row_start_idx > img2_height - tile_size )
|
||||
{
|
||||
#ifdef __ANDROID__
|
||||
return 0;
|
||||
#else
|
||||
throw std::runtime_error("l1 distance img2_tile_row_start_idx out of valid range\n");
|
||||
#endif
|
||||
}
|
||||
|
||||
if ( img2_tile_col_start_idx < 0 || img2_tile_col_start_idx > img2_width - tile_size )
|
||||
{
|
||||
#ifdef __ANDROID__
|
||||
return 0;
|
||||
#else
|
||||
throw std::runtime_error("l1 distance img2_tile_col_start_idx out of valid range\n");
|
||||
#endif
|
||||
}
|
||||
|
||||
return_type sum(0);
|
||||
|
||||
UNROLL_LOOP( tile_size )
|
||||
for ( int row_i = 0; row_i < tile_size; ++row_i )
|
||||
{
|
||||
const data_type* img1_ptr_row_i = img1_ptr + (img1_tile_row_start_idx + row_i) * img1_step + img1_tile_col_start_idx;
|
||||
const data_type* img2_ptr_row_i = img2_ptr + (img2_tile_row_start_idx + row_i) * img2_step + img2_tile_col_start_idx;
|
||||
|
||||
UNROLL_LOOP( tile_size )
|
||||
for ( int col_i = 0; col_i < tile_size; ++col_i )
|
||||
{
|
||||
data_type l1 = CUSTOME_ABS( img1_ptr_row_i[ col_i ] - img2_ptr_row_i[ col_i ] );
|
||||
sum += l1;
|
||||
}
|
||||
}
|
||||
|
||||
#undef CUSTOME_ABS
|
||||
|
||||
return sum;
|
||||
}
|
||||
|
||||
|
||||
template< typename data_type, typename return_type, int tile_size >
|
||||
static return_type l2_distance( const cv::Mat& img1, const cv::Mat& img2, \
|
||||
int img1_tile_row_start_idx, int img1_tile_col_start_idx, \
|
||||
int img2_tile_row_start_idx, int img2_tile_col_start_idx )
|
||||
{
|
||||
#define CUSTOME_ABS( x ) ( x ) > 0 ? ( x ) : - ( x )
|
||||
|
||||
const data_type* img1_ptr = (const data_type*)img1.data;
|
||||
const data_type* img2_ptr = (const data_type*)img2.data;
|
||||
|
||||
int img1_step = img1.step1();
|
||||
int img2_step = img2.step1();
|
||||
|
||||
int img1_width = img1.size().width;
|
||||
int img1_height = img1.size().height;
|
||||
|
||||
int img2_width = img2.size().width;
|
||||
int img2_height = img2.size().height;
|
||||
|
||||
// Range check for safety
|
||||
if ( img1_tile_row_start_idx < 0 || img1_tile_row_start_idx > img1_height - tile_size )
|
||||
{
|
||||
#ifdef __ANDROID__
|
||||
return 0;
|
||||
#else
|
||||
throw std::runtime_error("l2 distance img1_tile_row_start_idx" + std::to_string( img1_tile_row_start_idx ) + \
|
||||
" out of valid range (0, " + std::to_string( img1_height - tile_size ) + ")\n" );
|
||||
#endif
|
||||
}
|
||||
|
||||
if ( img1_tile_col_start_idx < 0 || img1_tile_col_start_idx > img1_width - tile_size )
|
||||
{
|
||||
#ifdef __ANDROID__
|
||||
return 0;
|
||||
#else
|
||||
throw std::runtime_error("l2 distance img1_tile_col_start_idx" + std::to_string( img1_tile_col_start_idx ) + \
|
||||
" out of valid range (0, " + std::to_string( img1_width - tile_size ) + ")\n" );
|
||||
#endif
|
||||
}
|
||||
|
||||
if ( img2_tile_row_start_idx < 0 || img2_tile_row_start_idx > img2_height - tile_size )
|
||||
{
|
||||
#ifdef __ANDROID__
|
||||
return 0;
|
||||
#else
|
||||
throw std::runtime_error("l2 distance img2_tile_row_start_idx out of valid range\n");
|
||||
#endif
|
||||
}
|
||||
|
||||
if ( img2_tile_col_start_idx < 0 || img2_tile_col_start_idx > img2_width - tile_size )
|
||||
{
|
||||
#ifdef __ANDROID__
|
||||
return 0;
|
||||
#else
|
||||
throw std::runtime_error("l2 distance img2_tile_col_start_idx out of valid range\n");
|
||||
#endif
|
||||
}
|
||||
|
||||
// printf("Search two tile with ref : \n");
|
||||
// print_tile<data_type>( img1, tile_size, img1_tile_row_start_idx, img1_tile_col_start_idx );
|
||||
// printf("Search two tile with alt :\n");
|
||||
// print_tile<data_type>( img2, tile_size, img2_tile_row_start_idx, img2_tile_col_start_idx );
|
||||
|
||||
return_type sum(0);
|
||||
|
||||
UNROLL_LOOP( tile_size )
|
||||
for ( int row_i = 0; row_i < tile_size; ++row_i )
|
||||
{
|
||||
const data_type* img1_ptr_row_i = img1_ptr + (img1_tile_row_start_idx + row_i) * img1_step + img1_tile_col_start_idx;
|
||||
const data_type* img2_ptr_row_i = img2_ptr + (img2_tile_row_start_idx + row_i) * img2_step + img2_tile_col_start_idx;
|
||||
|
||||
UNROLL_LOOP( tile_size )
|
||||
for ( int col_i = 0; col_i < tile_size; ++col_i )
|
||||
{
|
||||
data_type l1 = CUSTOME_ABS( img1_ptr_row_i[ col_i ] - img2_ptr_row_i[ col_i ] );
|
||||
sum += ( l1 * l1 );
|
||||
}
|
||||
}
|
||||
|
||||
#undef CUSTOME_ABS
|
||||
|
||||
return sum;
|
||||
}
|
||||
|
||||
|
||||
template<typename T, int tile_size>
|
||||
static cv::Mat extract_img_tile( const cv::Mat& img, int img_tile_row_start_idx, int img_tile_col_start_idx )
|
||||
{
|
||||
const T* img_ptr = (const T*)img.data;
|
||||
int img_width = img.size().width;
|
||||
int img_height = img.size().height;
|
||||
int img_step = img.step1();
|
||||
|
||||
if ( img_tile_row_start_idx < 0 || img_tile_row_start_idx > img_height - tile_size )
|
||||
{
|
||||
#ifdef __ANDROID__
|
||||
return cv::Mat();
|
||||
#else
|
||||
throw std::runtime_error("extract_img_tile img_tile_row_start_idx " + std::to_string( img_tile_row_start_idx ) + \
|
||||
" out of valid range (0, " + std::to_string( img_height - tile_size ) + ")\n" );
|
||||
#endif
|
||||
}
|
||||
|
||||
if ( img_tile_col_start_idx < 0 || img_tile_col_start_idx > img_width - tile_size )
|
||||
{
|
||||
#ifdef __ANDROID__
|
||||
return cv::Mat();
|
||||
#else
|
||||
throw std::runtime_error("extract_img_tile img_tile_col_start_idx " + std::to_string( img_tile_col_start_idx ) + \
|
||||
" out of valid range (0, " + std::to_string( img_width - tile_size ) + ")\n" );
|
||||
#endif
|
||||
}
|
||||
|
||||
cv::Mat img_tile( tile_size, tile_size, img.type() );
|
||||
T* img_tile_ptr = (T*)img_tile.data;
|
||||
int img_tile_step = img_tile.step1();
|
||||
|
||||
UNROLL_LOOP( tile_size )
|
||||
for ( int row_i = 0; row_i < tile_size; ++row_i )
|
||||
{
|
||||
const T* img_ptr_row_i = img_ptr + img_step * ( img_tile_row_start_idx + row_i );
|
||||
T* img_tile_ptr_row_i = img_tile_ptr + img_tile_step * row_i;
|
||||
|
||||
UNROLL_LOOP( tile_size )
|
||||
for ( int col_i = 0; col_i < tile_size; ++col_i )
|
||||
{
|
||||
img_tile_ptr_row_i[ col_i ] = img_ptr_row_i[ img_tile_col_start_idx + col_i ];
|
||||
}
|
||||
}
|
||||
|
||||
return img_tile;
|
||||
}
|
||||
|
||||
|
||||
void align_image_level( \
|
||||
const cv::Mat& ref_img, \
|
||||
const cv::Mat& alt_img, \
|
||||
std::vector<std::vector<std::pair<int, int>>>& prev_aligement, \
|
||||
std::vector<std::vector<std::pair<int, int>>>& curr_alignment, \
|
||||
int scale_factor_prev_curr, \
|
||||
int curr_tile_size, \
|
||||
int prev_tile_size, \
|
||||
int search_radiou, \
|
||||
int distance_type )
|
||||
{
|
||||
// Every align image level share the same distance function.
|
||||
// Use function ptr to reduce if else overhead inside for loop
|
||||
unsigned long long (*distance_func_ptr)(const cv::Mat&, const cv::Mat&, int, int, int, int) = nullptr;
|
||||
|
||||
if ( distance_type == 1 ) // l1 distance
|
||||
{
|
||||
if ( curr_tile_size == 8 )
|
||||
{
|
||||
distance_func_ptr = &l1_distance<uint16_t, unsigned long long, 8>;
|
||||
}
|
||||
else if ( curr_tile_size == 16 )
|
||||
{
|
||||
distance_func_ptr = &l1_distance<uint16_t, unsigned long long, 16>;
|
||||
}
|
||||
}
|
||||
else if ( distance_type == 2 ) // l2 distance
|
||||
{
|
||||
if ( curr_tile_size == 8 )
|
||||
{
|
||||
distance_func_ptr = &l2_distance<uint16_t, unsigned long long, 8>;
|
||||
}
|
||||
else if ( curr_tile_size == 16 )
|
||||
{
|
||||
distance_func_ptr = &l2_distance<uint16_t, unsigned long long, 16>;
|
||||
}
|
||||
}
|
||||
|
||||
// Every level share the same upsample function
|
||||
void (*upsample_alignment_func_ptr)(const std::vector<std::vector<std::pair<int, int>>>&, \
|
||||
std::vector<std::vector<std::pair<int, int>>>&, \
|
||||
int, int, const cv::Mat&, const cv::Mat&, bool) = nullptr;
|
||||
if ( scale_factor_prev_curr == 2 )
|
||||
{
|
||||
if ( curr_tile_size / prev_tile_size == 2 )
|
||||
{
|
||||
if ( curr_tile_size == 8 )
|
||||
{
|
||||
upsample_alignment_func_ptr = &build_upsampled_prev_aligement<2, 2, 8>;
|
||||
}
|
||||
else if ( curr_tile_size == 16 )
|
||||
{
|
||||
upsample_alignment_func_ptr = &build_upsampled_prev_aligement<2, 2, 16>;
|
||||
}
|
||||
else
|
||||
{
|
||||
#ifdef __ANDROID__
|
||||
return;
|
||||
#else
|
||||
throw std::runtime_error("Something wrong with upsampling function setting\n");
|
||||
#endif
|
||||
}
|
||||
|
||||
}
|
||||
else if ( curr_tile_size / prev_tile_size == 1 )
|
||||
{
|
||||
if ( curr_tile_size == 8 )
|
||||
{
|
||||
upsample_alignment_func_ptr = &build_upsampled_prev_aligement<2, 1, 8>;
|
||||
}
|
||||
else if ( curr_tile_size == 16 )
|
||||
{
|
||||
upsample_alignment_func_ptr = &build_upsampled_prev_aligement<2, 1, 16>;
|
||||
}
|
||||
else
|
||||
{
|
||||
#ifdef __ANDROID__
|
||||
return;
|
||||
#else
|
||||
throw std::runtime_error("Something wrong with upsampling function setting\n");
|
||||
#endif
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
#ifdef __ANDROID__
|
||||
return;
|
||||
#else
|
||||
throw std::runtime_error("Something wrong with upsampling function setting\n");
|
||||
#endif
|
||||
}
|
||||
}
|
||||
else if ( scale_factor_prev_curr == 4 )
|
||||
{
|
||||
if ( curr_tile_size / prev_tile_size == 2 )
|
||||
{
|
||||
if ( curr_tile_size == 8 )
|
||||
{
|
||||
upsample_alignment_func_ptr = &build_upsampled_prev_aligement<4, 2, 8>;
|
||||
}
|
||||
else if ( curr_tile_size == 16 )
|
||||
{
|
||||
upsample_alignment_func_ptr = &build_upsampled_prev_aligement<4, 2, 16>;
|
||||
}
|
||||
else
|
||||
{
|
||||
#ifdef __ANDROID__
|
||||
return;
|
||||
#else
|
||||
throw std::runtime_error("Something wrong with upsampling function setting\n");
|
||||
#endif
|
||||
}
|
||||
|
||||
}
|
||||
else if ( curr_tile_size / prev_tile_size == 1 )
|
||||
{
|
||||
if ( curr_tile_size == 8 )
|
||||
{
|
||||
upsample_alignment_func_ptr = &build_upsampled_prev_aligement<4, 1, 8>;
|
||||
}
|
||||
else if ( curr_tile_size == 16 )
|
||||
{
|
||||
upsample_alignment_func_ptr = &build_upsampled_prev_aligement<4, 1, 16>;
|
||||
}
|
||||
else
|
||||
{
|
||||
#ifdef __ANDROID__
|
||||
return;
|
||||
#else
|
||||
throw std::runtime_error("Something wrong with upsampling function setting\n");
|
||||
#endif
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
#ifdef __ANDROID__
|
||||
return;
|
||||
#else
|
||||
throw std::runtime_error("Something wrong with upsampling function setting\n");
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Function to extract reference image tile for memory cache
|
||||
cv::Mat (*extract_ref_img_tile)(const cv::Mat&, int, int) = nullptr;
|
||||
if ( curr_tile_size == 8 )
|
||||
{
|
||||
extract_ref_img_tile = &extract_img_tile<uint16_t, 8>;
|
||||
}
|
||||
else if ( curr_tile_size == 16 )
|
||||
{
|
||||
extract_ref_img_tile = &extract_img_tile<uint16_t, 16>;
|
||||
}
|
||||
|
||||
// Function to extract search image tile for memory cache
|
||||
cv::Mat (*extract_alt_img_search)(const cv::Mat&, int, int) = nullptr;
|
||||
if ( curr_tile_size == 8 )
|
||||
{
|
||||
if ( search_radiou == 1 )
|
||||
{
|
||||
extract_alt_img_search = &extract_img_tile<uint16_t, 8+1*2>;
|
||||
}
|
||||
else if ( search_radiou == 4 )
|
||||
{
|
||||
extract_alt_img_search = &extract_img_tile<uint16_t, 8+4*2>;
|
||||
}
|
||||
}
|
||||
else if ( curr_tile_size == 16 )
|
||||
{
|
||||
if ( search_radiou == 1 )
|
||||
{
|
||||
extract_alt_img_search = &extract_img_tile<uint16_t, 16+1*2>;
|
||||
}
|
||||
else if ( search_radiou == 4 )
|
||||
{
|
||||
extract_alt_img_search = &extract_img_tile<uint16_t, 16+4*2>;
|
||||
}
|
||||
}
|
||||
|
||||
int num_tiles_h = ref_img.size().height / (curr_tile_size / 2) - 1;
|
||||
int num_tiles_w = ref_img.size().width / (curr_tile_size / 2 ) - 1;
|
||||
|
||||
/* Upsample pervious layer alignment */
|
||||
std::vector<std::vector<std::pair<int, int>>> upsampled_prev_aligement;
|
||||
|
||||
// Coarsest level
|
||||
// prev_alignment is invalid / empty, construct alignment as (0,0)
|
||||
if ( prev_tile_size == -1 )
|
||||
{
|
||||
upsampled_prev_aligement.resize( num_tiles_h, \
|
||||
std::vector<std::pair<int, int>>( num_tiles_w, std::pair<int, int>(0, 0) ) );
|
||||
}
|
||||
// Upsample previous level alignment
|
||||
else
|
||||
{
|
||||
upsample_alignment_func_ptr( prev_aligement, upsampled_prev_aligement, \
|
||||
num_tiles_h, num_tiles_w, ref_img, alt_img, false );
|
||||
|
||||
// printf("\n!!!!!Upsampled previous alignment\n");
|
||||
// for ( int tile_row = 0; tile_row < int(upsampled_prev_aligement.size()); tile_row++ )
|
||||
// {
|
||||
// for ( int tile_col = 0; tile_col < int(upsampled_prev_aligement.at(0).size()); tile_col++ )
|
||||
// {
|
||||
// const auto tile_start = upsampled_prev_aligement.at( tile_row ).at( tile_col );
|
||||
// printf("up tile (%d, %d) -> start idx (%d, %d)\n", \
|
||||
// tile_row, tile_col, tile_start.first, tile_start.second);
|
||||
// }
|
||||
// }
|
||||
|
||||
}
|
||||
|
||||
#ifndef NDEBUG
|
||||
printf("%s::%s start: \n", __FILE__, __func__ );
|
||||
printf(" scale_factor_prev_curr %d, tile_size %d, prev_tile_size %d, search_radiou %d, distance L%d, \n", \
|
||||
scale_factor_prev_curr, curr_tile_size, prev_tile_size, search_radiou, distance_type );
|
||||
printf(" ref img size h=%d w=%d, alt img size h=%d w=%d, \n", \
|
||||
ref_img.size().height, ref_img.size().width, alt_img.size().height, alt_img.size().width );
|
||||
printf(" num tile h (upsampled) %d, num tile w (upsampled) %d\n", num_tiles_h, num_tiles_w);
|
||||
#endif
|
||||
|
||||
// allocate memory for current alignmenr
|
||||
curr_alignment.resize( num_tiles_h, std::vector<std::pair<int, int>>( num_tiles_w, std::pair<int, int>(0, 0) ) );
|
||||
|
||||
/* Pad alternative image */
|
||||
cv::Mat alt_img_pad;
|
||||
cv::copyMakeBorder( alt_img, \
|
||||
alt_img_pad, \
|
||||
search_radiou, search_radiou, search_radiou, search_radiou, \
|
||||
cv::BORDER_CONSTANT, cv::Scalar( UINT_LEAST16_MAX ) );
|
||||
|
||||
// printf("Reference image h=%d, w=%d: \n", ref_img.size().height, ref_img.size().width );
|
||||
// print_img<uint16_t>( ref_img );
|
||||
|
||||
// printf("Alter image pad h=%d, w=%d: \n", alt_img_pad.size().height, alt_img_pad.size().width );
|
||||
// print_img<uint16_t>( alt_img_pad );
|
||||
|
||||
// printf("!! enlarged tile size %d\n", curr_tile_size + 2 * search_radiou );
|
||||
|
||||
int alt_tile_row_idx_max = alt_img_pad.size().height - ( curr_tile_size + 2 * search_radiou );
|
||||
int alt_tile_col_idx_max = alt_img_pad.size().width - ( curr_tile_size + 2 * search_radiou );
|
||||
|
||||
// Dlete below distance vector, this is for debug only
|
||||
std::vector<std::vector<uint16_t>> distances( num_tiles_h, std::vector<uint16_t>( num_tiles_w, 0 ));
|
||||
|
||||
/* Iterate through all reference tile & compute distance */
|
||||
#pragma omp parallel for collapse(2)
|
||||
for ( int ref_tile_row_i = 0; ref_tile_row_i < num_tiles_h; ref_tile_row_i++ )
|
||||
{
|
||||
for ( int ref_tile_col_i = 0; ref_tile_col_i < num_tiles_w; ref_tile_col_i++ )
|
||||
{
|
||||
// Upper left index of reference tile
|
||||
int ref_tile_row_start_idx_i = ref_tile_row_i * curr_tile_size / 2;
|
||||
int ref_tile_col_start_idx_i = ref_tile_col_i * curr_tile_size / 2;
|
||||
|
||||
// printf("\nRef img tile [%d, %d] -> start idx [%d, %d] (row, col)\n", \
|
||||
// ref_tile_row_i, ref_tile_col_i, ref_tile_row_start_idx_i, ref_tile_col_start_idx_i );
|
||||
// printf("\nRef img tile [%d, %d]\n", ref_tile_row_i, ref_tile_col_i );
|
||||
// print_tile<uint16_t>( ref_img, curr_tile_size, ref_tile_row_start_idx_i, ref_tile_col_start_idx_i );
|
||||
|
||||
// Upsampled alignment at this tile
|
||||
// Alignment are relative displacement in pixel value
|
||||
int prev_alignment_row_i = upsampled_prev_aligement.at( ref_tile_row_i ).at( ref_tile_col_i ).first;
|
||||
int prev_alignment_col_i = upsampled_prev_aligement.at( ref_tile_row_i ).at( ref_tile_col_i ).second;
|
||||
|
||||
// Alternative image tile start idx
|
||||
int alt_tile_row_start_idx_i = ref_tile_row_start_idx_i + prev_alignment_row_i;
|
||||
int alt_tile_col_start_idx_i = ref_tile_col_start_idx_i + prev_alignment_col_i;
|
||||
|
||||
// Ensure alternative image tile within range
|
||||
if ( alt_tile_row_start_idx_i < 0 )
|
||||
alt_tile_row_start_idx_i = 0;
|
||||
if ( alt_tile_col_start_idx_i < 0 )
|
||||
alt_tile_col_start_idx_i = 0;
|
||||
if ( alt_tile_row_start_idx_i > alt_tile_row_idx_max )
|
||||
{
|
||||
// int before = alt_tile_row_start_idx_i;
|
||||
alt_tile_row_start_idx_i = alt_tile_row_idx_max;
|
||||
// printf("@@ change start x from %d to %d\n", before, alt_tile_row_idx_max);
|
||||
}
|
||||
if ( alt_tile_col_start_idx_i > alt_tile_col_idx_max )
|
||||
{
|
||||
// int before = alt_tile_col_start_idx_i;
|
||||
alt_tile_col_start_idx_i = alt_tile_col_idx_max;
|
||||
// printf("@@ change start y from %d to %d\n", before, alt_tile_col_idx_max );
|
||||
}
|
||||
|
||||
// Explicitly caching reference image tile
|
||||
cv::Mat ref_img_tile_i = extract_ref_img_tile( ref_img, ref_tile_row_start_idx_i, ref_tile_col_start_idx_i );
|
||||
cv::Mat alt_img_search_i = extract_alt_img_search( alt_img_pad, alt_tile_row_start_idx_i, alt_tile_col_start_idx_i );
|
||||
|
||||
// Because alternative image is padded with search radious.
|
||||
// Using same coordinate with reference image will automatically considered search radious * 2
|
||||
// printf("Alt image tile [%d, %d]-> start idx [%d, %d]\n", \
|
||||
// ref_tile_row_i, ref_tile_col_i, alt_tile_row_start_idx_i, alt_tile_col_start_idx_i );
|
||||
// printf("\nAlt image tile [%d, %d]\n", ref_tile_row_i, ref_tile_col_i );
|
||||
// print_tile<uint16_t>( alt_img_pad, curr_tile_size + 2 * search_radiou, alt_tile_row_start_idx_i, alt_tile_col_start_idx_i );
|
||||
|
||||
// Search based on L1/L2 distance
|
||||
unsigned long long min_distance_i = ULONG_LONG_MAX;
|
||||
int min_distance_row_i = -1;
|
||||
int min_distance_col_i = -1;
|
||||
for ( int search_row_j = 0; search_row_j < ( search_radiou * 2 + 1 ); search_row_j++ )
|
||||
{
|
||||
for ( int search_col_j = 0; search_col_j < ( search_radiou * 2 + 1 ); search_col_j++ )
|
||||
{
|
||||
// printf("\n--->tile at [%d, %d] search (%d, %d)\n", \
|
||||
// ref_tile_row_i, ref_tile_col_i, search_row_j - search_radiou, search_col_j - search_radiou );
|
||||
|
||||
// unsigned long long distance_j = distance_func_ptr( ref_img, alt_img_pad, \
|
||||
// ref_tile_row_start_idx_i, ref_tile_col_start_idx_i, \
|
||||
// alt_tile_row_start_idx_i + search_row_j, alt_tile_col_start_idx_i + search_col_j );
|
||||
|
||||
// unsigned long long distance_j = distance_func_ptr( ref_img_tile_i, alt_img_pad, \
|
||||
// 0, 0, \
|
||||
// alt_tile_row_start_idx_i + search_row_j, alt_tile_col_start_idx_i + search_col_j );
|
||||
|
||||
unsigned long long distance_j = distance_func_ptr( ref_img_tile_i, alt_img_search_i, \
|
||||
0, 0, \
|
||||
search_row_j, search_col_j );
|
||||
|
||||
// printf("<---tile at [%d, %d] search (%d, %d), new dis %llu, old dis %llu\n", \
|
||||
// ref_tile_row_i, ref_tile_col_i, search_row_j - search_radiou, search_col_j - search_radiou, distance_j, min_distance_i );
|
||||
|
||||
// If this is smaller distance
|
||||
if ( distance_j < min_distance_i )
|
||||
{
|
||||
min_distance_i = distance_j;
|
||||
min_distance_col_i = search_col_j;
|
||||
min_distance_row_i = search_row_j;
|
||||
}
|
||||
|
||||
// If same value, choose the one closer to the original tile location
|
||||
if ( distance_j == min_distance_i && min_distance_row_i != -1 && min_distance_col_i != -1 )
|
||||
{
|
||||
int prev_distance_row_2_ref = min_distance_row_i - search_radiou;
|
||||
int prev_distance_col_2_ref = min_distance_col_i - search_radiou;
|
||||
int curr_distance_row_2_ref = search_row_j - search_radiou;
|
||||
int curr_distance_col_2_ref = search_col_j - search_radiou;
|
||||
|
||||
int prev_distance_2_ref_sqr = prev_distance_row_2_ref * prev_distance_row_2_ref + prev_distance_col_2_ref * prev_distance_col_2_ref;
|
||||
int curr_distance_2_ref_sqr = curr_distance_row_2_ref * curr_distance_row_2_ref + curr_distance_col_2_ref * curr_distance_col_2_ref;
|
||||
|
||||
// previous min distance idx is farther away from ref tile start location
|
||||
if ( prev_distance_2_ref_sqr > curr_distance_2_ref_sqr )
|
||||
{
|
||||
// printf("@@@ Same distance %d, choose closer one (%d, %d) instead of (%d, %d)\n", \
|
||||
// distance_j, search_row_j, search_col_j, min_distance_row_i, min_distance_col_i);
|
||||
min_distance_col_i = search_col_j;
|
||||
min_distance_row_i = search_row_j;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// printf("tile at (%d, %d) alignment (%d, %d)\n", \
|
||||
// ref_tile_row_i, ref_tile_col_i, min_distance_row_i, min_distance_col_i );
|
||||
|
||||
int alignment_row_i = prev_alignment_row_i + min_distance_row_i - search_radiou;
|
||||
int alignment_col_i = prev_alignment_col_i + min_distance_col_i - search_radiou;
|
||||
|
||||
std::pair<int, int> alignment_i( alignment_row_i, alignment_col_i );
|
||||
|
||||
// Add min_distance_i's corresbonding idx as min
|
||||
curr_alignment.at( ref_tile_row_i ).at( ref_tile_col_i ) = alignment_i;
|
||||
distances.at( ref_tile_row_i ).at( ref_tile_col_i ) = min_distance_i;
|
||||
}
|
||||
}
|
||||
|
||||
// printf("\n!!!!!Min distance for each tile \n");
|
||||
// for ( int tile_row = 0; tile_row < num_tiles_h; tile_row++ )
|
||||
// {
|
||||
// for ( int tile_col = 0; tile_col < num_tiles_w; ++tile_col )
|
||||
// {
|
||||
// printf("tile (%d, %d) distance %u\n", \
|
||||
// tile_row, tile_col, distances.at( tile_row).at(tile_col ) );
|
||||
// }
|
||||
// }
|
||||
|
||||
// printf("\n!!!!!Alignment at current level\n");
|
||||
// for ( int tile_row = 0; tile_row < num_tiles_h; tile_row++ )
|
||||
// {
|
||||
// for ( int tile_col = 0; tile_col < num_tiles_w; tile_col++ )
|
||||
// {
|
||||
// const auto tile_start = curr_alignment.at( tile_row ).at( tile_col );
|
||||
// printf("tile (%d, %d) -> start idx (%d, %d)\n", \
|
||||
// tile_row, tile_col, tile_start.first, tile_start.second);
|
||||
// }
|
||||
// }
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
void align::process( const hdrplus::burst& burst_images, \
|
||||
std::vector<std::vector<std::vector<std::pair<int, int>>>>& images_alignment )
|
||||
{
|
||||
#ifndef NDEBUG
|
||||
printf("%s::%s align::process start\n", __FILE__, __func__ ); fflush(stdout);
|
||||
#endif
|
||||
|
||||
images_alignment.clear();
|
||||
images_alignment.resize( burst_images.num_images );
|
||||
|
||||
// image pyramid per image, per pyramid level
|
||||
std::vector<std::vector<cv::Mat>> per_grayimg_pyramid;
|
||||
|
||||
// printf("!!!!! ref bayer padded\n");
|
||||
// print_img<uint16_t>( burst_images.bayer_images_pad.at( burst_images.reference_image_idx) );
|
||||
// exit(1);
|
||||
|
||||
// printf("!!!!! ref gray padded\n");
|
||||
// print_img<uint16_t>( burst_images.grayscale_images_pad.at( burst_images.reference_image_idx) );
|
||||
// exit(1);
|
||||
|
||||
per_grayimg_pyramid.resize( burst_images.num_images );
|
||||
|
||||
#pragma omp parallel for
|
||||
for ( int img_idx = 0; img_idx < burst_images.num_images; ++img_idx )
|
||||
{
|
||||
// per_grayimg_pyramid[ img_idx ][ 0 ] is the original image
|
||||
// per_grayimg_pyramid[ img_idx ][ 3 ] is the coarsest image
|
||||
build_per_grayimg_pyramid( per_grayimg_pyramid.at( img_idx ), \
|
||||
burst_images.grayscale_images_pad.at( img_idx ), \
|
||||
this->inv_scale_factors );
|
||||
}
|
||||
|
||||
// #ifndef NDEBUG
|
||||
// printf("%s::%s build image pyramid of size : ", __FILE__, __func__ );
|
||||
// for ( int level_i = 0; level_i < num_levels; ++level_i )
|
||||
// {
|
||||
// printf("(%d, %d) ", per_grayimg_pyramid[ 0 ][ level_i ].size().height,
|
||||
// per_grayimg_pyramid[ 0 ][ level_i ].size().width );
|
||||
// }
|
||||
// printf("\n"); fflush(stdout);
|
||||
// #endif
|
||||
|
||||
// print image pyramid
|
||||
// for ( int level_i; level_i < num_levels; ++level_i )
|
||||
// {
|
||||
// printf("\n\n!!!!! ref gray pyramid level %d img : \n" , level_i );
|
||||
// print_img<uint16_t>( per_grayimg_pyramid[ burst_images.reference_image_idx ][ level_i ] );
|
||||
// }
|
||||
// exit(-1);
|
||||
|
||||
// Align every image
|
||||
const std::vector<cv::Mat>& ref_grayimg_pyramid = per_grayimg_pyramid[ burst_images.reference_image_idx ];
|
||||
std::vector<std::vector<std::pair<int, int>>> curr_alignment;
|
||||
std::vector<std::vector<std::pair<int, int>>> prev_alignment;
|
||||
for ( int img_idx = 0; img_idx < burst_images.num_images; ++img_idx )
|
||||
{
|
||||
// Do not align with reference image
|
||||
if ( img_idx == burst_images.reference_image_idx )
|
||||
continue;
|
||||
|
||||
const std::vector<cv::Mat>& alt_grayimg_pyramid = per_grayimg_pyramid[ img_idx ];
|
||||
|
||||
// Align every level from coarse to grain
|
||||
// level 0 : finest level, the original image
|
||||
// level 3 : coarsest level
|
||||
curr_alignment.clear();
|
||||
prev_alignment.clear();
|
||||
for ( int level_i = num_levels - 1; level_i >= 0; level_i-- ) // 3,2,1,0
|
||||
{
|
||||
// make curr alignment as previous alignment
|
||||
prev_alignment.swap( curr_alignment );
|
||||
curr_alignment.clear();
|
||||
|
||||
// printf("\n\n########################align level %d\n", level_i );
|
||||
align_image_level(
|
||||
ref_grayimg_pyramid[ level_i ], // reference image at current level
|
||||
alt_grayimg_pyramid[ level_i ], // alternative image at current level
|
||||
prev_alignment, // previous layer alignment
|
||||
curr_alignment, // current layer alignment
|
||||
( level_i == ( num_levels - 1 ) ? -1 : inv_scale_factors[ level_i + 1 ] ), // scale factor between previous layer and current layer. -1 if current layer is the coarsest layer, [-1, 4, 4, 2]
|
||||
grayimg_tile_sizes[ level_i ], // current level tile size
|
||||
( level_i == ( num_levels - 1 ) ? -1 : grayimg_tile_sizes[ level_i + 1 ] ), // previous level tile size
|
||||
grayimg_search_radious[ level_i ], // search radious
|
||||
distances[ level_i ] ); // L1/L2 distance
|
||||
|
||||
// printf("@@@Alignment at level %d is h=%d, w=%d", level_i, curr_alignment.size(), curr_alignment.at(0).size() );
|
||||
|
||||
|
||||
} // for pyramid level
|
||||
|
||||
// Alignment at grayscale image
|
||||
images_alignment.at( img_idx ).swap( curr_alignment );
|
||||
|
||||
// printf("\n!!!!!Alternative Image Alignment\n");
|
||||
// for ( int tile_row = 0; tile_row < images_alignment.at( img_idx ).size(); tile_row++ )
|
||||
// {
|
||||
// for ( int tile_col = 0; tile_col < images_alignment.at( img_idx ).at(0).size(); tile_col++ )
|
||||
// {
|
||||
// const auto tile_start = images_alignment.at( img_idx ).at( tile_row ).at( tile_col );
|
||||
// printf("tile (%d, %d) -> start idx (%d, %d)\n", \
|
||||
// tile_row, tile_col, tile_start.first, tile_start.second);
|
||||
// }
|
||||
// }
|
||||
|
||||
} // for alternative image
|
||||
|
||||
per_grayimg_pyramid.clear();
|
||||
|
||||
}
|
||||
|
||||
} // namespace hdrplus
|
@ -0,0 +1,234 @@
|
||||
#include <string>
|
||||
#include <cstdio>
|
||||
#include <iostream>
|
||||
#include <utility> // std::pair, std::makr_pair
|
||||
#include <memory> // std::shared_ptr
|
||||
#include <stdexcept> // std::runtime_error
|
||||
#include <opencv2/opencv.hpp> // all opencv header
|
||||
#include <libraw/libraw.h>
|
||||
#include <exiv2/exiv2.hpp> // exiv2
|
||||
#include "hdrplus/bayer_image.h"
|
||||
#include "hdrplus/utility.h" // box_filter_kxk
|
||||
namespace hdrplus
|
||||
{
|
||||
|
||||
bayer_image::bayer_image( const std::string& bayer_image_path )
|
||||
{
|
||||
libraw_processor = std::make_shared<LibRaw>();
|
||||
|
||||
// Open RAW image file
|
||||
int return_code;
|
||||
if ( ( return_code = libraw_processor->open_file( bayer_image_path.c_str() ) ) != LIBRAW_SUCCESS )
|
||||
{
|
||||
libraw_processor->recycle();
|
||||
#ifdef __ANDROID__
|
||||
return;
|
||||
#else
|
||||
throw std::runtime_error("Error opening file " + bayer_image_path + " " + libraw_strerror( return_code ));
|
||||
#endif
|
||||
}
|
||||
|
||||
// Unpack the raw image
|
||||
if ( ( return_code = libraw_processor->unpack() ) != LIBRAW_SUCCESS )
|
||||
{
|
||||
#ifdef __ANDROID__
|
||||
return;
|
||||
#else
|
||||
throw std::runtime_error("Error unpack file " + bayer_image_path + " " + libraw_strerror( return_code ));
|
||||
#endif
|
||||
}
|
||||
|
||||
// Get image basic info
|
||||
width = int( libraw_processor->imgdata.rawdata.sizes.raw_width );
|
||||
height = int( libraw_processor->imgdata.rawdata.sizes.raw_height );
|
||||
|
||||
// Read exif tags
|
||||
Exiv2::Image::AutoPtr image = Exiv2::ImageFactory::open(bayer_image_path);
|
||||
assert(image.get() != 0);
|
||||
image->readMetadata();
|
||||
Exiv2::ExifData &exifData = image->exifData();
|
||||
if (exifData.empty()) {
|
||||
std::string error(bayer_image_path);
|
||||
error += ": No Exif data found in the file";
|
||||
std::cout << error << std::endl;
|
||||
}
|
||||
|
||||
white_level = exifData["Exif.Image.WhiteLevel"].toLong();
|
||||
black_level_per_channel.resize( 4 );
|
||||
black_level_per_channel.at(0) = exifData["Exif.Image.BlackLevel"].toLong(0);
|
||||
black_level_per_channel.at(1) = exifData["Exif.Image.BlackLevel"].toLong(1);
|
||||
black_level_per_channel.at(2) = exifData["Exif.Image.BlackLevel"].toLong(2);
|
||||
black_level_per_channel.at(3) = exifData["Exif.Image.BlackLevel"].toLong(3);
|
||||
iso = exifData["Exif.Image.ISOSpeedRatings"].toLong();
|
||||
|
||||
// Create CV mat
|
||||
// https://answers.opencv.org/question/105972/de-bayering-a-cr2-image/
|
||||
// https://www.libraw.org/node/2141
|
||||
raw_image = cv::Mat( height, width, CV_16U, libraw_processor->imgdata.rawdata.raw_image ).clone(); // changed the order of width and height
|
||||
|
||||
// 2x2 box filter
|
||||
grayscale_image = box_filter_kxk<uint16_t, 2>( raw_image );
|
||||
|
||||
#ifndef NDEBUG
|
||||
printf("%s::%s read bayer image %s with\n width %zu\n height %zu\n iso %.3f\n white level %d\n black level %d %d %d %d\n", \
|
||||
__FILE__, __func__, bayer_image_path.c_str(), width, height, iso, white_level, \
|
||||
black_level_per_channel[0], black_level_per_channel[1], black_level_per_channel[2], black_level_per_channel[3] );
|
||||
fflush( stdout );
|
||||
#endif
|
||||
}
|
||||
|
||||
bayer_image::bayer_image( const std::vector<uint8_t>& bayer_image_content )
|
||||
{
|
||||
libraw_processor = std::make_shared<LibRaw>();
|
||||
|
||||
// Open RAW image file
|
||||
int return_code;
|
||||
if ( ( return_code = libraw_processor->open_buffer( (void *)(&bayer_image_content[0]), bayer_image_content.size() ) ) != LIBRAW_SUCCESS )
|
||||
{
|
||||
libraw_processor->recycle();
|
||||
#ifdef __ANDROID__
|
||||
return;
|
||||
#else
|
||||
throw std::runtime_error("Error opening file " + bayer_image_path + " " + libraw_strerror( return_code ));
|
||||
#endif
|
||||
}
|
||||
|
||||
// Unpack the raw image
|
||||
if ( ( return_code = libraw_processor->unpack() ) != LIBRAW_SUCCESS )
|
||||
{
|
||||
#ifdef __ANDROID__
|
||||
return;
|
||||
#else
|
||||
throw std::runtime_error("Error unpack file " + bayer_image_path + " " + libraw_strerror( return_code ));
|
||||
#endif
|
||||
}
|
||||
|
||||
// Get image basic info
|
||||
width = int( libraw_processor->imgdata.rawdata.sizes.raw_width );
|
||||
height = int( libraw_processor->imgdata.rawdata.sizes.raw_height );
|
||||
|
||||
// Read exif tags
|
||||
Exiv2::Image::AutoPtr image = Exiv2::ImageFactory::open(&bayer_image_content[0], bayer_image_content.size());
|
||||
assert(image.get() != 0);
|
||||
image->readMetadata();
|
||||
Exiv2::ExifData &exifData = image->exifData();
|
||||
if (exifData.empty()) {
|
||||
std::string error = "No Exif data found in the file";
|
||||
std::cout << error << std::endl;
|
||||
}
|
||||
|
||||
white_level = exifData["Exif.Image.WhiteLevel"].toLong();
|
||||
black_level_per_channel.resize( 4 );
|
||||
black_level_per_channel.at(0) = exifData["Exif.Image.BlackLevel"].toLong(0);
|
||||
black_level_per_channel.at(1) = exifData["Exif.Image.BlackLevel"].toLong(1);
|
||||
black_level_per_channel.at(2) = exifData["Exif.Image.BlackLevel"].toLong(2);
|
||||
black_level_per_channel.at(3) = exifData["Exif.Image.BlackLevel"].toLong(3);
|
||||
iso = exifData["Exif.Image.ISOSpeedRatings"].toLong();
|
||||
|
||||
// Create CV mat
|
||||
// https://answers.opencv.org/question/105972/de-bayering-a-cr2-image/
|
||||
// https://www.libraw.org/node/2141
|
||||
raw_image = cv::Mat( height, width, CV_16U, libraw_processor->imgdata.rawdata.raw_image ).clone(); // changed the order of width and height
|
||||
|
||||
// 2x2 box filter
|
||||
grayscale_image = box_filter_kxk<uint16_t, 2>( raw_image );
|
||||
|
||||
#ifndef NDEBUG
|
||||
printf("%s::%s read bayer image with\n width %zu\n height %zu\n iso %.3f\n white level %d\n black level %d %d %d %d\n", \
|
||||
__FILE__, __func__, width, height, iso, white_level, \
|
||||
black_level_per_channel[0], black_level_per_channel[1], black_level_per_channel[2], black_level_per_channel[3] );
|
||||
fflush( stdout );
|
||||
#endif
|
||||
}
|
||||
|
||||
bayer_image::bayer_image( std::shared_ptr<MemFile> bayer_image_file )
|
||||
{
|
||||
libraw_processor = std::make_shared<LibRaw>();
|
||||
|
||||
// Open RAW image file
|
||||
int return_code;
|
||||
{
|
||||
std::vector<uint8_t>& fileData = bayer_image_file->content;
|
||||
if ( ( return_code = libraw_processor->open_buffer( (void *)(&fileData[0]), fileData.size() ) ) != LIBRAW_SUCCESS )
|
||||
{
|
||||
libraw_processor->recycle();
|
||||
#ifdef __ANDROID__
|
||||
return;
|
||||
#else
|
||||
throw std::runtime_error("Error opening file " + bayer_image_path + " " + libraw_strerror( return_code ));
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Unpack the raw image
|
||||
if ( ( return_code = libraw_processor->unpack() ) != LIBRAW_SUCCESS )
|
||||
{
|
||||
#ifdef __ANDROID__
|
||||
return;
|
||||
#else
|
||||
throw std::runtime_error("Error unpack file " + bayer_image_path + " " + libraw_strerror( return_code ));
|
||||
#endif
|
||||
}
|
||||
|
||||
// Get image basic info
|
||||
width = int( libraw_processor->imgdata.rawdata.sizes.raw_width );
|
||||
height = int( libraw_processor->imgdata.rawdata.sizes.raw_height );
|
||||
|
||||
// Read exif tags
|
||||
Exiv2::Image::AutoPtr image = Exiv2::ImageFactory::open(&bayer_image_file->content[0], bayer_image_file->content.size());
|
||||
assert(image.get() != 0);
|
||||
image->readMetadata();
|
||||
Exiv2::ExifData &exifData = image->exifData();
|
||||
if (exifData.empty()) {
|
||||
std::string error = "No Exif data found in the file";
|
||||
std::cout << error << std::endl;
|
||||
}
|
||||
|
||||
white_level = exifData["Exif.Image.WhiteLevel"].toLong();
|
||||
black_level_per_channel.resize( 4 );
|
||||
black_level_per_channel.at(0) = exifData["Exif.Image.BlackLevel"].toLong(0);
|
||||
black_level_per_channel.at(1) = exifData["Exif.Image.BlackLevel"].toLong(1);
|
||||
black_level_per_channel.at(2) = exifData["Exif.Image.BlackLevel"].toLong(2);
|
||||
black_level_per_channel.at(3) = exifData["Exif.Image.BlackLevel"].toLong(3);
|
||||
iso = exifData["Exif.Image.ISOSpeedRatings"].toLong();
|
||||
|
||||
// Create CV mat
|
||||
// https://answers.opencv.org/question/105972/de-bayering-a-cr2-image/
|
||||
// https://www.libraw.org/node/2141
|
||||
raw_image = cv::Mat( height, width, CV_16U, libraw_processor->imgdata.rawdata.raw_image ).clone(); // changed the order of width and height
|
||||
|
||||
// 2x2 box filter
|
||||
grayscale_image = box_filter_kxk<uint16_t, 2>( raw_image );
|
||||
|
||||
#ifndef NDEBUG
|
||||
printf("%s::%s read bayer image with\n width %zu\n height %zu\n iso %.3f\n white level %d\n black level %d %d %d %d\n", \
|
||||
__FILE__, __func__, width, height, iso, white_level, \
|
||||
black_level_per_channel[0], black_level_per_channel[1], black_level_per_channel[2], black_level_per_channel[3] );
|
||||
fflush( stdout );
|
||||
#endif
|
||||
}
|
||||
|
||||
std::pair<double, double> bayer_image::get_noise_params() const
|
||||
{
|
||||
// Set ISO to 100 if not positive
|
||||
double iso_ = iso <= 0 ? 100 : iso;
|
||||
|
||||
// Calculate shot noise and read noise parameters w.r.t ISO 100
|
||||
double lambda_shot_p = iso_ / 100.0f * baseline_lambda_shot;
|
||||
double lambda_read_p = (iso_ / 100.0f) * (iso_ / 100.0f) * baseline_lambda_read;
|
||||
|
||||
double black_level = (black_level_per_channel[0] + \
|
||||
black_level_per_channel[1] + \
|
||||
black_level_per_channel[2] + \
|
||||
black_level_per_channel[3]) / 4.0;
|
||||
|
||||
// Rescale shot and read noise to normal range
|
||||
double lambda_shot = lambda_shot_p * (white_level - black_level);
|
||||
double lambda_read = lambda_read_p * (white_level - black_level) * (white_level - black_level);
|
||||
|
||||
// return pair
|
||||
return std::make_pair(lambda_shot, lambda_read);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,321 @@
|
||||
#include <cstdio>
|
||||
#include <string>
|
||||
#include <omp.h>
|
||||
#include <opencv2/opencv.hpp> // all opencv header
|
||||
#include "hdrplus/burst.h"
|
||||
#include "hdrplus/utility.h"
|
||||
|
||||
namespace hdrplus
|
||||
{
|
||||
|
||||
burst::burst( const std::string& burst_path, const std::string& reference_image_path )
|
||||
{
|
||||
std::vector<cv::String> bayer_image_paths;
|
||||
// Search through the input path directory to get all input image path
|
||||
if ( burst_path.at( burst_path.size() - 1) == '/')
|
||||
cv::glob( burst_path + "*.dng", bayer_image_paths, false );
|
||||
else
|
||||
cv::glob( burst_path + "/*.dng", bayer_image_paths, false );
|
||||
|
||||
#ifndef NDEBUG
|
||||
for ( const auto& bayer_img_path_i : bayer_image_paths )
|
||||
{
|
||||
printf("img i path %s\n", bayer_img_path_i.c_str()); fflush(stdout);
|
||||
}
|
||||
printf("ref img path %s\n", reference_image_path.c_str()); fflush(stdout);
|
||||
#endif
|
||||
|
||||
// Number of images
|
||||
num_images = bayer_image_paths.size();
|
||||
|
||||
// Find reference image path in input directory
|
||||
// reference image path need to be absolute path
|
||||
reference_image_idx = -1;
|
||||
for ( size_t i = 0; i < bayer_image_paths.size(); ++i )
|
||||
{
|
||||
if ( bayer_image_paths[ i ] == reference_image_path )
|
||||
{
|
||||
reference_image_idx = i;
|
||||
}
|
||||
}
|
||||
|
||||
if ( reference_image_idx == -1 )
|
||||
{
|
||||
return;
|
||||
// throw std::runtime_error("Error unable to locate reference image " + reference_image_path );
|
||||
}
|
||||
|
||||
#ifndef NDEBUG
|
||||
for ( const auto& bayer_image_path_i : bayer_image_paths )
|
||||
{
|
||||
printf("%s::%s Find image %s\n", \
|
||||
__FILE__, __func__, bayer_image_path_i.c_str());
|
||||
}
|
||||
|
||||
printf("%s::%s reference image idx %d\n", \
|
||||
__FILE__, __func__, reference_image_idx );
|
||||
#endif
|
||||
|
||||
// Get source bayer image
|
||||
// Downsample original bayer image by 2x2 box filter
|
||||
for ( const auto& bayer_image_path_i : bayer_image_paths )
|
||||
{
|
||||
bayer_images.emplace_back( bayer_image_path_i );
|
||||
}
|
||||
|
||||
// Pad information
|
||||
int tile_size_bayer = 32;
|
||||
int padding_top = tile_size_bayer / 2;
|
||||
int padding_bottom = tile_size_bayer / 2 + \
|
||||
( (bayer_images[ 0 ].height % tile_size_bayer) == 0 ? \
|
||||
0 : tile_size_bayer - bayer_images[ 0 ].height % tile_size_bayer );
|
||||
int padding_left = tile_size_bayer / 2;
|
||||
int padding_right = tile_size_bayer / 2 + \
|
||||
( (bayer_images[ 0 ].width % tile_size_bayer) == 0 ? \
|
||||
0 : tile_size_bayer - bayer_images[ 0 ].width % tile_size_bayer );
|
||||
padding_info_bayer = std::vector<int>{ padding_top, padding_bottom, padding_left, padding_right };
|
||||
|
||||
// Pad bayer image
|
||||
for ( const auto& bayer_image_i : bayer_images )
|
||||
{
|
||||
cv::Mat bayer_image_pad_i;
|
||||
cv::copyMakeBorder( bayer_image_i.raw_image, \
|
||||
bayer_image_pad_i, \
|
||||
padding_top, padding_bottom, padding_left, padding_right, \
|
||||
cv::BORDER_REFLECT );
|
||||
|
||||
// cv::Mat use internal reference count
|
||||
bayer_images_pad.emplace_back( bayer_image_pad_i );
|
||||
grayscale_images_pad.emplace_back( box_filter_kxk<uint16_t, 2>( bayer_image_pad_i ) );
|
||||
}
|
||||
|
||||
#ifndef NDEBUG
|
||||
printf("%s::%s Pad bayer image from (%d, %d) -> (%d, %d)\n", \
|
||||
__FILE__, __func__, \
|
||||
bayer_images[ 0 ].height, \
|
||||
bayer_images[ 0 ].width, \
|
||||
bayer_images_pad[ 0 ].size().height, \
|
||||
bayer_images_pad[ 0 ].size().width );
|
||||
printf("%s::%s pad top %d, buttom %d, left %d, right %d\n", \
|
||||
__FILE__, __func__, \
|
||||
padding_top, padding_bottom, padding_left, padding_right );
|
||||
#endif
|
||||
}
|
||||
|
||||
burst::burst( const std::vector<std::string>& bayer_image_paths, int reference_image_index )
|
||||
{
|
||||
// Number of images
|
||||
num_images = bayer_image_paths.size();
|
||||
|
||||
// Find reference image path in input directory
|
||||
// reference image path need to be absolute path
|
||||
reference_image_idx = -1;
|
||||
if ( reference_image_index >= 0 && reference_image_index < bayer_image_paths.size() )
|
||||
{
|
||||
reference_image_idx = reference_image_index;
|
||||
}
|
||||
|
||||
if ( reference_image_idx == -1 )
|
||||
{
|
||||
return;
|
||||
// throw std::runtime_error("Error reference image index is out of range " );
|
||||
}
|
||||
|
||||
#ifndef NDEBUG
|
||||
for ( const auto& bayer_image_path_i : bayer_image_paths )
|
||||
{
|
||||
printf("%s::%s Find image %s\n", \
|
||||
__FILE__, __func__, bayer_image_path_i.c_str());
|
||||
}
|
||||
|
||||
printf("%s::%s reference image idx %d\n", \
|
||||
__FILE__, __func__, reference_image_idx );
|
||||
#endif
|
||||
|
||||
// Get source bayer image
|
||||
// Downsample original bayer image by 2x2 box filter
|
||||
for ( const auto& bayer_image_path_i : bayer_image_paths )
|
||||
{
|
||||
bayer_images.emplace_back( bayer_image_path_i );
|
||||
}
|
||||
|
||||
// Pad information
|
||||
int tile_size_bayer = 32;
|
||||
int padding_top = tile_size_bayer / 2;
|
||||
int padding_bottom = tile_size_bayer / 2 + \
|
||||
( (bayer_images[ 0 ].height % tile_size_bayer) == 0 ? \
|
||||
0 : tile_size_bayer - bayer_images[ 0 ].height % tile_size_bayer );
|
||||
int padding_left = tile_size_bayer / 2;
|
||||
int padding_right = tile_size_bayer / 2 + \
|
||||
( (bayer_images[ 0 ].width % tile_size_bayer) == 0 ? \
|
||||
0 : tile_size_bayer - bayer_images[ 0 ].width % tile_size_bayer );
|
||||
padding_info_bayer = std::vector<int>{ padding_top, padding_bottom, padding_left, padding_right };
|
||||
|
||||
// Pad bayer image
|
||||
for ( const auto& bayer_image_i : bayer_images )
|
||||
{
|
||||
cv::Mat bayer_image_pad_i;
|
||||
cv::copyMakeBorder( bayer_image_i.raw_image, \
|
||||
bayer_image_pad_i, \
|
||||
padding_top, padding_bottom, padding_left, padding_right, \
|
||||
cv::BORDER_REFLECT );
|
||||
|
||||
// cv::Mat use internal reference count
|
||||
bayer_images_pad.emplace_back( bayer_image_pad_i );
|
||||
grayscale_images_pad.emplace_back( box_filter_kxk<uint16_t, 2>( bayer_image_pad_i ) );
|
||||
}
|
||||
|
||||
#ifndef NDEBUG
|
||||
printf("%s::%s Pad bayer image from (%d, %d) -> (%d, %d)\n", \
|
||||
__FILE__, __func__, \
|
||||
bayer_images[ 0 ].height, \
|
||||
bayer_images[ 0 ].width, \
|
||||
bayer_images_pad[ 0 ].size().height, \
|
||||
bayer_images_pad[ 0 ].size().width );
|
||||
printf("%s::%s pad top %d, buttom %d, left %d, right %d\n", \
|
||||
__FILE__, __func__, \
|
||||
padding_top, padding_bottom, padding_left, padding_right );
|
||||
#endif
|
||||
}
|
||||
|
||||
burst::burst( const std::vector<std::vector<uint8_t> >& bayer_image_contents, int reference_image_index )
|
||||
{
|
||||
// Number of images
|
||||
num_images = bayer_image_contents.size();
|
||||
|
||||
// Find reference image path in input directory
|
||||
// reference image path need to be absolute path
|
||||
reference_image_idx = -1;
|
||||
if ( reference_image_index >= 0 && reference_image_index < bayer_image_contents.size() )
|
||||
{
|
||||
reference_image_idx = reference_image_index;
|
||||
}
|
||||
|
||||
if ( reference_image_idx == -1 )
|
||||
{
|
||||
return;
|
||||
// throw std::runtime_error("Error reference image index is out of range " );
|
||||
}
|
||||
|
||||
#ifndef NDEBUG
|
||||
printf("%s::%s reference image idx %d\n", \
|
||||
__FILE__, __func__, reference_image_idx );
|
||||
#endif
|
||||
|
||||
// Get source bayer image
|
||||
// Downsample original bayer image by 2x2 box filter
|
||||
for ( const auto& bayer_image_content : bayer_image_contents )
|
||||
{
|
||||
bayer_images.emplace_back( bayer_image_content );
|
||||
}
|
||||
|
||||
// Pad information
|
||||
int tile_size_bayer = 32;
|
||||
int padding_top = tile_size_bayer / 2;
|
||||
int padding_bottom = tile_size_bayer / 2 + \
|
||||
( (bayer_images[ 0 ].height % tile_size_bayer) == 0 ? \
|
||||
0 : tile_size_bayer - bayer_images[ 0 ].height % tile_size_bayer );
|
||||
int padding_left = tile_size_bayer / 2;
|
||||
int padding_right = tile_size_bayer / 2 + \
|
||||
( (bayer_images[ 0 ].width % tile_size_bayer) == 0 ? \
|
||||
0 : tile_size_bayer - bayer_images[ 0 ].width % tile_size_bayer );
|
||||
padding_info_bayer = std::vector<int>{ padding_top, padding_bottom, padding_left, padding_right };
|
||||
|
||||
// Pad bayer image
|
||||
for ( const auto& bayer_image_i : bayer_images )
|
||||
{
|
||||
cv::Mat bayer_image_pad_i;
|
||||
cv::copyMakeBorder( bayer_image_i.raw_image, \
|
||||
bayer_image_pad_i, \
|
||||
padding_top, padding_bottom, padding_left, padding_right, \
|
||||
cv::BORDER_REFLECT );
|
||||
|
||||
// cv::Mat use internal reference count
|
||||
bayer_images_pad.emplace_back( bayer_image_pad_i );
|
||||
grayscale_images_pad.emplace_back( box_filter_kxk<uint16_t, 2>( bayer_image_pad_i ) );
|
||||
}
|
||||
|
||||
#ifndef NDEBUG
|
||||
printf("%s::%s Pad bayer image from (%d, %d) -> (%d, %d)\n", \
|
||||
__FILE__, __func__, \
|
||||
bayer_images[ 0 ].height, \
|
||||
bayer_images[ 0 ].width, \
|
||||
bayer_images_pad[ 0 ].size().height, \
|
||||
bayer_images_pad[ 0 ].size().width );
|
||||
printf("%s::%s pad top %d, buttom %d, left %d, right %d\n", \
|
||||
__FILE__, __func__, \
|
||||
padding_top, padding_bottom, padding_left, padding_right );
|
||||
#endif
|
||||
}
|
||||
|
||||
burst::burst( const std::vector<std::shared_ptr<MemFile> >& bayer_image_files, int reference_image_index )
|
||||
{
|
||||
// Number of images
|
||||
num_images = bayer_image_files.size();
|
||||
|
||||
// Find reference image path in input directory
|
||||
// reference image path need to be absolute path
|
||||
reference_image_idx = -1;
|
||||
if ( reference_image_index >= 0 && reference_image_index < bayer_image_files.size() )
|
||||
{
|
||||
reference_image_idx = reference_image_index;
|
||||
}
|
||||
|
||||
if ( reference_image_idx == -1 )
|
||||
{
|
||||
return;
|
||||
// throw std::runtime_error("Error reference image index is out of range " );
|
||||
}
|
||||
|
||||
#ifndef NDEBUG
|
||||
printf("%s::%s reference image idx %d\n", \
|
||||
__FILE__, __func__, reference_image_idx );
|
||||
#endif
|
||||
|
||||
// Get source bayer image
|
||||
// Downsample original bayer image by 2x2 box filter
|
||||
for ( const auto& bayer_image_file : bayer_image_files )
|
||||
{
|
||||
bayer_images.emplace_back( bayer_image_file );
|
||||
}
|
||||
|
||||
// Pad information
|
||||
int tile_size_bayer = 32;
|
||||
int padding_top = tile_size_bayer / 2;
|
||||
int padding_bottom = tile_size_bayer / 2 + \
|
||||
( (bayer_images[ 0 ].height % tile_size_bayer) == 0 ? \
|
||||
0 : tile_size_bayer - bayer_images[ 0 ].height % tile_size_bayer );
|
||||
int padding_left = tile_size_bayer / 2;
|
||||
int padding_right = tile_size_bayer / 2 + \
|
||||
( (bayer_images[ 0 ].width % tile_size_bayer) == 0 ? \
|
||||
0 : tile_size_bayer - bayer_images[ 0 ].width % tile_size_bayer );
|
||||
padding_info_bayer = std::vector<int>{ padding_top, padding_bottom, padding_left, padding_right };
|
||||
|
||||
// Pad bayer image
|
||||
for ( const auto& bayer_image_i : bayer_images )
|
||||
{
|
||||
cv::Mat bayer_image_pad_i;
|
||||
cv::copyMakeBorder( bayer_image_i.raw_image, \
|
||||
bayer_image_pad_i, \
|
||||
padding_top, padding_bottom, padding_left, padding_right, \
|
||||
cv::BORDER_REFLECT );
|
||||
|
||||
// cv::Mat use internal reference count
|
||||
bayer_images_pad.emplace_back( bayer_image_pad_i );
|
||||
grayscale_images_pad.emplace_back( box_filter_kxk<uint16_t, 2>( bayer_image_pad_i ) );
|
||||
}
|
||||
|
||||
#ifndef NDEBUG
|
||||
printf("%s::%s Pad bayer image from (%d, %d) -> (%d, %d)\n", \
|
||||
__FILE__, __func__, \
|
||||
bayer_images[ 0 ].height, \
|
||||
bayer_images[ 0 ].width, \
|
||||
bayer_images_pad[ 0 ].size().height, \
|
||||
bayer_images_pad[ 0 ].size().width );
|
||||
printf("%s::%s pad top %d, buttom %d, left %d, right %d\n", \
|
||||
__FILE__, __func__, \
|
||||
padding_top, padding_bottom, padding_left, padding_right );
|
||||
#endif
|
||||
}
|
||||
|
||||
} // namespace hdrplus
|
@ -0,0 +1,786 @@
|
||||
#include <iostream>
|
||||
#include <opencv2/opencv.hpp> // all opencv header
|
||||
#include "hdrplus/finish.h"
|
||||
#include "hdrplus/utility.h"
|
||||
#include <cmath>
|
||||
|
||||
#ifdef __ANDROID__
|
||||
#define DBG_OUTPUT_ROOT "/sdcard/com.xypower.mpapp/tmp/"
|
||||
#else
|
||||
#define DBG_OUTPUT_ROOT ""
|
||||
#endif
|
||||
// #include <type_traits>
|
||||
|
||||
namespace hdrplus
|
||||
{
|
||||
|
||||
|
||||
cv::Mat convert16bit2_8bit_(cv::Mat ans){
|
||||
if(ans.type()==CV_16UC3){
|
||||
cv::MatIterator_<cv::Vec3w> it, end;
|
||||
for( it = ans.begin<cv::Vec3w>(), end = ans.end<cv::Vec3w>(); it != end; ++it)
|
||||
{
|
||||
// std::cout<<sizeof (*it)[0] <<std::endl;
|
||||
(*it)[0] *=(255.0/USHRT_MAX);
|
||||
(*it)[1] *=(255.0/USHRT_MAX);
|
||||
(*it)[2] *=(255.0/USHRT_MAX);
|
||||
}
|
||||
ans.convertTo(ans, CV_8UC3);
|
||||
}else if(ans.type()==CV_16UC1){
|
||||
u_int16_t* ptr = (u_int16_t*)ans.data;
|
||||
int end = ans.rows*ans.cols;
|
||||
for(int i=0;i<end;i++){
|
||||
*(ptr+i) *=(255.0/USHRT_MAX);
|
||||
}
|
||||
ans.convertTo(ans, CV_8UC1);
|
||||
}else{
|
||||
std::cout<<"Unsupported Data Type"<<std::endl;
|
||||
}
|
||||
return ans;
|
||||
}
|
||||
|
||||
cv::Mat convert8bit2_16bit_(cv::Mat ans){
|
||||
if(ans.type()==CV_8UC3){
|
||||
ans.convertTo(ans, CV_16UC3);
|
||||
cv::MatIterator_<cv::Vec3w> it, end;
|
||||
for( it = ans.begin<cv::Vec3w>(), end = ans.end<cv::Vec3w>(); it != end; ++it)
|
||||
{
|
||||
// std::cout<<sizeof (*it)[0] <<std::endl;
|
||||
(*it)[0] *=(65535.0/255.0);
|
||||
(*it)[1] *=(65535.0/255.0);
|
||||
(*it)[2] *=(65535.0/255.0);
|
||||
}
|
||||
|
||||
}else if(ans.type()==CV_8UC1){
|
||||
ans.convertTo(ans, CV_16UC1);
|
||||
u_int16_t* ptr = (u_int16_t*)ans.data;
|
||||
int end = ans.rows*ans.cols;
|
||||
for(int i=0;i<end;i++){
|
||||
*(ptr+i) *=(65535.0/255.0);
|
||||
}
|
||||
|
||||
}else{
|
||||
std::cout<<"Unsupported Data Type"<<std::endl;
|
||||
}
|
||||
return ans;
|
||||
}
|
||||
|
||||
cv::Mat convert8bit2_12bit_(cv::Mat ans){
|
||||
// cv::Mat ans(I);
|
||||
cv::MatIterator_<cv::Vec3w> it, end;
|
||||
for( it = ans.begin<cv::Vec3w>(), end = ans.end<cv::Vec3w>(); it != end; ++it)
|
||||
{
|
||||
// std::cout<<sizeof (*it)[0] <<std::endl;
|
||||
(*it)[0] *=(2048.0/255.0);
|
||||
(*it)[1] *=(2048.0/255.0);
|
||||
(*it)[2] *=(2048.0/255.0);
|
||||
}
|
||||
ans.convertTo(ans, CV_16UC3);
|
||||
return ans;
|
||||
}
|
||||
|
||||
uint16_t uGammaCompress_1pix(float x, float threshold,float gainMin,float gainMax,float exponent){
|
||||
// Normalize pixel val
|
||||
x/=USHRT_MAX;
|
||||
// check the val against the threshold
|
||||
if(x<=threshold){
|
||||
x =gainMin*x;
|
||||
}else{
|
||||
x = gainMax* pow(x,exponent)-gainMax+1;
|
||||
}
|
||||
// clip
|
||||
if(x<0){
|
||||
x=0;
|
||||
}else{
|
||||
if(x>1){
|
||||
x = 1;
|
||||
}
|
||||
}
|
||||
|
||||
x*=USHRT_MAX;
|
||||
|
||||
return (uint16_t)x;
|
||||
}
|
||||
|
||||
uint16_t uGammaDecompress_1pix(float x, float threshold,float gainMin,float gainMax,float exponent){
|
||||
// Normalize pixel val
|
||||
x/=65535.0;
|
||||
// check the val against the threshold
|
||||
if(x<=threshold){
|
||||
x = x/gainMin;
|
||||
}else{
|
||||
x = pow((x+gainMax-1)/gainMax,exponent);
|
||||
}
|
||||
// clip
|
||||
if(x<0){
|
||||
x=0;
|
||||
}else{
|
||||
if(x>1){
|
||||
x = 1;
|
||||
}
|
||||
}
|
||||
x*=65535;
|
||||
|
||||
return (uint16_t)x;
|
||||
}
|
||||
|
||||
cv::Mat uGammaCompress_(cv::Mat m,float threshold,float gainMin,float gainMax,float exponent){
|
||||
if(m.type()==CV_16UC3){
|
||||
cv::MatIterator_<cv::Vec3w> it, end;
|
||||
for( it = m.begin<cv::Vec3w>(), end = m.end<cv::Vec3w>(); it != end; ++it)
|
||||
{
|
||||
(*it)[0] =uGammaCompress_1pix((*it)[0],threshold,gainMin,gainMax,exponent);
|
||||
(*it)[1] =uGammaCompress_1pix((*it)[1],threshold,gainMin,gainMax,exponent);
|
||||
(*it)[2] =uGammaCompress_1pix((*it)[2],threshold,gainMin,gainMax,exponent);
|
||||
}
|
||||
}else if(m.type()==CV_16UC1){
|
||||
u_int16_t* ptr = (u_int16_t*)m.data;
|
||||
int end = m.rows*m.cols;
|
||||
for(int i=0;i<end;i++){
|
||||
*(ptr+i) = uGammaCompress_1pix(*(ptr+i),threshold,gainMin,gainMax,exponent);
|
||||
}
|
||||
|
||||
}else{
|
||||
std::cout<<"Unsupported Data Type"<<std::endl;
|
||||
}
|
||||
return m;
|
||||
}
|
||||
|
||||
cv::Mat uGammaDecompress_(cv::Mat m,float threshold,float gainMin,float gainMax,float exponent){
|
||||
if(m.type()==CV_16UC3){
|
||||
cv::MatIterator_<cv::Vec3w> it, end;
|
||||
for( it = m.begin<cv::Vec3w>(), end = m.end<cv::Vec3w>(); it != end; ++it)
|
||||
{
|
||||
(*it)[0] =uGammaDecompress_1pix((*it)[0],threshold,gainMin,gainMax,exponent);
|
||||
(*it)[1] =uGammaDecompress_1pix((*it)[1],threshold,gainMin,gainMax,exponent);
|
||||
(*it)[2] =uGammaDecompress_1pix((*it)[2],threshold,gainMin,gainMax,exponent);
|
||||
}
|
||||
}else if(m.type()==CV_16UC1){
|
||||
u_int16_t* ptr = (u_int16_t*)m.data;
|
||||
int end = m.rows*m.cols;
|
||||
for(int i=0;i<end;i++){
|
||||
*(ptr+i) = uGammaDecompress_1pix(*(ptr+i),threshold,gainMin,gainMax,exponent);
|
||||
}
|
||||
|
||||
}else{
|
||||
std::cout<<"Unsupported Data Type"<<std::endl;
|
||||
}
|
||||
|
||||
return m;
|
||||
}
|
||||
|
||||
cv::Mat gammasRGB(cv::Mat img, bool mode){
|
||||
if(mode){// compress
|
||||
return uGammaCompress_(img,0.0031308, 12.92, 1.055, 1. / 2.4);
|
||||
}else{ // decompress
|
||||
return uGammaDecompress_(img, 0.04045, 12.92, 1.055, 2.4);
|
||||
}
|
||||
}
|
||||
|
||||
void copy_mat_16U_2(u_int16_t* ptr_A, cv::Mat B){
|
||||
// u_int16_t* ptr_A = (u_int16_t*)A.data;
|
||||
u_int16_t* ptr_B = (u_int16_t*)B.data;
|
||||
for(int r = 0; r < B.rows; r++) {
|
||||
for(int c = 0; c < B.cols; c++) {
|
||||
*(ptr_A+r*B.cols+c) = *(ptr_B+r*B.cols+c);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
cv::Mat mean_(cv::Mat img){
|
||||
// initialize processedImg
|
||||
int H = img.rows;
|
||||
int W = img.cols;
|
||||
cv::Mat processedImg = cv::Mat(H,W,CV_16UC1);
|
||||
u_int16_t* ptr = (u_int16_t*)processedImg.data;
|
||||
|
||||
// traverse img
|
||||
int idx = 0;
|
||||
cv::MatIterator_<cv::Vec3w> it, end;
|
||||
for( it = img.begin<cv::Vec3w>(), end = img.end<cv::Vec3w>(); it != end; ++it)
|
||||
{
|
||||
uint32_t tmp = (*it)[0]+(*it)[1]+(*it)[2];
|
||||
uint16_t avg_val = tmp/3;
|
||||
*(ptr+idx) = avg_val;
|
||||
idx++;
|
||||
}
|
||||
|
||||
return processedImg;
|
||||
}
|
||||
|
||||
double getMean(cv::Mat img){
|
||||
u_int16_t* ptr = (u_int16_t*)img.data;
|
||||
int max_idx = img.rows*img.cols*img.channels();
|
||||
double sum=0;
|
||||
for(int i=0;i<max_idx;i++){
|
||||
sum += *(ptr+i);
|
||||
}
|
||||
sum/=max_idx;
|
||||
sum/=USHRT_MAX;
|
||||
return sum;
|
||||
}
|
||||
|
||||
cv::Mat matMultiply_scalar(cv::Mat img,float gain){
|
||||
u_int16_t* ptr = (u_int16_t*)img.data;
|
||||
int max_idx = img.rows*img.cols*img.channels();
|
||||
for(int i=0;i<max_idx;i++){
|
||||
double tmp = *(ptr+i)*gain;
|
||||
if(tmp<0){
|
||||
*(ptr+i)=0;
|
||||
}else if(tmp>USHRT_MAX){
|
||||
*(ptr+i) = USHRT_MAX;
|
||||
}else{
|
||||
*(ptr+i)=(u_int16_t)tmp;
|
||||
}
|
||||
}
|
||||
return img;
|
||||
}
|
||||
|
||||
double getSaturated(cv::Mat img, double threshold){
|
||||
threshold *= USHRT_MAX;
|
||||
double count=0;
|
||||
u_int16_t* ptr = (u_int16_t*)img.data;
|
||||
int max_idx = img.rows*img.cols*img.channels();
|
||||
for(int i=0;i<max_idx;i++){
|
||||
if(*(ptr+i)>threshold){
|
||||
count++;
|
||||
}
|
||||
}
|
||||
return count/(double)max_idx;
|
||||
|
||||
}
|
||||
|
||||
cv::Mat meanGain_(cv::Mat img,int gain){
|
||||
if(img.channels()!=3){
|
||||
std::cout<<"unsupport img type in meanGain_()"<<std::endl;
|
||||
return cv::Mat();
|
||||
}else{ // RGB img
|
||||
int H = img.rows;
|
||||
int W = img.cols;
|
||||
cv::Mat processedImg = cv::Mat(H,W,CV_16UC1);
|
||||
u_int16_t* ptr = (u_int16_t*)processedImg.data;
|
||||
int idx=0;
|
||||
|
||||
cv::MatIterator_<cv::Vec3w> it, end;
|
||||
for( it = img.begin<cv::Vec3w>(), end = img.end<cv::Vec3w>(); it != end; ++it)
|
||||
{
|
||||
double sum = 0;
|
||||
// R
|
||||
double tmp = (*it)[0]*gain;
|
||||
if(tmp<0) tmp=0;
|
||||
if(tmp>USHRT_MAX) tmp = USHRT_MAX;
|
||||
sum+=tmp;
|
||||
|
||||
// G
|
||||
tmp = (*it)[1]*gain;
|
||||
if(tmp<0) tmp=0;
|
||||
if(tmp>USHRT_MAX) tmp = USHRT_MAX;
|
||||
sum+=tmp;
|
||||
|
||||
// B
|
||||
tmp = (*it)[2]*gain;
|
||||
if(tmp<0) tmp=0;
|
||||
if(tmp>USHRT_MAX) tmp = USHRT_MAX;
|
||||
sum+=tmp;
|
||||
|
||||
// put into processedImg
|
||||
uint16_t avg_val = sum/3;
|
||||
*(ptr+idx) = avg_val;
|
||||
idx++;
|
||||
}
|
||||
return processedImg;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
cv::Mat applyScaling_(cv::Mat mergedImage, cv::Mat shortGray, cv::Mat fusedGray){
|
||||
cv::Mat result = mergedImage.clone();
|
||||
u_int16_t* ptr_shortg = (u_int16_t*)shortGray.data;
|
||||
u_int16_t* ptr_fusedg = (u_int16_t*)fusedGray.data;
|
||||
int count = 0;
|
||||
cv::MatIterator_<cv::Vec3w> it, end;
|
||||
for( it = result.begin<cv::Vec3w>(), end = result.end<cv::Vec3w>(); it != end; ++it)
|
||||
{
|
||||
double s = 1;
|
||||
if(*(ptr_shortg+count)!=0){
|
||||
s = *(ptr_fusedg+count);
|
||||
s/=*(ptr_shortg+count);
|
||||
}
|
||||
for(int c=0;c<mergedImage.channels();c++){
|
||||
double tmp = (*it)[c]*s;
|
||||
if(tmp<0){
|
||||
(*it)[c] = 0;
|
||||
}else if(tmp>USHRT_MAX){
|
||||
(*it)[c] = USHRT_MAX;
|
||||
}else{
|
||||
(*it)[c] = tmp;
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void localToneMap(cv::Mat& mergedImage, Options options, cv::Mat& shortg,
|
||||
cv::Mat& longg, cv::Mat& fusedg, int& gain){
|
||||
std::cout<<"HDR Tone Mapping..."<<std::endl;
|
||||
// # Work with grayscale images
|
||||
cv::Mat shortGray = rgb_2_gray<uint16_t, uint16_t, CV_16U>(mergedImage); //mean_(mergedImage);
|
||||
std::cout<<"--- Compute grayscale image"<<std::endl;
|
||||
|
||||
// compute gain
|
||||
gain = 0;
|
||||
if(options.ltmGain==-1){
|
||||
double dsFactor = 25;
|
||||
int down_height = round(shortGray.rows/dsFactor);
|
||||
int down_width = round(shortGray.cols/dsFactor);
|
||||
cv::Mat shortS;
|
||||
cv::resize(shortGray,shortS,cv::Size(down_height,down_width),cv::INTER_LINEAR);
|
||||
shortS = shortS.reshape(1,1);
|
||||
|
||||
bool bestGain = false;
|
||||
double compression = 1.0;
|
||||
double saturated = 0.0;
|
||||
cv::Mat shortSg = gammasRGB(shortS.clone(), true);
|
||||
double sSMean = getMean(shortSg);
|
||||
|
||||
while((compression < 1.9 && saturated < .95)||((!bestGain) && (compression < 6) && (gain < 30) && (saturated < 0.33))){
|
||||
gain += 2;
|
||||
cv::Mat longSg = gammasRGB(shortS.clone()*gain, true);
|
||||
double lSMean = getMean(longSg);
|
||||
compression = lSMean / sSMean;
|
||||
bestGain = lSMean > (1 - sSMean) / 2; // only works if burst underexposed
|
||||
saturated = getSaturated(longSg,0.95);
|
||||
if(options.verbose==4){
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}else{
|
||||
if(options.ltmGain>0){
|
||||
gain = options.ltmGain;
|
||||
}
|
||||
}
|
||||
std::cout<<"--- Compute gain"<<std::endl;
|
||||
// create a synthetic long exposure
|
||||
cv::Mat longGray = meanGain_(mergedImage.clone(),gain);
|
||||
std::cout<<"--- Synthetic long expo"<<std::endl;
|
||||
// apply gamma correction to both
|
||||
longg = gammasRGB(longGray.clone(), true);
|
||||
shortg = gammasRGB(shortGray.clone(),true);
|
||||
std::cout<<"--- Apply Gamma correction"<<std::endl;
|
||||
// perform tone mapping by exposure fusion in grayscale
|
||||
cv::Ptr<cv::MergeMertens> mergeMertens = cv::createMergeMertens();
|
||||
std::cout<<"--- Create Mertens"<<std::endl;
|
||||
// hack: cv2 mergeMertens expects inputs between 0 and 255
|
||||
// but the result is scaled between 0 and 1 (some values can actually be greater than 1!)
|
||||
std::vector<cv::Mat> src_expos;
|
||||
src_expos.push_back(convert16bit2_8bit_(shortg.clone()));
|
||||
src_expos.push_back(convert16bit2_8bit_(longg.clone()));
|
||||
mergeMertens->process(src_expos, fusedg);
|
||||
fusedg = fusedg*USHRT_MAX;
|
||||
fusedg.convertTo(fusedg, CV_16UC1);
|
||||
std::cout<<"--- Apply Mertens"<<std::endl;
|
||||
// undo gamma correction
|
||||
cv::Mat fusedGray = gammasRGB(fusedg.clone(), false);
|
||||
// cv::imwrite("fusedg_degamma.png", fusedGray);
|
||||
std::cout<<"--- Un-apply Gamma correction"<<std::endl;
|
||||
// scale each RGB channel of the short exposure accordingly
|
||||
mergedImage = applyScaling_(mergedImage, shortGray, fusedGray);
|
||||
std::cout<<"--- Scale channels"<<std::endl;
|
||||
}
|
||||
|
||||
u_int16_t enhanceContrast_1pix(u_int16_t pix_val,double gain){
|
||||
double x = pix_val;
|
||||
x/=USHRT_MAX;
|
||||
x = x - gain*sin(2*M_PI*x);
|
||||
if(x<0){
|
||||
x = 0;
|
||||
}else if(x>1){
|
||||
x = 1;
|
||||
}
|
||||
u_int16_t result = x*USHRT_MAX;
|
||||
return result;
|
||||
}
|
||||
|
||||
cv::Mat enhanceContrast(cv::Mat image, Options options){
|
||||
if(options.gtmContrast>=0 && options.gtmContrast<=1){
|
||||
u_int16_t* ptr = (u_int16_t*)image.data;
|
||||
int end = image.rows*image.cols*image.channels();
|
||||
for(int idx = 0;idx<end;idx++){
|
||||
*(ptr+idx) = enhanceContrast_1pix(*(ptr+idx),options.gtmContrast);
|
||||
}
|
||||
}else{
|
||||
std::cout<<"GTM ignored, expected a contrast enhancement ratio between 0 and 1"<<std::endl;
|
||||
}
|
||||
return image;
|
||||
}
|
||||
|
||||
cv::Mat distL1_(cv::Mat X, cv::Mat Y){
|
||||
int end_x = X.rows*X.cols*X.channels();
|
||||
int end_y = Y.rows*Y.cols*Y.channels();
|
||||
cv::Mat result = cv::Mat(X.rows,X.cols,X.type());
|
||||
if(end_x==end_y){
|
||||
u_int16_t* ptr_x = (u_int16_t*)X.data;
|
||||
u_int16_t* ptr_y = (u_int16_t*)Y.data;
|
||||
u_int16_t* ptr_r = (u_int16_t*)result.data;
|
||||
for(int i=0;i<end_x;i++){
|
||||
if(*(ptr_x+i)<*(ptr_y+i)){
|
||||
*(ptr_r+i) = *(ptr_y+i) - *(ptr_x+i);
|
||||
}else{
|
||||
*(ptr_r+i) = *(ptr_x+i) - *(ptr_y+i);
|
||||
}
|
||||
}
|
||||
}else{
|
||||
std::cout<<"Mat size not match. distL1_ failed!"<<std::endl;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
cv::Mat sharpenTriple_(cv::Mat image,
|
||||
cv::Mat blur0, cv::Mat low0, float th0, float k0,
|
||||
cv::Mat blur1, cv::Mat low1, float th1, float k1,
|
||||
cv::Mat blur2, cv::Mat low2, float th2, float k2){
|
||||
// create result mat
|
||||
cv::Mat result = cv::Mat(image.rows,image.cols,image.type());
|
||||
// initialize iteraters
|
||||
u_int16_t* ptr_r = (u_int16_t*)result.data;
|
||||
u_int16_t* ptr_img = (u_int16_t*)image.data;
|
||||
u_int16_t* ptr_blur0 = (u_int16_t*)blur0.data;
|
||||
u_int16_t* ptr_low0 = (u_int16_t*)low0.data;
|
||||
u_int16_t* ptr_blur1 = (u_int16_t*)blur1.data;
|
||||
u_int16_t* ptr_low1 = (u_int16_t*)low1.data;
|
||||
u_int16_t* ptr_blur2 = (u_int16_t*)blur2.data;
|
||||
u_int16_t* ptr_low2 = (u_int16_t*)low2.data;
|
||||
int n_channels = image.channels();
|
||||
int end = image.rows*image.cols*n_channels;
|
||||
// traverse Image
|
||||
for(int idx = 0;idx<end;idx++){
|
||||
double r, r0, r1, r2;
|
||||
double x = *(ptr_img+idx);
|
||||
double l0 = *(ptr_low0+idx)/(double)USHRT_MAX;
|
||||
double l1 = *(ptr_low1+idx)/(double)USHRT_MAX;
|
||||
double l2 = *(ptr_low2+idx)/(double)USHRT_MAX;
|
||||
double b0 = *(ptr_blur0+idx);
|
||||
double b1 = *(ptr_blur1+idx);
|
||||
double b2 = *(ptr_blur2+idx);
|
||||
r0 = l0<th0? x:x+k0*(x-b0);
|
||||
r1 = l1<th1? x:x+k1*(x-b1);
|
||||
r2 = l2<th2? x:x+k2*(x-b2);
|
||||
r = (r0+r1+r2)/3.0;
|
||||
if(r<0) r=0;
|
||||
if(r>USHRT_MAX) r = USHRT_MAX;
|
||||
*(ptr_r+idx) = (u_int16_t)r;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
cv::Mat sharpenTriple(cv::Mat image, Tuning tuning, Options options){
|
||||
// sharpen the image using unsharp masking
|
||||
std::vector<float> amounts = tuning.sharpenAmount;
|
||||
std::vector<float> sigmas = tuning.sharpenSigma;
|
||||
std::vector<float> thresholds = tuning.sharpenThreshold;
|
||||
// Compute all Gaussian blur
|
||||
cv::Mat blur0,blur1,blur2;
|
||||
cv::GaussianBlur(image,blur0,cv::Size(0,0),sigmas[0]);
|
||||
cv::GaussianBlur(image,blur1,cv::Size(0,0),sigmas[1]);
|
||||
cv::GaussianBlur(image,blur2,cv::Size(0,0),sigmas[2]);
|
||||
std::cout<<" --- gaussian blur"<<std::endl;
|
||||
// cv::imwrite("blur2.png", blur2);
|
||||
// Compute all low contrast images
|
||||
cv::Mat low0 = distL1_(blur0, image);
|
||||
cv::Mat low1 = distL1_(blur1, image);
|
||||
cv::Mat low2 = distL1_(blur2, image);
|
||||
std::cout<<" --- low contrast"<<std::endl;
|
||||
// cv::imwrite("low2.png", low2);
|
||||
// Compute the triple sharpen
|
||||
cv::Mat sharpImage = sharpenTriple_(image,
|
||||
blur0, low0, thresholds[0], amounts[0],
|
||||
blur1, low1, thresholds[1], amounts[1],
|
||||
blur2, low2, thresholds[2], amounts[2]);
|
||||
std::cout<<" --- sharpen"<<std::endl;
|
||||
return sharpImage;
|
||||
}
|
||||
|
||||
void copy_mat_16U_3(u_int16_t* ptr_A, cv::Mat B){
|
||||
// u_int16_t* ptr_A = (u_int16_t*)A.data;
|
||||
u_int16_t* ptr_B = (u_int16_t*)B.data;
|
||||
int H = B.rows;
|
||||
int W = B.cols;
|
||||
int end = H*W;
|
||||
for(int i=0;i<end;i++){
|
||||
*(ptr_A+i) = *(ptr_B+i);
|
||||
}
|
||||
}
|
||||
|
||||
// void copy_mat_16U_3(u_int16_t* ptr_A, cv::Mat B){
|
||||
// // u_int16_t* ptr_A = (u_int16_t*)A.data;
|
||||
// u_int16_t* ptr_B = (u_int16_t*)B.data;
|
||||
// for(int r = 0; r < B.rows; r++) {
|
||||
// for(int c = 0; c < B.cols; c++) {
|
||||
// *(ptr_A+r*B.cols+c) = *(ptr_B+r*B.cols+c);
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
cv::Mat processMergedMat(cv::Mat mergedImg, int opencv_type){
|
||||
cv::Mat m;
|
||||
#if 0
|
||||
uint16_t* ptr = (uint16_t*)mergedImg.data;
|
||||
for(int r = 0; r < mergedImg.rows; r++) {
|
||||
std::vector<int> dvals;
|
||||
for(int c = 0; c < mergedImg.cols; c++) {
|
||||
dvals.push_back(*(ptr+r*mergedImg.cols+c));
|
||||
}
|
||||
cv::Mat mline(dvals, true);
|
||||
cv::transpose(mline, mline);
|
||||
m.push_back(mline);
|
||||
}
|
||||
#endif
|
||||
int ch = CV_MAT_CN(opencv_type);
|
||||
|
||||
m = mergedImg.clone();
|
||||
m = m.reshape(ch);
|
||||
m.convertTo(m, opencv_type);
|
||||
|
||||
return m;
|
||||
}
|
||||
|
||||
void show20_20(cv::Mat m){
|
||||
u_int16_t* ptr = (u_int16_t*)m.data;
|
||||
for(int i=0;i<20;i++){
|
||||
for(int j=0;j<20;j++){
|
||||
std::cout<<*(ptr+i*m.cols+j)<<", ";
|
||||
}
|
||||
std::cout<<std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
void writeCSV(std::string filename, cv::Mat m)
|
||||
{
|
||||
std::ofstream myfile;
|
||||
myfile.open(filename.c_str());
|
||||
myfile<< cv::format(m, cv::Formatter::FMT_CSV) << std::endl;
|
||||
myfile.close();
|
||||
}
|
||||
|
||||
void finish::process(const hdrplus::burst& burst_images, cv::Mat& finalOutputImage){
|
||||
// copy mergedBayer to rawReference
|
||||
std::cout<<"finish pipeline start ..."<<std::endl;
|
||||
|
||||
// save merged Image value
|
||||
#ifndef HDRPLUS_NO_DETAILED_OUTPUT
|
||||
writeCSV(DBG_OUTPUT_ROOT "merged.csv",burst_images.merged_bayer_image);
|
||||
#endif
|
||||
|
||||
this->refIdx = burst_images.reference_image_idx;
|
||||
// this->burstPath = burstPath;
|
||||
// std::cout<<"processMerged:"<<std::endl;
|
||||
// show20_20(mergedB);
|
||||
|
||||
#ifndef HDRPLUS_NO_DETAILED_OUTPUT
|
||||
this->mergedBayer = loadFromCSV(DBG_OUTPUT_ROOT "merged.csv", CV_16UC1);
|
||||
// this->mergedBayer = processMergedMat(mergedB,CV_16UC1);//loadFromCSV("merged.csv", CV_16UC1);
|
||||
// std::cout<<"processMerged:"<<std::endl;
|
||||
// show20_20(this->mergedBayer);
|
||||
// this->mergedBayer = loadFromCSV(DBG_OUTPUT_ROOT "merged.csv", CV_16UC1);
|
||||
// this->mergedBayer = processMergedMat(burst_images.merged_bayer_image, CV_16UC1);
|
||||
#else
|
||||
// this->mergedBayer = loadFromCSV(DBG_OUTPUT_ROOT "merged.csv", CV_16UC1);
|
||||
this->mergedBayer = processMergedMat(burst_images.merged_bayer_image, CV_16UC1);
|
||||
// std::cout<<"processMerged:"<<std::endl;
|
||||
#endif
|
||||
// std::cout<<"csv:"<<std::endl;
|
||||
// show20_20(this->mergedBayer);
|
||||
// load_rawPathList(burstPath);
|
||||
|
||||
// read in ref img
|
||||
// bayer_image* ref = new bayer_image(rawPathList[refIdx]);
|
||||
bayer_image* ref = new bayer_image(burst_images.bayer_images[burst_images.reference_image_idx]);
|
||||
cv::Mat processedRefImage = postprocess(ref->libraw_processor,params.rawpyArgs);
|
||||
|
||||
std::cout<<"size ref: "<<processedRefImage.rows<<"*"<<processedRefImage.cols<<std::endl;
|
||||
|
||||
// write reference image
|
||||
#ifndef HDRPLUS_NO_DETAILED_OUTPUT
|
||||
if(params.flags["writeReferenceImage"]){
|
||||
std::cout<<"writing reference img ..."<<std::endl;
|
||||
cv::Mat outputImg = convert16bit2_8bit_(processedRefImage.clone());
|
||||
cv::cvtColor(outputImg, outputImg, cv::COLOR_RGB2BGR);
|
||||
// cv::imshow("test",processedImage);
|
||||
cv::imwrite(DBG_OUTPUT_ROOT "processedRef.jpg", outputImg);
|
||||
// cv::waitKey(0);
|
||||
}
|
||||
#endif
|
||||
|
||||
// write gamma reference
|
||||
#ifndef HDRPLUS_NO_DETAILED_OUTPUT
|
||||
if(params.flags["writeGammaReference"]){
|
||||
std::cout<<"writing Gamma reference img ..."<<std::endl;
|
||||
cv::Mat outputImg = gammasRGB(processedRefImage.clone(),true);
|
||||
outputImg = convert16bit2_8bit_(outputImg);
|
||||
cv::cvtColor(outputImg, outputImg, cv::COLOR_RGB2BGR);
|
||||
cv::imwrite(DBG_OUTPUT_ROOT "processedRefGamma.jpg", outputImg);
|
||||
}
|
||||
#endif
|
||||
|
||||
// get the bayer_image of the merged image
|
||||
// bayer_image* mergedImg = new bayer_image(rawPathList[refIdx]);
|
||||
bayer_image* mergedImg = new bayer_image(burst_images.bayer_images[this->refIdx]);
|
||||
mergedImg->libraw_processor->imgdata.rawdata.raw_image = (uint16_t*)this->mergedBayer.data;
|
||||
// copy_mat_16U_3(mergedImg->libraw_processor->imgdata.rawdata.raw_image,this->mergedBayer);
|
||||
cv::Mat processedMerge = postprocess(mergedImg->libraw_processor,params.rawpyArgs);
|
||||
|
||||
// write merged image
|
||||
#ifndef HDRPLUS_NO_DETAILED_OUTPUT
|
||||
if(params.flags["writeMergedImage"]){
|
||||
std::cout<<"writing Merged img ..."<<std::endl;
|
||||
cv::Mat outputImg = convert16bit2_8bit_(processedMerge.clone());
|
||||
cv::cvtColor(outputImg, outputImg, cv::COLOR_RGB2BGR);
|
||||
cv::imwrite(DBG_OUTPUT_ROOT "mergedImg.jpg", outputImg);
|
||||
}
|
||||
#endif
|
||||
|
||||
// write gamma merged image
|
||||
#ifndef HDRPLUS_NO_DETAILED_OUTPUT
|
||||
if(params.flags["writeMergedImage"]){
|
||||
std::cout<<"writing Gamma Merged img ..."<<std::endl;
|
||||
cv::Mat outputImg = gammasRGB(processedMerge.clone(),true);
|
||||
outputImg = convert16bit2_8bit_(outputImg);
|
||||
cv::cvtColor(outputImg, outputImg, cv::COLOR_RGB2BGR);
|
||||
cv::imwrite(DBG_OUTPUT_ROOT "mergedImgGamma.jpg", outputImg);
|
||||
}
|
||||
#endif
|
||||
|
||||
// step 5. HDR tone mapping
|
||||
// processedImage, gain, shortExposure, longExposure, fusedExposure = localToneMap(burstPath, processedImage, options)
|
||||
int gain;
|
||||
if(params.options.ltmGain){
|
||||
cv::Mat shortExposure, longExposure, fusedExposure;
|
||||
|
||||
localToneMap(processedMerge, params.options,shortExposure,longExposure,fusedExposure,gain);
|
||||
std::cout<<"gain="<< gain<<std::endl;
|
||||
#ifndef HDRPLUS_NO_DETAILED_OUTPUT
|
||||
if(params.flags["writeShortExposure"]){
|
||||
std::cout<<"writing ShortExposure img ..."<<std::endl;
|
||||
cv::Mat outputImg = convert16bit2_8bit_(shortExposure);
|
||||
cv::imwrite(DBG_OUTPUT_ROOT "shortg.jpg", outputImg);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifndef HDRPLUS_NO_DETAILED_OUTPUT
|
||||
if(params.flags["writeLongExposure"]){
|
||||
std::cout<<"writing LongExposure img ..."<<std::endl;
|
||||
cv::Mat outputImg = convert16bit2_8bit_(longExposure);
|
||||
cv::imwrite(DBG_OUTPUT_ROOT "longg.jpg", outputImg);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifndef HDRPLUS_NO_DETAILED_OUTPUT
|
||||
if(params.flags["writeFusedExposure"]){
|
||||
std::cout<<"writing FusedExposure img ..."<<std::endl;
|
||||
cv::Mat outputImg = convert16bit2_8bit_(fusedExposure);
|
||||
cv::imwrite(DBG_OUTPUT_ROOT "fusedg.jpg", outputImg);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifndef HDRPLUS_NO_DETAILED_OUTPUT
|
||||
if(params.flags["writeLTMImage"]){
|
||||
std::cout<<"writing LTMImage ..."<<std::endl;
|
||||
cv::Mat outputImg = convert16bit2_8bit_(processedMerge.clone());
|
||||
cv::cvtColor(outputImg, outputImg, cv::COLOR_RGB2BGR);
|
||||
cv::imwrite(DBG_OUTPUT_ROOT "ltmGain.jpg", outputImg);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifndef HDRPLUS_NO_DETAILED_OUTPUT
|
||||
if(params.flags["writeLTMGamma"]){
|
||||
std::cout<<"writing LTMImage Gamma ..."<<std::endl;
|
||||
cv::Mat outputImg = gammasRGB(processedMerge.clone(),true);
|
||||
outputImg = convert16bit2_8bit_(outputImg);
|
||||
cv::cvtColor(outputImg, outputImg, cv::COLOR_RGB2BGR);
|
||||
cv::imwrite(DBG_OUTPUT_ROOT "ltmGain_gamma.jpg", outputImg);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
// step 6 GTM: contrast enhancement / global tone mapping
|
||||
if(params.options.gtmContrast){
|
||||
processedMerge = enhanceContrast(processedMerge, params.options);
|
||||
std::cout<<"STEP 6 -- Apply GTM"<<std::endl;
|
||||
}
|
||||
|
||||
// apply the final sRGB gamma curve
|
||||
processedMerge = gammasRGB(processedMerge.clone(),true);
|
||||
std::cout<<"-- Apply Gamma"<<std::endl;
|
||||
|
||||
#ifndef HDRPLUS_NO_DETAILED_OUTPUT
|
||||
if(params.flags["writeGTMImage"]) {
|
||||
std::cout<<"writing GTMImage ..."<<std::endl;
|
||||
cv::Mat outputImg = convert16bit2_8bit_(processedMerge.clone());
|
||||
cv::cvtColor(outputImg, outputImg, cv::COLOR_RGB2BGR);
|
||||
|
||||
cv::imwrite(DBG_OUTPUT_ROOT "GTM_gamma.jpg", outputImg);
|
||||
}
|
||||
#endif
|
||||
|
||||
// Step 7: sharpen
|
||||
finalOutputImage = sharpenTriple(processedMerge.clone(), params.tuning, params.options);
|
||||
cv::Mat& processedImage = finalOutputImage;
|
||||
#ifndef HDRPLUS_NO_DETAILED_OUTPUT
|
||||
if(params.flags["writeFinalImage"]){
|
||||
std::cout<<"writing FinalImage ..."<<std::endl;
|
||||
cv::Mat outputImg = convert16bit2_8bit_(processedImage.clone());
|
||||
cv::cvtColor(outputImg, outputImg, cv::COLOR_RGB2BGR);
|
||||
|
||||
cv::imwrite(DBG_OUTPUT_ROOT "FinalImage.jpg", outputImg);
|
||||
}
|
||||
#endif
|
||||
|
||||
// write final ref
|
||||
#ifndef HDRPLUS_NO_DETAILED_OUTPUT
|
||||
if(params.flags["writeReferenceFinal"]){
|
||||
std::cout<<"writing Final Ref Image ..."<<std::endl;
|
||||
if(params.options.ltmGain){
|
||||
params.options.ltmGain = gain;
|
||||
}
|
||||
cv::Mat shortExposureRef, longExposureRef, fusedExposureRef;
|
||||
localToneMap(processedRefImage, params.options,shortExposureRef,longExposureRef,fusedExposureRef,gain);
|
||||
if(params.options.gtmContrast){ // contrast enhancement / global tone mapping
|
||||
processedRefImage = enhanceContrast(processedRefImage, params.options);
|
||||
}
|
||||
processedRefImage = gammasRGB(processedRefImage.clone(),true);
|
||||
// sharpen
|
||||
processedRefImage = sharpenTriple(processedRefImage.clone(), params.tuning, params.options);
|
||||
cv::Mat outputImg = convert16bit2_8bit_(processedRefImage.clone());
|
||||
cv::cvtColor(outputImg, outputImg, cv::COLOR_RGB2BGR);
|
||||
|
||||
cv::imwrite(DBG_OUTPUT_ROOT "FinalReference.jpg", outputImg);
|
||||
}
|
||||
#endif
|
||||
// End of finishing
|
||||
}
|
||||
|
||||
void finish::copy_mat_16U(cv::Mat& A, cv::Mat B){
|
||||
u_int16_t* ptr_A = (u_int16_t*)A.data;
|
||||
u_int16_t* ptr_B = (u_int16_t*)B.data;
|
||||
for(int r = 0; r < A.rows; r++) {
|
||||
for(int c = 0; c < A.cols; c++) {
|
||||
*(ptr_A+r*A.cols+c) = *(ptr_B+r*B.cols+c);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
void finish::copy_rawImg2libraw(std::shared_ptr<LibRaw>& libraw_ptr, cv::Mat B){
|
||||
u_int16_t* ptr_A = (u_int16_t*)libraw_ptr->imgdata.rawdata.raw_image;
|
||||
u_int16_t* ptr_B = (u_int16_t*)B.data;
|
||||
for(int r = 0; r < B.rows; r++) {
|
||||
for(int c = 0; c < B.cols; c++) {
|
||||
*(ptr_A+r*B.cols+c) = *(ptr_B+r*B.cols+c);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
} // namespace hdrplus
|
@ -0,0 +1,138 @@
|
||||
#include <cstdio>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <utility> // std::pair
|
||||
#include <opencv2/opencv.hpp> // all opencv header
|
||||
#include "hdrplus/hdrplus_pipeline.h"
|
||||
#include "hdrplus/burst.h"
|
||||
#include "hdrplus/align.h"
|
||||
#include "hdrplus/merge.h"
|
||||
#include "hdrplus/finish.h"
|
||||
#include <fstream>
|
||||
|
||||
#ifdef __ANDROID__
|
||||
// #include <AndroidHelper.h>
|
||||
#endif
|
||||
|
||||
namespace hdrplus
|
||||
{
|
||||
|
||||
void hdrplus_pipeline::run_pipeline( \
|
||||
const std::string& burst_path, \
|
||||
const std::string& reference_image_path )
|
||||
{
|
||||
// Create burst of images
|
||||
burst burst_images( burst_path, reference_image_path );
|
||||
|
||||
std::vector<std::vector<std::vector<std::pair<int, int>>>> alignments;
|
||||
|
||||
// Run align
|
||||
align_module.process( burst_images, alignments );
|
||||
|
||||
// Run merging
|
||||
merge_module.process( burst_images, alignments );
|
||||
|
||||
|
||||
// Run finishing
|
||||
cv::Mat finalImg;
|
||||
finish_module.process( burst_images, finalImg);
|
||||
}
|
||||
|
||||
bool hdrplus_pipeline::run_pipeline( \
|
||||
const std::vector<std::string>& burst_paths, \
|
||||
int reference_image_index, cv::Mat& finalImg )
|
||||
{
|
||||
// Create burst of images
|
||||
burst burst_images( burst_paths, reference_image_index );
|
||||
std::vector<std::vector<std::vector<std::pair<int, int>>>> alignments;
|
||||
#ifdef __ANDROID__
|
||||
// ALOGI("Finish loading images");
|
||||
#endif
|
||||
|
||||
// Run align
|
||||
align_module.process( burst_images, alignments );
|
||||
#ifdef __ANDROID__
|
||||
// ALOGI("Finish align");
|
||||
#endif
|
||||
|
||||
// Run merging
|
||||
merge_module.process( burst_images, alignments );
|
||||
#ifdef __ANDROID__
|
||||
// ALOGI("Finish merging");
|
||||
#endif
|
||||
|
||||
// Run finishing
|
||||
finish_module.process( burst_images, finalImg);
|
||||
#ifdef __ANDROID__
|
||||
// ALOGI("Finish process");
|
||||
#endif
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool hdrplus_pipeline::run_pipeline( \
|
||||
const std::vector<std::vector<uint8_t> >& burst_contents, \
|
||||
int reference_image_index, cv::Mat& finalImg )
|
||||
{
|
||||
// Create burst of images
|
||||
burst burst_images( burst_contents, reference_image_index );
|
||||
std::vector<std::vector<std::vector<std::pair<int, int>>>> alignments;
|
||||
#ifdef __ANDROID__
|
||||
// ALOGI("Finish loading images");
|
||||
#endif
|
||||
|
||||
// Run align
|
||||
align_module.process( burst_images, alignments );
|
||||
#ifdef __ANDROID__
|
||||
// ALOGI("Finish align");
|
||||
#endif
|
||||
|
||||
// Run merging
|
||||
merge_module.process( burst_images, alignments );
|
||||
#ifdef __ANDROID__
|
||||
// ALOGI("Finish merging");
|
||||
#endif
|
||||
|
||||
// Run finishing
|
||||
finish_module.process( burst_images, finalImg);
|
||||
#ifdef __ANDROID__
|
||||
// ALOGI("Finish process");
|
||||
#endif
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool hdrplus_pipeline::run_pipeline( \
|
||||
const std::vector<std::shared_ptr<MemFile> >& burst_files, \
|
||||
int reference_image_index, cv::Mat& finalImg )
|
||||
{
|
||||
// Create burst of images
|
||||
burst burst_images( burst_files, reference_image_index );
|
||||
std::vector<std::vector<std::vector<std::pair<int, int>>>> alignments;
|
||||
#ifdef __ANDROID__
|
||||
// ALOGI("Finish loading images");
|
||||
#endif
|
||||
|
||||
// Run align
|
||||
align_module.process( burst_images, alignments );
|
||||
#ifdef __ANDROID__
|
||||
// ALOGI("Finish align");
|
||||
#endif
|
||||
|
||||
// Run merging
|
||||
merge_module.process( burst_images, alignments );
|
||||
#ifdef __ANDROID__
|
||||
// ALOGI("Finish merging");
|
||||
#endif
|
||||
|
||||
// Run finishing
|
||||
finish_module.process( burst_images, finalImg);
|
||||
#ifdef __ANDROID__
|
||||
// ALOGI("Finish process");
|
||||
#endif
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace hdrplus
|
@ -0,0 +1,340 @@
|
||||
#include <opencv2/opencv.hpp> // all opencv header
|
||||
#include <vector>
|
||||
#include <utility>
|
||||
#include "hdrplus/merge.h"
|
||||
#include "hdrplus/burst.h"
|
||||
#include "hdrplus/utility.h"
|
||||
|
||||
namespace hdrplus
|
||||
{
|
||||
|
||||
void merge::process(hdrplus::burst& burst_images, \
|
||||
std::vector<std::vector<std::vector<std::pair<int, int>>>>& alignments)
|
||||
{
|
||||
// 4.1 Noise Parameters and RMS
|
||||
// Noise parameters calculated from baseline ISO noise parameters
|
||||
double lambda_shot, lambda_read;
|
||||
std::tie(lambda_shot, lambda_read) = burst_images.bayer_images[burst_images.reference_image_idx].get_noise_params();
|
||||
|
||||
// 4.2-4.4 Denoising and Merging
|
||||
|
||||
// Get padded bayer image
|
||||
cv::Mat reference_image = burst_images.bayer_images_pad[burst_images.reference_image_idx];
|
||||
#ifndef NDEBUG
|
||||
// cv::imwrite("ref.jpg", reference_image);
|
||||
#endif
|
||||
|
||||
// Get raw channels
|
||||
std::vector<cv::Mat> channels(4);
|
||||
hdrplus::extract_rgb_from_bayer<uint16_t>(reference_image, channels[0], channels[1], channels[2], channels[3]);
|
||||
|
||||
std::vector<cv::Mat> processed_channels(4);
|
||||
// For each channel, perform denoising and merge
|
||||
for (int i = 0; i < 4; ++i) {
|
||||
// Get channel mat
|
||||
cv::Mat channel_i = channels[i];
|
||||
// cv::imwrite("ref" + std::to_string(i) + ".jpg", channel_i);
|
||||
|
||||
//we should be getting the individual channel in the same place where we call the processChannel function with the reference channel in its arguments
|
||||
//possibly we could add another argument in the processChannel function which is the channel_i for the alternate image. maybe using a loop to cover all the other images
|
||||
|
||||
//create list of channel_i of alternate images:
|
||||
std::vector<cv::Mat> alternate_channel_i_list;
|
||||
for (int j = 0; j < burst_images.num_images; j++) {
|
||||
if (j != burst_images.reference_image_idx) {
|
||||
|
||||
//get alternate image
|
||||
cv::Mat alt_image = burst_images.bayer_images_pad[j];
|
||||
std::vector<cv::Mat> alt_channels(4);
|
||||
hdrplus::extract_rgb_from_bayer<uint16_t>(alt_image, alt_channels[0], alt_channels[1], alt_channels[2], alt_channels[3]);
|
||||
|
||||
alternate_channel_i_list.push_back(alt_channels[i]);
|
||||
}
|
||||
}
|
||||
|
||||
// Apply merging on the channel
|
||||
cv::Mat merged_channel = processChannel(burst_images, alignments, channel_i, alternate_channel_i_list, lambda_shot, lambda_read);
|
||||
// cv::imwrite("merged" + std::to_string(i) + ".jpg", merged_channel);
|
||||
|
||||
// Put channel raw data back to channels
|
||||
merged_channel.convertTo(processed_channels[i], CV_16U);
|
||||
}
|
||||
|
||||
// Write all channels back to a bayer mat
|
||||
cv::Mat merged(reference_image.rows, reference_image.cols, CV_16U);
|
||||
int x, y;
|
||||
for (y = 0; y < reference_image.rows; ++y){
|
||||
uint16_t* row = merged.ptr<uint16_t>(y);
|
||||
if (y % 2 == 0){
|
||||
uint16_t* i0 = processed_channels[0].ptr<uint16_t>(y / 2);
|
||||
uint16_t* i1 = processed_channels[1].ptr<uint16_t>(y / 2);
|
||||
|
||||
for (x = 0; x < reference_image.cols;){
|
||||
//R
|
||||
row[x] = i0[x / 2];
|
||||
x++;
|
||||
|
||||
//G1
|
||||
row[x] = i1[x / 2];
|
||||
x++;
|
||||
}
|
||||
}
|
||||
else {
|
||||
uint16_t* i2 = processed_channels[2].ptr<uint16_t>(y / 2);
|
||||
uint16_t* i3 = processed_channels[3].ptr<uint16_t>(y / 2);
|
||||
|
||||
for(x = 0; x < reference_image.cols;){
|
||||
//G2
|
||||
row[x] = i2[x / 2];
|
||||
x++;
|
||||
|
||||
//B
|
||||
row[x] = i3[x / 2];
|
||||
x++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Remove padding
|
||||
std::vector<int> padding = burst_images.padding_info_bayer;
|
||||
cv::Range horizontal = cv::Range(padding[2], reference_image.cols - padding[3]);
|
||||
cv::Range vertical = cv::Range(padding[0], reference_image.rows - padding[1]);
|
||||
burst_images.merged_bayer_image = merged(vertical, horizontal);
|
||||
// cv::imwrite("merged.jpg", burst_images.merged_bayer_image);
|
||||
}
|
||||
|
||||
std::vector<cv::Mat> merge::getReferenceTiles(cv::Mat reference_image) {
|
||||
std::vector<cv::Mat> reference_tiles;
|
||||
for (int y = 0; y < reference_image.rows - offset; y += offset) {
|
||||
for (int x = 0; x < reference_image.cols - offset; x += offset) {
|
||||
cv::Mat tile = reference_image(cv::Rect(x, y, TILE_SIZE, TILE_SIZE));
|
||||
reference_tiles.push_back(tile);
|
||||
}
|
||||
}
|
||||
return reference_tiles;
|
||||
}
|
||||
|
||||
cv::Mat merge::mergeTiles(std::vector<cv::Mat> tiles, int num_rows, int num_cols) {
|
||||
// 1. get all four subsets: original (evenly split), horizontal overlapped,
|
||||
// vertical overlapped, 2D overlapped
|
||||
std::vector<std::vector<cv::Mat>> tiles_original;
|
||||
std::vector<cv::Mat> row;
|
||||
for (int y = 0; y < num_rows / offset - 1; y += 2) {
|
||||
row.clear();
|
||||
for (int x = 0; x < num_cols / offset - 1; x += 2) {
|
||||
row.push_back(tiles[y * (num_cols / offset - 1) + x]);
|
||||
}
|
||||
tiles_original.push_back(row);
|
||||
}
|
||||
|
||||
std::vector<std::vector<cv::Mat>> tiles_horizontal;
|
||||
// std::vector<cv::Mat> row;
|
||||
for (int y = 0; y < num_rows / offset - 1; y += 2) {
|
||||
row.clear();
|
||||
for (int x = 1; x < num_cols / offset - 1; x += 2) {
|
||||
row.push_back(tiles[y * (num_cols / offset - 1) + x]);
|
||||
}
|
||||
tiles_horizontal.push_back(row);
|
||||
}
|
||||
|
||||
std::vector<std::vector<cv::Mat>> tiles_vertical;
|
||||
// std::vector<cv::Mat> row;
|
||||
for (int y = 1; y < num_rows / offset - 1; y += 2) {
|
||||
row.clear();
|
||||
for (int x = 0; x < num_cols / offset - 1; x += 2) {
|
||||
row.push_back(tiles[y * (num_cols / offset - 1) + x]);
|
||||
}
|
||||
tiles_vertical.push_back(row);
|
||||
}
|
||||
|
||||
std::vector<std::vector<cv::Mat>> tiles_2d;
|
||||
// std::vector<cv::Mat> row;
|
||||
for (int y = 1; y < num_rows / offset - 1; y += 2) {
|
||||
row.clear();
|
||||
for (int x = 1; x < num_cols / offset - 1; x += 2) {
|
||||
row.push_back(tiles[y * (num_cols / offset - 1) + x]);
|
||||
}
|
||||
tiles_2d.push_back(row);
|
||||
}
|
||||
|
||||
// 2. Concatenate the four subsets
|
||||
cv::Mat img_original = cat2Dtiles(tiles_original);
|
||||
cv::Mat img_horizontal = cat2Dtiles(tiles_horizontal);
|
||||
cv::Mat img_vertical = cat2Dtiles(tiles_vertical);
|
||||
cv::Mat img_2d = cat2Dtiles(tiles_2d);
|
||||
|
||||
// 3. Add the four subsets together
|
||||
img_original(cv::Rect(offset, 0, num_cols - TILE_SIZE, num_rows)) += img_horizontal;
|
||||
img_original(cv::Rect(0, offset, num_cols, num_rows - TILE_SIZE)) += img_vertical;
|
||||
img_original(cv::Rect(offset, offset, num_cols - TILE_SIZE, num_rows - TILE_SIZE)) += img_2d;
|
||||
|
||||
return img_original;
|
||||
}
|
||||
|
||||
cv::Mat merge::processChannel(hdrplus::burst& burst_images, \
|
||||
std::vector<std::vector<std::vector<std::pair<int, int>>>>& alignments, \
|
||||
cv::Mat channel_image, \
|
||||
std::vector<cv::Mat> alternate_channel_i_list,\
|
||||
float lambda_shot, \
|
||||
float lambda_read) {
|
||||
// Get tiles of the reference image
|
||||
std::vector<cv::Mat> reference_tiles = getReferenceTiles(channel_image);
|
||||
|
||||
// Get noise variance (sigma**2 = lambda_shot * tileRMS + lambda_read)
|
||||
std::vector<float> noise_variance = getNoiseVariance(reference_tiles, lambda_shot, lambda_read);
|
||||
|
||||
// Apply FFT on reference tiles (spatial to frequency)
|
||||
std::vector<cv::Mat> reference_tiles_DFT;
|
||||
for (auto ref_tile : reference_tiles) {
|
||||
cv::Mat ref_tile_DFT;
|
||||
ref_tile.convertTo(ref_tile_DFT, CV_32F);
|
||||
cv::dft(ref_tile_DFT, ref_tile_DFT, cv::DFT_COMPLEX_OUTPUT);
|
||||
reference_tiles_DFT.push_back(ref_tile_DFT);
|
||||
}
|
||||
|
||||
// Acquire alternate tiles and apply FFT on them as well
|
||||
std::vector<std::vector<cv::Mat>> alt_tiles_list(reference_tiles.size());
|
||||
int num_tiles_row = alternate_channel_i_list[0].rows / offset - 1;
|
||||
int num_tiles_col = alternate_channel_i_list[0].cols / offset - 1;
|
||||
std::vector<cv::Mat> alt_tiles;
|
||||
for (int y = 0; y < num_tiles_row; ++y) {
|
||||
for (int x = 0; x < num_tiles_col; ++x) {
|
||||
alt_tiles.clear();
|
||||
// Get reference tile location
|
||||
int top_left_y = y * offset;
|
||||
int top_left_x = x * offset;
|
||||
|
||||
for (int i = 0; i < alternate_channel_i_list.size(); ++i) {
|
||||
// Get alignment displacement
|
||||
int displacement_y, displacement_x;
|
||||
std::tie(displacement_y, displacement_x) = alignments[i + 1][y][x];
|
||||
// Get tile
|
||||
cv::Mat alt_tile = alternate_channel_i_list[i](cv::Rect(top_left_x + displacement_x, top_left_y + displacement_y, TILE_SIZE, TILE_SIZE));
|
||||
// Apply FFT
|
||||
cv::Mat alt_tile_DFT;
|
||||
alt_tile.convertTo(alt_tile_DFT, CV_32F);
|
||||
cv::dft(alt_tile_DFT, alt_tile_DFT, cv::DFT_COMPLEX_OUTPUT);
|
||||
alt_tiles.push_back(alt_tile_DFT);
|
||||
}
|
||||
alt_tiles_list[y * num_tiles_col + x] = alt_tiles;
|
||||
}
|
||||
}
|
||||
|
||||
// 4.2 Temporal Denoising
|
||||
reference_tiles_DFT = temporal_denoise(reference_tiles_DFT, alt_tiles_list, noise_variance, TEMPORAL_FACTOR);
|
||||
|
||||
// 4.3 Spatial Denoising
|
||||
reference_tiles_DFT = spatial_denoise(reference_tiles_DFT, alternate_channel_i_list.size(), noise_variance, SPATIAL_FACTOR);
|
||||
//now reference tiles are temporally and spatially denoised
|
||||
|
||||
// Apply IFFT on reference tiles (frequency to spatial)
|
||||
std::vector<cv::Mat> denoised_tiles;
|
||||
for (auto dft_tile : reference_tiles_DFT) {
|
||||
cv::Mat denoised_tile;
|
||||
cv::divide(dft_tile, TILE_SIZE * TILE_SIZE, dft_tile);
|
||||
cv::dft(dft_tile, denoised_tile, cv::DFT_INVERSE | cv::DFT_REAL_OUTPUT);
|
||||
denoised_tiles.push_back(denoised_tile);
|
||||
}
|
||||
reference_tiles = denoised_tiles;
|
||||
|
||||
// 4.4 Cosine Window Merging
|
||||
// Process tiles through 2D cosine window
|
||||
std::vector<cv::Mat> windowed_tiles;
|
||||
for (auto tile : reference_tiles) {
|
||||
windowed_tiles.push_back(cosineWindow2D(tile));
|
||||
}
|
||||
|
||||
// Merge tiles
|
||||
return mergeTiles(windowed_tiles, channel_image.rows, channel_image.cols);
|
||||
}
|
||||
|
||||
std::vector<cv::Mat> merge::temporal_denoise(std::vector<cv::Mat> tiles, std::vector<std::vector<cv::Mat>> alt_tiles, std::vector<float> noise_variance, float temporal_factor) {
|
||||
// goal: temporially denoise using the weiner filter
|
||||
// input:
|
||||
// 1. array of 2D dft tiles of the reference image
|
||||
// 2. array of 2D dft tiles of the aligned alternate image
|
||||
// 3. estimated noise variance
|
||||
// 4. temporal factor
|
||||
// return: merged image patches dft
|
||||
|
||||
// calculate noise scaling
|
||||
double temporal_noise_scaling = (TILE_SIZE * TILE_SIZE * (2.0 / 16)) * TEMPORAL_FACTOR;
|
||||
|
||||
// loop across tiles
|
||||
std::vector<cv::Mat> denoised;
|
||||
for (int i = 0; i < tiles.size(); ++i) {
|
||||
// sum of pairwise denoising
|
||||
cv::Mat tile_sum = tiles[i].clone();
|
||||
double coeff = temporal_noise_scaling * noise_variance[i];
|
||||
|
||||
// Ref tile
|
||||
cv::Mat tile = tiles[i];
|
||||
// Alt tiles
|
||||
std::vector<cv::Mat> alt_tiles_i = alt_tiles[i];
|
||||
|
||||
for (int j = 0; j < alt_tiles_i.size(); ++j) {
|
||||
// Alt tile
|
||||
cv::Mat alt_tile = alt_tiles_i[j];
|
||||
// Tile difference
|
||||
cv::Mat diff = tile - alt_tile;
|
||||
|
||||
// Calculate absolute difference
|
||||
cv::Mat complexMats[2];
|
||||
cv::split(diff, complexMats); // planes[0] = Re(DFT(I)), planes[1] = Im(DFT(I))
|
||||
cv::magnitude(complexMats[0], complexMats[1], complexMats[0]); // planes[0] = magnitude
|
||||
cv::Mat absolute_diff = complexMats[0].mul(complexMats[0]);
|
||||
|
||||
// find shrinkage operator A
|
||||
cv::Mat shrinkage;
|
||||
cv::divide(absolute_diff, absolute_diff + coeff, shrinkage);
|
||||
cv::merge(std::vector<cv::Mat>{shrinkage, shrinkage}, shrinkage);
|
||||
|
||||
// Interpolation
|
||||
tile_sum += alt_tile + diff.mul(shrinkage);
|
||||
}
|
||||
// Average by num of frames
|
||||
cv::divide(tile_sum, alt_tiles_i.size() + 1, tile_sum);
|
||||
denoised.push_back(tile_sum);
|
||||
}
|
||||
|
||||
return denoised;
|
||||
}
|
||||
|
||||
std::vector<cv::Mat> merge::spatial_denoise(std::vector<cv::Mat> tiles, int num_alts, std::vector<float> noise_variance, float spatial_factor) {
|
||||
|
||||
double spatial_noise_scaling = (TILE_SIZE * TILE_SIZE * (1.0 / 16)) * spatial_factor;
|
||||
|
||||
// Calculate |w| using ifftshift
|
||||
cv::Mat row_distances = cv::Mat::zeros(1, TILE_SIZE, CV_32F);
|
||||
for(int i = 0; i < TILE_SIZE; ++i) {
|
||||
row_distances.at<float>(i) = i - offset;
|
||||
}
|
||||
row_distances = cv::repeat(row_distances.t(), 1, TILE_SIZE);
|
||||
cv::Mat col_distances = row_distances.t();
|
||||
cv::Mat distances;
|
||||
cv::sqrt(row_distances.mul(row_distances) + col_distances.mul(col_distances), distances);
|
||||
ifftshift(distances);
|
||||
|
||||
std::vector<cv::Mat> denoised;
|
||||
// Loop through all tiles
|
||||
for (int i = 0; i < tiles.size(); ++i) {
|
||||
cv::Mat tile = tiles[i];
|
||||
float coeff = noise_variance[i] / (num_alts + 1) * spatial_noise_scaling;
|
||||
|
||||
// Calculate absolute difference
|
||||
cv::Mat complexMats[2];
|
||||
cv::split(tile, complexMats); // planes[0] = Re(DFT(I)), planes[1] = Im(DFT(I))
|
||||
cv::magnitude(complexMats[0], complexMats[1], complexMats[0]); // planes[0] = magnitude
|
||||
cv::Mat absolute_diff = complexMats[0].mul(complexMats[0]);
|
||||
|
||||
// Division
|
||||
cv::Mat scale;
|
||||
cv::divide(absolute_diff, absolute_diff + distances * coeff, scale);
|
||||
cv::merge(std::vector<cv::Mat>{scale, scale}, scale);
|
||||
denoised.push_back(tile.mul(scale));
|
||||
}
|
||||
return denoised;
|
||||
}
|
||||
|
||||
|
||||
} // namespace hdrplus
|
@ -0,0 +1,53 @@
|
||||
#include <iostream>
|
||||
#include <opencv2/opencv.hpp> // all opencv header
|
||||
#include <hdrplus/params.h>
|
||||
|
||||
namespace hdrplus
|
||||
{
|
||||
|
||||
void setParams(std::shared_ptr<LibRaw>& libraw_ptr, RawpyArgs rawpyArgs){
|
||||
libraw_ptr->imgdata.params.user_qual = rawpyArgs.demosaic_algorithm;
|
||||
libraw_ptr->imgdata.params.half_size = rawpyArgs.half_size;
|
||||
libraw_ptr->imgdata.params.use_camera_wb = rawpyArgs.use_camera_wb;
|
||||
libraw_ptr->imgdata.params.use_auto_wb = rawpyArgs.use_auto_wb;
|
||||
libraw_ptr->imgdata.params.no_auto_bright = rawpyArgs.no_auto_bright;
|
||||
libraw_ptr->imgdata.params.output_color = rawpyArgs.output_color;
|
||||
libraw_ptr->imgdata.params.gamm[0] = rawpyArgs.gamma[0];
|
||||
libraw_ptr->imgdata.params.gamm[1] = rawpyArgs.gamma[1];
|
||||
libraw_ptr->imgdata.params.output_bps = rawpyArgs.output_bps;
|
||||
}
|
||||
|
||||
cv::Mat postprocess(std::shared_ptr<LibRaw>& libraw_ptr, RawpyArgs rawpyArgs){
|
||||
std::cout<<"postprocessing..."<<std::endl;
|
||||
// set parameters
|
||||
setParams(libraw_ptr,rawpyArgs);
|
||||
|
||||
std::cout<<"conversion to 16 bit using black and white levels, demosaicking, white balance, color correction..."<<std::endl;
|
||||
|
||||
libraw_ptr->dcraw_process();
|
||||
int errorcode;
|
||||
|
||||
libraw_processed_image_t *ret_img = libraw_ptr->dcraw_make_mem_image(&errorcode);
|
||||
|
||||
int opencv_type = CV_16UC3; // 16bit RGB
|
||||
if(ret_img->colors==1){ // grayscale
|
||||
if(ret_img->bits == 8){ // uint8
|
||||
opencv_type = CV_8UC1;
|
||||
}else{ // uint16
|
||||
opencv_type = CV_16UC1;
|
||||
}
|
||||
}else{// RGB
|
||||
if(ret_img->bits == 8){ //8bit
|
||||
opencv_type = CV_8UC3;
|
||||
}else{ // 16bit
|
||||
opencv_type = CV_16UC3;
|
||||
}
|
||||
}
|
||||
|
||||
cv::Mat processedImg(ret_img->height,ret_img->width,opencv_type,ret_img->data);
|
||||
|
||||
std::cout<<"postprocess finished!"<<std::endl;
|
||||
return processedImg;
|
||||
}
|
||||
|
||||
}
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,36 @@
|
||||
|
||||
// MACHINE GENERATED -- DO NOT EDIT
|
||||
|
||||
extern "C" {
|
||||
struct halide_filter_metadata_t;
|
||||
void halide_register_argv_and_metadata(
|
||||
int (*filter_argv_call)(void **),
|
||||
const struct halide_filter_metadata_t *filter_metadata,
|
||||
const char * const *extra_key_value_pairs
|
||||
);
|
||||
}
|
||||
|
||||
extern "C" {
|
||||
extern int hdrplus_pipeline_argv(void **args);
|
||||
extern const struct halide_filter_metadata_t *hdrplus_pipeline_metadata();
|
||||
}
|
||||
|
||||
#ifdef HALIDE_REGISTER_EXTRA_KEY_VALUE_PAIRS_FUNC
|
||||
extern "C" const char * const *HALIDE_REGISTER_EXTRA_KEY_VALUE_PAIRS_FUNC();
|
||||
#endif // HALIDE_REGISTER_EXTRA_KEY_VALUE_PAIRS_FUNC
|
||||
|
||||
namespace halide_nsreg_hdrplus_pipeline {
|
||||
namespace {
|
||||
struct Registerer {
|
||||
Registerer() {
|
||||
#ifdef HALIDE_REGISTER_EXTRA_KEY_VALUE_PAIRS_FUNC
|
||||
halide_register_argv_and_metadata(::hdrplus_pipeline_argv, ::hdrplus_pipeline_metadata(), HALIDE_REGISTER_EXTRA_KEY_VALUE_PAIRS_FUNC());
|
||||
#else
|
||||
halide_register_argv_and_metadata(::hdrplus_pipeline_argv, ::hdrplus_pipeline_metadata(), nullptr);
|
||||
#endif // HALIDE_REGISTER_EXTRA_KEY_VALUE_PAIRS_FUNC
|
||||
}
|
||||
};
|
||||
static Registerer registerer;
|
||||
} // namespace
|
||||
} // halide_nsreg_hdrplus_pipeline
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,36 @@
|
||||
|
||||
// MACHINE GENERATED -- DO NOT EDIT
|
||||
|
||||
extern "C" {
|
||||
struct halide_filter_metadata_t;
|
||||
void halide_register_argv_and_metadata(
|
||||
int (*filter_argv_call)(void **),
|
||||
const struct halide_filter_metadata_t *filter_metadata,
|
||||
const char * const *extra_key_value_pairs
|
||||
);
|
||||
}
|
||||
|
||||
extern "C" {
|
||||
extern int hdrplus_pipeline_argv(void **args);
|
||||
extern const struct halide_filter_metadata_t *hdrplus_pipeline_metadata();
|
||||
}
|
||||
|
||||
#ifdef HALIDE_REGISTER_EXTRA_KEY_VALUE_PAIRS_FUNC
|
||||
extern "C" const char * const *HALIDE_REGISTER_EXTRA_KEY_VALUE_PAIRS_FUNC();
|
||||
#endif // HALIDE_REGISTER_EXTRA_KEY_VALUE_PAIRS_FUNC
|
||||
|
||||
namespace halide_nsreg_hdrplus_pipeline {
|
||||
namespace {
|
||||
struct Registerer {
|
||||
Registerer() {
|
||||
#ifdef HALIDE_REGISTER_EXTRA_KEY_VALUE_PAIRS_FUNC
|
||||
halide_register_argv_and_metadata(::hdrplus_pipeline_argv, ::hdrplus_pipeline_metadata(), HALIDE_REGISTER_EXTRA_KEY_VALUE_PAIRS_FUNC());
|
||||
#else
|
||||
halide_register_argv_and_metadata(::hdrplus_pipeline_argv, ::hdrplus_pipeline_metadata(), nullptr);
|
||||
#endif // HALIDE_REGISTER_EXTRA_KEY_VALUE_PAIRS_FUNC
|
||||
}
|
||||
};
|
||||
static Registerer registerer;
|
||||
} // namespace
|
||||
} // halide_nsreg_hdrplus_pipeline
|
||||
|
@ -0,0 +1,13 @@
|
||||
#ifndef __HDRPLUS__
|
||||
#define __HDRPLUS__
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include <opencv2/opencv.hpp> // all opencv header
|
||||
|
||||
int doHdrPlus(const std::string& dir_path, const std::string& out_name, const std::vector<std::string>& in_names);
|
||||
|
||||
bool doHdrPlus(const std::vector< std::vector<uint8_t> >& images, cv::Mat& mat);
|
||||
|
||||
#endif // __HDRPLUS__
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,39 @@
|
||||
#include "Burst.h"
|
||||
|
||||
Halide::Runtime::Buffer<uint16_t> Burst::ToBuffer() const {
|
||||
if (Raws.empty()) {
|
||||
return Halide::Runtime::Buffer<uint16_t>();
|
||||
}
|
||||
|
||||
Halide::Runtime::Buffer<uint16_t> result(GetWidth(), GetHeight(),
|
||||
Raws.size());
|
||||
for (int i = 0; i < Raws.size(); ++i) {
|
||||
auto resultSlice = result.sliced(2, i);
|
||||
Raws[i].CopyToBuffer(resultSlice);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void Burst::CopyToBuffer(Halide::Runtime::Buffer<uint16_t> &buffer) const {
|
||||
buffer.copy_from(ToBuffer());
|
||||
}
|
||||
|
||||
std::vector<RawImage> Burst::LoadRaws(const std::vector< std::vector<uint8_t> >& images) {
|
||||
std::vector<RawImage> result;
|
||||
for (const auto &img : images) {
|
||||
result.emplace_back(&img[0], img.size());
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
std::vector<RawImage> Burst::LoadRaws(const std::string &dirPath,
|
||||
std::vector<std::string> &inputs) {
|
||||
std::vector<RawImage> result;
|
||||
for (const auto &input : inputs) {
|
||||
const std::string img_path = dirPath + "/" + input;
|
||||
result.emplace_back(img_path);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
const RawImage &Burst::GetRaw(const size_t i) const { return this->Raws[i]; }
|
@ -0,0 +1,76 @@
|
||||
#pragma once
|
||||
|
||||
#include "InputSource.h"
|
||||
|
||||
#include <hdrplus_pipeline.h>
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
class Burst {
|
||||
public:
|
||||
Burst(std::string dir_path, std::vector<std::string> inputs)
|
||||
: Dir(std::move(dir_path)), Inputs(std::move(inputs)),
|
||||
Raws(LoadRaws(Dir, Inputs))
|
||||
|
||||
{
|
||||
}
|
||||
|
||||
Burst(const std::vector< std::vector<uint8_t> >& images)
|
||||
: Raws(LoadRaws(images))
|
||||
{
|
||||
}
|
||||
|
||||
~Burst() = default;
|
||||
|
||||
Burst(const Burst& src)
|
||||
{
|
||||
this->Dir = src.Dir;
|
||||
this->Inputs = src.Inputs;
|
||||
this->Raws = src.Raws;
|
||||
int aa = 0;
|
||||
}
|
||||
|
||||
int GetWidth() const { return Raws.empty() ? -1 : Raws[0].GetWidth(); }
|
||||
|
||||
int GetHeight() const { return Raws.empty() ? -1 : Raws[0].GetHeight(); }
|
||||
|
||||
int GetBlackLevel() const
|
||||
{
|
||||
return Raws.empty() ? -1 : Raws[0].GetScalarBlackLevel();
|
||||
}
|
||||
|
||||
int GetWhiteLevel() const {
|
||||
return Raws.empty() ? -1 : Raws[0].GetWhiteLevel();
|
||||
}
|
||||
|
||||
WhiteBalance GetWhiteBalance() const {
|
||||
return Raws.empty() ? WhiteBalance{-1, -1, -1, -1}
|
||||
: Raws[0].GetWhiteBalance();
|
||||
}
|
||||
|
||||
CfaPattern GetCfaPattern() const {
|
||||
return Raws.empty() ? CfaPattern::CFA_UNKNOWN : Raws[0].GetCfaPattern();
|
||||
}
|
||||
|
||||
Halide::Runtime::Buffer<float> GetColorCorrectionMatrix() const {
|
||||
return Raws.empty() ? Halide::Runtime::Buffer<float>()
|
||||
: Raws[0].GetColorCorrectionMatrix();
|
||||
}
|
||||
|
||||
Halide::Runtime::Buffer<uint16_t> ToBuffer() const;
|
||||
|
||||
void CopyToBuffer(Halide::Runtime::Buffer<uint16_t> &buffer) const;
|
||||
|
||||
const RawImage &GetRaw(const size_t i) const;
|
||||
|
||||
private:
|
||||
std::string Dir;
|
||||
std::vector<std::string> Inputs;
|
||||
std::vector<RawImage> Raws;
|
||||
|
||||
private:
|
||||
static std::vector<RawImage> LoadRaws(const std::string &dirPath,
|
||||
std::vector<std::string> &inputs);
|
||||
static std::vector<RawImage> LoadRaws(const std::vector< std::vector<uint8_t> >& images);
|
||||
};
|
@ -0,0 +1,147 @@
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <stdio.h>
|
||||
|
||||
#ifdef _DEBUG
|
||||
#define STB_IMAGE_WRITE_IMPLEMENTATION
|
||||
#include <include/stb_image_write.h>
|
||||
#endif
|
||||
|
||||
#include <hdrplus_pipeline.h>
|
||||
#include "Burst.h"
|
||||
|
||||
#include <include/HDRPlus.h>
|
||||
|
||||
extern "C" void halide_register_argv_and_metadata(
|
||||
int (*filter_argv_call)(void **),
|
||||
const struct halide_filter_metadata_t *filter_metadata,
|
||||
const char *const *extra_key_value_pairs) {
|
||||
}
|
||||
/*
|
||||
* HDRPlus Class -- Houses file I/O, defines pipeline attributes and calls
|
||||
* processes main stages of the pipeline.
|
||||
*/
|
||||
class HDRPlus {
|
||||
const Burst &burst;
|
||||
|
||||
public:
|
||||
const Compression c;
|
||||
const Gain g;
|
||||
|
||||
HDRPlus(Burst& burst, const Compression c, const Gain g)
|
||||
: burst(burst), c(c), g(g)
|
||||
{
|
||||
}
|
||||
|
||||
Halide::Runtime::Buffer<uint8_t> process() {
|
||||
const int width = burst.GetWidth();
|
||||
const int height = burst.GetHeight();
|
||||
|
||||
Halide::Runtime::Buffer<uint8_t> output_img(3, width, height);
|
||||
|
||||
#ifdef _DEBUG
|
||||
std::cerr << "Black point: " << burst.GetBlackLevel() << std::endl;
|
||||
std::cerr << "White point: " << burst.GetWhiteLevel() << std::endl;
|
||||
#endif
|
||||
|
||||
const WhiteBalance wb = burst.GetWhiteBalance();
|
||||
std::cerr << "RGGB: " << wb.r << " " << wb.g0 << " " << wb.g1 << " " << wb.b
|
||||
<< std::endl;
|
||||
|
||||
Halide::Runtime::Buffer<uint16_t> imgs = burst.ToBuffer();
|
||||
if (imgs.dimensions() != 3 || imgs.extent(2) < 2) {
|
||||
return output_img;
|
||||
#if 0
|
||||
throw std::invalid_argument(
|
||||
"The input of HDRPlus must be a 3-dimensional buffer with at least "
|
||||
"two channels.");
|
||||
#endif
|
||||
}
|
||||
|
||||
const int cfa_pattern = static_cast<int>(burst.GetCfaPattern());
|
||||
auto ccm = burst.GetColorCorrectionMatrix();
|
||||
hdrplus_pipeline(imgs, burst.GetBlackLevel(), burst.GetWhiteLevel(), wb.r,
|
||||
wb.g0, wb.g1, wb.b, cfa_pattern, ccm, c, g, output_img);
|
||||
|
||||
// transpose to account for interleaved layout
|
||||
output_img.transpose(0, 1);
|
||||
output_img.transpose(1, 2);
|
||||
|
||||
return output_img;
|
||||
}
|
||||
|
||||
#ifdef _DEBUG
|
||||
static bool save_png(const std::string &dir_path, const std::string &img_name,
|
||||
const Halide::Runtime::Buffer<uint8_t> &img) {
|
||||
const std::string img_path = dir_path + "/" + img_name;
|
||||
|
||||
const int stride_in_bytes = img.width() * img.channels();
|
||||
if (!stbi_write_png(img_path.c_str(), img.width(), img.height(),
|
||||
img.channels(), img.data(), stride_in_bytes)) {
|
||||
std::cerr << "Unable to write output image '" << img_name << "'"
|
||||
<< std::endl;
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
};
|
||||
|
||||
bool doHdrPlus(const std::vector< std::vector<uint8_t> >& images, cv::Mat& mat)
|
||||
{
|
||||
Compression c = 3.8f;
|
||||
Gain g = 1.1f;
|
||||
|
||||
Burst burst(images);
|
||||
|
||||
HDRPlus hdr_plus(burst, c, g);
|
||||
|
||||
Halide::Runtime::Buffer<uint8_t> outputHdr = hdr_plus.process();
|
||||
|
||||
#ifdef _DEBUG
|
||||
HDRPlus::save_png("/sdcard/com.xypower.mpapp/tmp", "2.png", outputHdr);
|
||||
#endif
|
||||
int width = outputHdr.width();
|
||||
int height = outputHdr.height();
|
||||
int channels = outputHdr.channels();
|
||||
int jch = 0;
|
||||
mat = cv::Mat::zeros(height, width, CV_8UC3);
|
||||
for (int i = 0; i < height; ++i)
|
||||
{
|
||||
jch = 0;
|
||||
for (int j = 0; j < width; ++j)
|
||||
{
|
||||
for (int n = 0; n < channels; ++n)
|
||||
{
|
||||
mat.at<uchar>(i, jch + n) = (uchar)outputHdr(j, i, n);
|
||||
}
|
||||
jch += channels;
|
||||
}
|
||||
}
|
||||
|
||||
// if (!HDRPlus::save_png(dir_path, out_name, output)) {
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
#if 0
|
||||
int doHdrPlus(const std::string& dir_path, const std::string& out_name, const std::vector<std::string>& in_names) {
|
||||
|
||||
|
||||
Compression c = 3.8f;
|
||||
Gain g = 1.1f;
|
||||
|
||||
Burst burst(dir_path, in_names);
|
||||
|
||||
HDRPlus hdr_plus(burst, c, g);
|
||||
|
||||
Halide::Runtime::Buffer<uint8_t> output = hdr_plus.process();
|
||||
|
||||
if (!HDRPlus::save_png(dir_path, out_name, output)) {
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
@ -0,0 +1,154 @@
|
||||
#include "InputSource.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <unordered_map>
|
||||
|
||||
#include "LibRaw2DngConverter.h"
|
||||
|
||||
RawImage::RawImage(const std::string &path)
|
||||
: Path(path), RawProcessor(std::make_shared<LibRaw>()) {
|
||||
// TODO: Check LibRaw parametres.
|
||||
// RawProcessor->imgdata.params.X = Y;
|
||||
|
||||
std::cerr << "Opening " << path << std::endl;
|
||||
if (int err = RawProcessor->open_file(path.c_str())) {
|
||||
std::cerr << "Cannot open file " << path
|
||||
<< " error: " << libraw_strerror(err) << std::endl;
|
||||
#if 0
|
||||
throw std::runtime_error("Error opening " + path);
|
||||
#endif
|
||||
}
|
||||
if (int err = RawProcessor->unpack()) {
|
||||
std::cerr << "Cannot unpack file " << path
|
||||
<< " error: " << libraw_strerror(err) << std::endl;
|
||||
#if 0
|
||||
throw std::runtime_error("Error opening " + path);
|
||||
#endif
|
||||
}
|
||||
if (int ret = RawProcessor->raw2image()) {
|
||||
std::cerr << "Cannot do raw2image on " << path
|
||||
<< " error: " << libraw_strerror(ret) << std::endl;
|
||||
#if 0
|
||||
throw std::runtime_error("Error opening " + path);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
RawImage::RawImage(const uint8_t* data, size_t length)
|
||||
: RawProcessor(std::make_shared<LibRaw>())
|
||||
{
|
||||
std::cerr << "Opening raw from memory" << std::endl;
|
||||
if (int err = RawProcessor->open_buffer((void *)data, length)) {
|
||||
std::cerr << "Cannot open raw from memory" << " error: " << libraw_strerror(err) << std::endl;
|
||||
#if 0
|
||||
throw std::runtime_error("Error opening raw");
|
||||
#endif
|
||||
}
|
||||
if (int err = RawProcessor->unpack()) {
|
||||
std::cerr << "Cannot unpack raw from memory " << " error: " << libraw_strerror(err) << std::endl;
|
||||
#if 0
|
||||
throw std::runtime_error("Error opening " + path);
|
||||
#endif
|
||||
}
|
||||
if (int ret = RawProcessor->raw2image()) {
|
||||
std::cerr << "Cannot do raw2image" << " error: " << libraw_strerror(ret) << std::endl;
|
||||
#if 0
|
||||
throw std::runtime_error("Error opening " + path);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
WhiteBalance RawImage::GetWhiteBalance() const {
|
||||
const auto coeffs = RawProcessor->imgdata.color.cam_mul;
|
||||
// Scale multipliers to green channel
|
||||
const float r = coeffs[0] / coeffs[1];
|
||||
const float g0 = 1.f; // same as coeffs[1] / coeffs[1];
|
||||
const float g1 = 1.f;
|
||||
const float b = coeffs[2] / coeffs[1];
|
||||
return WhiteBalance{r, g0, g1, b};
|
||||
}
|
||||
|
||||
void RawImage::CopyToBuffer(Halide::Runtime::Buffer<uint16_t> &buffer) const {
|
||||
const auto image_data = (uint16_t *)RawProcessor->imgdata.rawdata.raw_image;
|
||||
const auto raw_width = RawProcessor->imgdata.rawdata.sizes.raw_width;
|
||||
const auto raw_height = RawProcessor->imgdata.rawdata.sizes.raw_height;
|
||||
const auto top = RawProcessor->imgdata.rawdata.sizes.top_margin;
|
||||
const auto left = RawProcessor->imgdata.rawdata.sizes.left_margin;
|
||||
Halide::Runtime::Buffer<uint16_t> raw_buffer(image_data, raw_width,
|
||||
raw_height);
|
||||
buffer.copy_from(raw_buffer.translated({-left, -top}));
|
||||
}
|
||||
|
||||
void RawImage::WriteDng(const std::string &output_path,
|
||||
const Halide::Runtime::Buffer<uint16_t> &buffer) const {
|
||||
LibRaw2DngConverter converter(*this);
|
||||
converter.SetBuffer(buffer);
|
||||
converter.Write(output_path);
|
||||
}
|
||||
|
||||
std::array<float, 4> RawImage::GetBlackLevel() const {
|
||||
// See https://www.libraw.org/node/2471
|
||||
const auto raw_color = RawProcessor->imgdata.color;
|
||||
const auto base_black_level = static_cast<float>(raw_color.black);
|
||||
|
||||
std::array<float, 4> black_level = {
|
||||
base_black_level + static_cast<float>(raw_color.cblack[0]),
|
||||
base_black_level + static_cast<float>(raw_color.cblack[1]),
|
||||
base_black_level + static_cast<float>(raw_color.cblack[2]),
|
||||
base_black_level + static_cast<float>(raw_color.cblack[3])};
|
||||
|
||||
if (raw_color.cblack[4] == 2 && raw_color.cblack[5] == 2) {
|
||||
for (int x = 0; x < raw_color.cblack[4]; ++x) {
|
||||
for (int y = 0; y < raw_color.cblack[5]; ++y) {
|
||||
const auto index = y * 2 + x;
|
||||
black_level[index] = raw_color.cblack[6 + index];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return black_level;
|
||||
}
|
||||
|
||||
int RawImage::GetScalarBlackLevel() const {
|
||||
const auto black_level = GetBlackLevel();
|
||||
return static_cast<int>(
|
||||
*std::min_element(black_level.begin(), black_level.end()));
|
||||
}
|
||||
|
||||
std::string RawImage::GetCfaPatternString() const {
|
||||
static const std::unordered_map<char, char> CDESC_TO_CFA = {
|
||||
{'R', 0}, {'G', 1}, {'B', 2}, {'r', 0}, {'g', 1}, {'b', 2}};
|
||||
const auto &cdesc = RawProcessor->imgdata.idata.cdesc;
|
||||
return {CDESC_TO_CFA.at(cdesc[RawProcessor->COLOR(0, 0)]),
|
||||
CDESC_TO_CFA.at(cdesc[RawProcessor->COLOR(0, 1)]),
|
||||
CDESC_TO_CFA.at(cdesc[RawProcessor->COLOR(1, 0)]),
|
||||
CDESC_TO_CFA.at(cdesc[RawProcessor->COLOR(1, 1)])};
|
||||
}
|
||||
|
||||
CfaPattern RawImage::GetCfaPattern() const {
|
||||
const auto cfa_pattern = GetCfaPatternString();
|
||||
if (cfa_pattern == std::string{0, 1, 1, 2}) {
|
||||
return CfaPattern::CFA_RGGB;
|
||||
} else if (cfa_pattern == std::string{1, 0, 2, 1}) {
|
||||
return CfaPattern::CFA_GRBG;
|
||||
} else if (cfa_pattern == std::string{2, 1, 1, 0}) {
|
||||
return CfaPattern::CFA_BGGR;
|
||||
} else if (cfa_pattern == std::string{1, 2, 0, 1}) {
|
||||
return CfaPattern::CFA_GBRG;
|
||||
}
|
||||
#if 0
|
||||
throw std::invalid_argument("Unsupported CFA pattern: " + cfa_pattern);
|
||||
#endif
|
||||
return CfaPattern::CFA_UNKNOWN;
|
||||
}
|
||||
|
||||
Halide::Runtime::Buffer<float> RawImage::GetColorCorrectionMatrix() const {
|
||||
const auto raw_color = RawProcessor->imgdata.color;
|
||||
Halide::Runtime::Buffer<float> ccm(3, 3);
|
||||
for (int i = 0; i < 3; ++i) {
|
||||
for (int j = 0; j < 3; ++j) {
|
||||
ccm(i, j) = raw_color.rgb_cam[j][i];
|
||||
}
|
||||
}
|
||||
return ccm;
|
||||
}
|
@ -0,0 +1,47 @@
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <string>
|
||||
|
||||
#include <libraw/libraw.h>
|
||||
|
||||
#include "finish.h"
|
||||
#include <HalideBuffer.h>
|
||||
|
||||
class RawImage {
|
||||
public:
|
||||
explicit RawImage(const std::string &path);
|
||||
explicit RawImage(const uint8_t* data, size_t length);
|
||||
|
||||
~RawImage() = default;
|
||||
|
||||
int GetWidth() const { return RawProcessor->imgdata.rawdata.sizes.width; }
|
||||
|
||||
int GetHeight() const { return RawProcessor->imgdata.rawdata.sizes.height; }
|
||||
|
||||
int GetScalarBlackLevel() const;
|
||||
|
||||
std::array<float, 4> GetBlackLevel() const;
|
||||
|
||||
int GetWhiteLevel() const { return RawProcessor->imgdata.color.maximum; }
|
||||
|
||||
WhiteBalance GetWhiteBalance() const;
|
||||
|
||||
std::string GetCfaPatternString() const;
|
||||
CfaPattern GetCfaPattern() const;
|
||||
|
||||
Halide::Runtime::Buffer<float> GetColorCorrectionMatrix() const;
|
||||
|
||||
void CopyToBuffer(Halide::Runtime::Buffer<uint16_t> &buffer) const;
|
||||
|
||||
// Writes current RawImage as DNG. If buffer was provided, then use it instead
|
||||
// of internal buffer.
|
||||
void WriteDng(const std::string &path,
|
||||
const Halide::Runtime::Buffer<uint16_t> &buffer = {}) const;
|
||||
|
||||
std::shared_ptr<LibRaw> GetRawProcessor() const { return RawProcessor; }
|
||||
|
||||
private:
|
||||
std::string Path;
|
||||
std::shared_ptr<LibRaw> RawProcessor;
|
||||
};
|
@ -0,0 +1,95 @@
|
||||
#include "LibRaw2DngConverter.h"
|
||||
|
||||
#include <unordered_map>
|
||||
|
||||
#include <libraw/libraw.h>
|
||||
|
||||
#include "InputSource.h"
|
||||
|
||||
LibRaw2DngConverter::LibRaw2DngConverter(const RawImage &raw)
|
||||
: OutputStream(), Raw(raw),
|
||||
Tiff(SetTiffFields(
|
||||
TiffPtr(TIFFStreamOpen("", &OutputStream), TIFFClose))) {}
|
||||
|
||||
LibRaw2DngConverter::TiffPtr
|
||||
LibRaw2DngConverter::SetTiffFields(LibRaw2DngConverter::TiffPtr tiff_ptr) {
|
||||
const auto RawProcessor = Raw.GetRawProcessor();
|
||||
const auto raw_color = RawProcessor->imgdata.color;
|
||||
|
||||
const uint16_t bayer_pattern_dimensions[] = {2, 2};
|
||||
|
||||
const auto tiff = tiff_ptr.get();
|
||||
TIFFSetField(tiff, TIFFTAG_DNGVERSION, "\01\04\00\00");
|
||||
TIFFSetField(tiff, TIFFTAG_DNGBACKWARDVERSION, "\01\04\00\00");
|
||||
TIFFSetField(tiff, TIFFTAG_SUBFILETYPE, 0);
|
||||
TIFFSetField(tiff, TIFFTAG_COMPRESSION, COMPRESSION_NONE);
|
||||
TIFFSetField(tiff, TIFFTAG_BITSPERSAMPLE, 16);
|
||||
TIFFSetField(tiff, TIFFTAG_ROWSPERSTRIP, 1);
|
||||
TIFFSetField(tiff, TIFFTAG_ORIENTATION, ORIENTATION_TOPLEFT);
|
||||
TIFFSetField(tiff, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_CFA);
|
||||
TIFFSetField(tiff, TIFFTAG_SAMPLESPERPIXEL, 1);
|
||||
TIFFSetField(tiff, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG);
|
||||
TIFFSetField(tiff, TIFFTAG_SAMPLEFORMAT, SAMPLEFORMAT_UINT);
|
||||
TIFFSetField(tiff, TIFFTAG_CFAREPEATPATTERNDIM, &bayer_pattern_dimensions);
|
||||
|
||||
const std::string cfa = Raw.GetCfaPatternString();
|
||||
TIFFSetField(tiff, TIFFTAG_CFAPATTERN, cfa.c_str());
|
||||
|
||||
TIFFSetField(tiff, TIFFTAG_MAKE, "hdr-plus");
|
||||
TIFFSetField(tiff, TIFFTAG_UNIQUECAMERAMODEL, "hdr-plus");
|
||||
|
||||
const std::array<float, 9> color_matrix = {
|
||||
raw_color.cam_xyz[0][0], raw_color.cam_xyz[0][1], raw_color.cam_xyz[0][2],
|
||||
raw_color.cam_xyz[1][0], raw_color.cam_xyz[1][1], raw_color.cam_xyz[1][2],
|
||||
raw_color.cam_xyz[2][0], raw_color.cam_xyz[2][1], raw_color.cam_xyz[2][2],
|
||||
};
|
||||
TIFFSetField(tiff, TIFFTAG_COLORMATRIX1, 9, &color_matrix);
|
||||
TIFFSetField(tiff, TIFFTAG_CALIBRATIONILLUMINANT1, 21); // D65
|
||||
|
||||
const std::array<float, 3> as_shot_neutral = {
|
||||
1.f / (raw_color.cam_mul[0] / raw_color.cam_mul[1]), 1.f,
|
||||
1.f / (raw_color.cam_mul[2] / raw_color.cam_mul[1])};
|
||||
TIFFSetField(tiff, TIFFTAG_ASSHOTNEUTRAL, 3, &as_shot_neutral);
|
||||
|
||||
TIFFSetField(tiff, TIFFTAG_CFALAYOUT, 1); // Rectangular (or square) layout
|
||||
TIFFSetField(
|
||||
tiff, TIFFTAG_CFAPLANECOLOR, 3,
|
||||
"\00\01\02"); // RGB
|
||||
// https://www.awaresystems.be/imaging/tiff/tifftags/cfaplanecolor.html
|
||||
|
||||
const std::array<float, 4> black_level = Raw.GetBlackLevel();
|
||||
TIFFSetField(tiff, TIFFTAG_BLACKLEVEL, 4, &black_level);
|
||||
|
||||
static const uint32_t white_level = raw_color.maximum;
|
||||
TIFFSetField(tiff, TIFFTAG_WHITELEVEL, 1, &white_level);
|
||||
|
||||
if (RawProcessor->imgdata.sizes.flip > 0) {
|
||||
// Seems that LibRaw uses LibTIFF notation.
|
||||
TIFFSetField(tiff, TIFFTAG_ORIENTATION, RawProcessor->imgdata.sizes.flip);
|
||||
} else {
|
||||
TIFFSetField(tiff, TIFFTAG_ORIENTATION, ORIENTATION_TOPLEFT);
|
||||
}
|
||||
return tiff_ptr;
|
||||
}
|
||||
|
||||
void LibRaw2DngConverter::SetBuffer(
|
||||
const Halide::Runtime::Buffer<uint16_t> &buffer) const {
|
||||
const auto width = buffer.width();
|
||||
const auto height = buffer.height();
|
||||
const auto tiff = Tiff.get();
|
||||
TIFFSetField(tiff, TIFFTAG_IMAGEWIDTH, width);
|
||||
TIFFSetField(tiff, TIFFTAG_IMAGELENGTH, height);
|
||||
|
||||
uint16_t *row_pointer = buffer.data();
|
||||
for (int row = 0; row < height; row++) {
|
||||
TIFFWriteScanline(tiff, row_pointer, row, 0);
|
||||
row_pointer += width;
|
||||
}
|
||||
}
|
||||
|
||||
void LibRaw2DngConverter::Write(const std::string &path) const {
|
||||
TIFFCheckpointDirectory(Tiff.get());
|
||||
TIFFFlush(Tiff.get());
|
||||
std::ofstream output(path, std::ofstream::binary);
|
||||
output << OutputStream.str();
|
||||
}
|
@ -0,0 +1,26 @@
|
||||
#pragma once
|
||||
|
||||
#include <sstream>
|
||||
#include <tiffio.h>
|
||||
#include <tiffio.hxx>
|
||||
|
||||
#include <HalideBuffer.h>
|
||||
|
||||
class RawImage;
|
||||
|
||||
class LibRaw2DngConverter {
|
||||
using TiffPtr = std::shared_ptr<TIFF>;
|
||||
TiffPtr SetTiffFields(TiffPtr tiff_ptr);
|
||||
|
||||
public:
|
||||
explicit LibRaw2DngConverter(const RawImage &raw);
|
||||
|
||||
void SetBuffer(const Halide::Runtime::Buffer<uint16_t> &buffer) const;
|
||||
|
||||
void Write(const std::string &path) const;
|
||||
|
||||
private:
|
||||
std::ostringstream OutputStream;
|
||||
const RawImage &Raw;
|
||||
std::shared_ptr<TIFF> Tiff;
|
||||
};
|
@ -0,0 +1,36 @@
|
||||
#ifndef HDRPLUS_FINISH_H_
|
||||
#define HDRPLUS_FINISH_H_
|
||||
|
||||
#include <hdrplus_pipeline.h>
|
||||
|
||||
template <class T = float> struct TypedWhiteBalance {
|
||||
template <class TT>
|
||||
explicit TypedWhiteBalance(const TypedWhiteBalance<TT> &other)
|
||||
: r(other.r), g0(other.g0), g1(other.g1), b(other.b) {}
|
||||
|
||||
TypedWhiteBalance(T r, T g0, T g1, T b) : r(r), g0(g0), g1(g1), b(b) {}
|
||||
|
||||
T r;
|
||||
T g0;
|
||||
T g1;
|
||||
T b;
|
||||
};
|
||||
|
||||
using WhiteBalance = TypedWhiteBalance<float>;
|
||||
|
||||
typedef uint16_t BlackPoint;
|
||||
typedef uint16_t WhitePoint;
|
||||
|
||||
typedef float Compression;
|
||||
typedef float Gain;
|
||||
|
||||
enum class CfaPattern : int {
|
||||
CFA_UNKNOWN = 0,
|
||||
CFA_RGGB = 1,
|
||||
CFA_GRBG = 2,
|
||||
CFA_BGGR = 3,
|
||||
CFA_GBRG = 4
|
||||
};
|
||||
|
||||
|
||||
#endif
|
@ -0,0 +1,60 @@
|
||||
// Copyright 2014 The Android Open Source Project
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
cc_library_shared {
|
||||
name: "libimg_utils",
|
||||
|
||||
srcs: [
|
||||
"src/EndianUtils.cpp",
|
||||
"src/FileInput.cpp",
|
||||
"src/FileOutput.cpp",
|
||||
"src/SortedEntryVector.cpp",
|
||||
"src/Input.cpp",
|
||||
"src/Output.cpp",
|
||||
"src/Orderable.cpp",
|
||||
"src/TiffIfd.cpp",
|
||||
"src/TiffWritable.cpp",
|
||||
"src/TiffWriter.cpp",
|
||||
"src/TiffEntry.cpp",
|
||||
"src/TiffEntryImpl.cpp",
|
||||
"src/ByteArrayOutput.cpp",
|
||||
"src/DngUtils.cpp",
|
||||
"src/StripSource.cpp",
|
||||
],
|
||||
|
||||
shared_libs: [
|
||||
"liblog",
|
||||
"libutils",
|
||||
"libcutils",
|
||||
],
|
||||
|
||||
cflags: [
|
||||
"-Wall",
|
||||
"-Wextra",
|
||||
"-Werror",
|
||||
"-fvisibility=hidden",
|
||||
],
|
||||
|
||||
product_variables: {
|
||||
debuggable: {
|
||||
// Enable assert() in eng builds
|
||||
cflags: [
|
||||
"-UNDEBUG",
|
||||
"-DLOG_NDEBUG=1",
|
||||
],
|
||||
},
|
||||
},
|
||||
|
||||
export_include_dirs: ["include"],
|
||||
}
|
@ -0,0 +1,83 @@
|
||||
/*
|
||||
* Copyright 2014 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef IMG_UTILS_BYTE_ARRAY_OUTPUT_H
|
||||
#define IMG_UTILS_BYTE_ARRAY_OUTPUT_H
|
||||
|
||||
#include <img_utils/Output.h>
|
||||
|
||||
#include <utils/Errors.h>
|
||||
// #include <utils/Vector.h>
|
||||
|
||||
#include <cutils/compiler.h>
|
||||
#include <stdint.h>
|
||||
#include <vector>
|
||||
|
||||
namespace android {
|
||||
namespace img_utils {
|
||||
|
||||
/**
|
||||
* Utility class that accumulates written bytes into a buffer.
|
||||
*/
|
||||
class ANDROID_API ByteArrayOutput : public Output {
|
||||
public:
|
||||
|
||||
ByteArrayOutput();
|
||||
|
||||
virtual ~ByteArrayOutput();
|
||||
|
||||
/**
|
||||
* Open this ByteArrayOutput.
|
||||
*
|
||||
* Returns OK on success, or a negative error code.
|
||||
*/
|
||||
virtual status_t open();
|
||||
|
||||
/**
|
||||
* Write bytes from the given buffer. The number of bytes given in the count
|
||||
* argument will be written. Bytes will be written from the given buffer starting
|
||||
* at the index given in the offset argument.
|
||||
*
|
||||
* Returns OK on success, or a negative error code.
|
||||
*/
|
||||
virtual status_t write(const uint8_t* buf, size_t offset, size_t count);
|
||||
|
||||
/**
|
||||
* Close this ByteArrayOutput.
|
||||
*
|
||||
* Returns OK on success, or a negative error code.
|
||||
*/
|
||||
virtual status_t close();
|
||||
|
||||
/**
|
||||
* Get current size of the array of bytes written.
|
||||
*/
|
||||
virtual size_t getSize() const;
|
||||
|
||||
/**
|
||||
* Get pointer to array of bytes written. It is not valid to use this pointer if
|
||||
* open, write, or close is called after this method.
|
||||
*/
|
||||
virtual const uint8_t* getArray() const;
|
||||
|
||||
protected:
|
||||
std::vector<uint8_t> mByteArray;
|
||||
};
|
||||
|
||||
} /*namespace img_utils*/
|
||||
} /*namespace android*/
|
||||
|
||||
#endif /*IMG_UTILS_BYTE_ARRAY_OUTPUT_H*/
|
@ -0,0 +1,232 @@
|
||||
/*
|
||||
* Copyright 2014 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef IMG_UTILS_DNG_UTILS_H
|
||||
#define IMG_UTILS_DNG_UTILS_H
|
||||
|
||||
#include <img_utils/ByteArrayOutput.h>
|
||||
#include <img_utils/EndianUtils.h>
|
||||
|
||||
#include <utils/Errors.h>
|
||||
#include <utils/Log.h>
|
||||
#include <utils/RefBase.h>
|
||||
|
||||
#include <cutils/compiler.h>
|
||||
#include <stdint.h>
|
||||
|
||||
namespace android {
|
||||
namespace img_utils {
|
||||
|
||||
#define NELEMS(x) ((int) (sizeof(x) / sizeof((x)[0])))
|
||||
#define CLAMP(x, low, high) (((x) > (high)) ? (high) : (((x) < (low)) ? (low) : (x)))
|
||||
|
||||
/**
|
||||
* Utility class for building values for the OpcodeList tags specified
|
||||
* in the Adobe DNG 1.4 spec.
|
||||
*/
|
||||
class ANDROID_API OpcodeListBuilder : public LightRefBase<OpcodeListBuilder> {
|
||||
public:
|
||||
// Note that the Adobe DNG 1.4 spec for Bayer phase (defined for the
|
||||
// FixBadPixelsConstant and FixBadPixelsList opcodes) is incorrect. It's
|
||||
// inconsistent with the DNG SDK (cf. dng_negative::SetBayerMosaic and
|
||||
// dng_opcode_FixBadPixelsList::IsGreen), and Adobe confirms that the
|
||||
// spec should be updated to match the SDK.
|
||||
enum CfaLayout {
|
||||
CFA_GRBG = 0,
|
||||
CFA_RGGB,
|
||||
CFA_BGGR,
|
||||
CFA_GBRG,
|
||||
CFA_NONE,
|
||||
};
|
||||
|
||||
OpcodeListBuilder();
|
||||
virtual ~OpcodeListBuilder();
|
||||
|
||||
/**
|
||||
* Get the total size of this opcode list in bytes.
|
||||
*/
|
||||
virtual size_t getSize() const;
|
||||
|
||||
/**
|
||||
* Get the number of opcodes defined in this list.
|
||||
*/
|
||||
virtual uint32_t getCount() const;
|
||||
|
||||
/**
|
||||
* Write the opcode list into the given buffer. This buffer
|
||||
* must be able to hold at least as many elements as returned
|
||||
* by calling the getSize() method.
|
||||
*
|
||||
* Returns OK on success, or a negative error code.
|
||||
*/
|
||||
virtual status_t buildOpList(/*out*/ uint8_t* buf) const;
|
||||
|
||||
/**
|
||||
* Add GainMap opcode(s) for the given metadata parameters. The given
|
||||
* CFA layout must match the layout of the shading map passed into the
|
||||
* lensShadingMap parameter.
|
||||
*
|
||||
* Returns OK on success, or a negative error code.
|
||||
*/
|
||||
virtual status_t addGainMapsForMetadata(uint32_t lsmWidth,
|
||||
uint32_t lsmHeight,
|
||||
uint32_t activeAreaTop,
|
||||
uint32_t activeAreaLeft,
|
||||
uint32_t activeAreaBottom,
|
||||
uint32_t activeAreaRight,
|
||||
CfaLayout cfa,
|
||||
const float* lensShadingMap);
|
||||
|
||||
/**
|
||||
* Add a GainMap opcode with the given fields. The mapGains array
|
||||
* must have mapPointsV * mapPointsH * mapPlanes elements.
|
||||
*
|
||||
* Returns OK on success, or a negative error code.
|
||||
*/
|
||||
virtual status_t addGainMap(uint32_t top,
|
||||
uint32_t left,
|
||||
uint32_t bottom,
|
||||
uint32_t right,
|
||||
uint32_t plane,
|
||||
uint32_t planes,
|
||||
uint32_t rowPitch,
|
||||
uint32_t colPitch,
|
||||
uint32_t mapPointsV,
|
||||
uint32_t mapPointsH,
|
||||
double mapSpacingV,
|
||||
double mapSpacingH,
|
||||
double mapOriginV,
|
||||
double mapOriginH,
|
||||
uint32_t mapPlanes,
|
||||
const float* mapGains);
|
||||
|
||||
/**
|
||||
* Add WarpRectilinear opcode for the given metadata parameters.
|
||||
*
|
||||
* Returns OK on success, or a negative error code.
|
||||
*/
|
||||
virtual status_t addWarpRectilinearForMetadata(const float* kCoeffs,
|
||||
uint32_t activeArrayWidth,
|
||||
uint32_t activeArrayHeight,
|
||||
float opticalCenterX,
|
||||
float opticalCenterY);
|
||||
|
||||
/**
|
||||
* Add a WarpRectilinear opcode.
|
||||
*
|
||||
* numPlanes - Number of planes included in this opcode.
|
||||
* opticalCenterX, opticalCenterY - Normalized x,y coordinates of the sensor optical
|
||||
* center relative to the top,left pixel of the produced images (e.g. [0.5, 0.5]
|
||||
* gives a sensor optical center in the image center.
|
||||
* kCoeffs - A list of coefficients for the polynomial equation representing the distortion
|
||||
* correction. For each plane, 6 coefficients must be included:
|
||||
* {k_r0, k_r1, k_r2, k_r3, k_t0, k_t1}. See the DNG 1.4 specification for an
|
||||
* outline of the polynomial used here.
|
||||
*
|
||||
* Returns OK on success, or a negative error code.
|
||||
*/
|
||||
virtual status_t addWarpRectilinear(uint32_t numPlanes,
|
||||
double opticalCenterX,
|
||||
double opticalCenterY,
|
||||
const double* kCoeffs);
|
||||
|
||||
|
||||
/**
|
||||
* Add FixBadPixelsList opcode for the given metadata parameters.
|
||||
*
|
||||
* Returns OK on success, or a negative error code.
|
||||
*/
|
||||
virtual status_t addBadPixelListForMetadata(const uint32_t* hotPixels,
|
||||
uint32_t xyPairCount,
|
||||
uint32_t colorFilterArrangement);
|
||||
|
||||
/**
|
||||
* Add FixBadPixelsList opcode.
|
||||
*
|
||||
* bayerPhase - 0=top-left of image is red, 1=top-left of image is green pixel in red row,
|
||||
* 2=top-left of image is green pixel in blue row, 3=top-left of image is
|
||||
* blue.
|
||||
* badPointCount - number of (x,y) pairs of bad pixels are given in badPointRowColPairs.
|
||||
* badRectCount - number of (top, left, bottom, right) tuples are given in
|
||||
* badRectTopLeftBottomRightTuples
|
||||
*
|
||||
* Returns OK on success, or a negative error code.
|
||||
*/
|
||||
virtual status_t addBadPixelList(uint32_t bayerPhase,
|
||||
uint32_t badPointCount,
|
||||
uint32_t badRectCount,
|
||||
const uint32_t* badPointRowColPairs,
|
||||
const uint32_t* badRectTopLeftBottomRightTuples);
|
||||
|
||||
// TODO: Add other Opcode methods
|
||||
protected:
|
||||
static const uint32_t FLAG_OPTIONAL = 0x1u;
|
||||
static const uint32_t FLAG_OPTIONAL_FOR_PREVIEW = 0x2u;
|
||||
|
||||
// Opcode IDs
|
||||
enum {
|
||||
WARP_RECTILINEAR_ID = 1,
|
||||
FIX_BAD_PIXELS_LIST = 5,
|
||||
GAIN_MAP_ID = 9,
|
||||
};
|
||||
|
||||
// LSM mosaic indices
|
||||
enum {
|
||||
LSM_R_IND = 0,
|
||||
LSM_GE_IND = 1,
|
||||
LSM_GO_IND = 2,
|
||||
LSM_B_IND = 3,
|
||||
};
|
||||
|
||||
uint32_t mCount;
|
||||
ByteArrayOutput mOpList;
|
||||
EndianOutput mEndianOut;
|
||||
|
||||
status_t addOpcodePreamble(uint32_t opcodeId);
|
||||
|
||||
private:
|
||||
/**
|
||||
* Add Bayer GainMap opcode(s) for the given metadata parameters.
|
||||
* CFA layout must match the layout of the shading map passed into the
|
||||
* lensShadingMap parameter.
|
||||
*
|
||||
* Returns OK on success, or a negative error code.
|
||||
*/
|
||||
status_t addBayerGainMapsForMetadata(uint32_t lsmWidth,
|
||||
uint32_t lsmHeight,
|
||||
uint32_t activeAreaWidth,
|
||||
uint32_t activeAreaHeight,
|
||||
CfaLayout cfa,
|
||||
const float* lensShadingMap);
|
||||
|
||||
/**
|
||||
* Add Bayer GainMap opcode(s) for the given metadata parameters.
|
||||
* CFA layout must match the layout of the shading map passed into the
|
||||
* lensShadingMap parameter.
|
||||
*
|
||||
* Returns OK on success, or a negative error code.
|
||||
*/
|
||||
status_t addMonochromeGainMapsForMetadata(uint32_t lsmWidth,
|
||||
uint32_t lsmHeight,
|
||||
uint32_t activeAreaWidth,
|
||||
uint32_t activeAreaHeight,
|
||||
const float* lensShadingMap);
|
||||
};
|
||||
|
||||
} /*namespace img_utils*/
|
||||
} /*namespace android*/
|
||||
|
||||
#endif /*IMG_UTILS_DNG_UTILS_H*/
|
@ -0,0 +1,250 @@
|
||||
/*
|
||||
* Copyright 2014 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef IMG_UTILS_ENDIAN_UTILS
|
||||
#define IMG_UTILS_ENDIAN_UTILS
|
||||
|
||||
#include <img_utils/Output.h>
|
||||
|
||||
#include <cutils/compiler.h>
|
||||
#include <utils/Errors.h>
|
||||
#include <stdint.h>
|
||||
#include <endian.h>
|
||||
#include <assert.h>
|
||||
|
||||
namespace android {
|
||||
namespace img_utils {
|
||||
|
||||
/**
|
||||
* Endianness types supported.
|
||||
*/
|
||||
enum ANDROID_API Endianness {
|
||||
UNDEFINED_ENDIAN, // Default endianness will be used.
|
||||
BIG,
|
||||
LITTLE
|
||||
};
|
||||
|
||||
/**
|
||||
* Convert from the native device endianness to big endian.
|
||||
*/
|
||||
template<typename T>
|
||||
T convertToBigEndian(T in);
|
||||
|
||||
/**
|
||||
* Convert from the native device endianness to little endian.
|
||||
*/
|
||||
template<typename T>
|
||||
T convertToLittleEndian(T in);
|
||||
|
||||
/**
|
||||
* A utility class for writing to an Output with the given endianness.
|
||||
*/
|
||||
class ANDROID_API EndianOutput : public Output {
|
||||
public:
|
||||
/**
|
||||
* Wrap the given Output. Calling write methods will result in
|
||||
* writes to this output.
|
||||
*/
|
||||
explicit EndianOutput(Output* out, Endianness end=LITTLE);
|
||||
|
||||
virtual ~EndianOutput();
|
||||
|
||||
/**
|
||||
* Call open on the wrapped output.
|
||||
*/
|
||||
virtual status_t open();
|
||||
|
||||
/**
|
||||
* Call close on the wrapped output.
|
||||
*/
|
||||
virtual status_t close();
|
||||
|
||||
/**
|
||||
* Set the endianness to use when writing.
|
||||
*/
|
||||
virtual void setEndianness(Endianness end);
|
||||
|
||||
/**
|
||||
* Get the currently configured endianness.
|
||||
*/
|
||||
virtual Endianness getEndianness() const;
|
||||
|
||||
/**
|
||||
* Get the current number of bytes written by this EndianOutput.
|
||||
*/
|
||||
virtual uint32_t getCurrentOffset() const;
|
||||
|
||||
|
||||
// TODO: switch write methods to uint32_t instead of size_t,
|
||||
// the max size of a TIFF files is bounded
|
||||
|
||||
/**
|
||||
* The following methods will write elements from given input buffer to the output.
|
||||
* Count elements in the buffer will be written with the endianness set for this
|
||||
* EndianOutput. If the given offset is greater than zero, that many elements will
|
||||
* be skipped in the buffer before writing.
|
||||
*
|
||||
* Returns OK on success, or a negative error code.
|
||||
*/
|
||||
virtual status_t write(const uint8_t* buf, size_t offset, size_t count);
|
||||
|
||||
virtual status_t write(const int8_t* buf, size_t offset, size_t count);
|
||||
|
||||
virtual status_t write(const uint16_t* buf, size_t offset, size_t count);
|
||||
|
||||
virtual status_t write(const int16_t* buf, size_t offset, size_t count);
|
||||
|
||||
virtual status_t write(const uint32_t* buf, size_t offset, size_t count);
|
||||
|
||||
virtual status_t write(const int32_t* buf, size_t offset, size_t count);
|
||||
|
||||
virtual status_t write(const uint64_t* buf, size_t offset, size_t count);
|
||||
|
||||
virtual status_t write(const int64_t* buf, size_t offset, size_t count);
|
||||
|
||||
virtual status_t write(const float* buf, size_t offset, size_t count);
|
||||
|
||||
virtual status_t write(const double* buf, size_t offset, size_t count);
|
||||
|
||||
protected:
|
||||
template<typename T>
|
||||
inline status_t writeHelper(const T* buf, size_t offset, size_t count);
|
||||
|
||||
uint32_t mOffset;
|
||||
Output* mOutput;
|
||||
Endianness mEndian;
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
inline status_t EndianOutput::writeHelper(const T* buf, size_t offset, size_t count) {
|
||||
assert(offset <= count);
|
||||
status_t res = OK;
|
||||
size_t size = sizeof(T);
|
||||
switch(mEndian) {
|
||||
case BIG: {
|
||||
for (size_t i = offset; i < count; ++i) {
|
||||
T tmp = convertToBigEndian<T>(buf[offset + i]);
|
||||
if ((res = mOutput->write(reinterpret_cast<uint8_t*>(&tmp), 0, size))
|
||||
!= OK) {
|
||||
return res;
|
||||
}
|
||||
mOffset += size;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case LITTLE: {
|
||||
for (size_t i = offset; i < count; ++i) {
|
||||
T tmp = convertToLittleEndian<T>(buf[offset + i]);
|
||||
if ((res = mOutput->write(reinterpret_cast<uint8_t*>(&tmp), 0, size))
|
||||
!= OK) {
|
||||
return res;
|
||||
}
|
||||
mOffset += size;
|
||||
}
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
return BAD_VALUE;
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
template<>
|
||||
inline uint8_t convertToBigEndian(uint8_t in) {
|
||||
return in;
|
||||
}
|
||||
|
||||
template<>
|
||||
inline int8_t convertToBigEndian(int8_t in) {
|
||||
return in;
|
||||
}
|
||||
|
||||
template<>
|
||||
inline uint16_t convertToBigEndian(uint16_t in) {
|
||||
return htobe16(in);
|
||||
}
|
||||
|
||||
template<>
|
||||
inline int16_t convertToBigEndian(int16_t in) {
|
||||
return htobe16(in);
|
||||
}
|
||||
|
||||
template<>
|
||||
inline uint32_t convertToBigEndian(uint32_t in) {
|
||||
return htobe32(in);
|
||||
}
|
||||
|
||||
template<>
|
||||
inline int32_t convertToBigEndian(int32_t in) {
|
||||
return htobe32(in);
|
||||
}
|
||||
|
||||
template<>
|
||||
inline uint64_t convertToBigEndian(uint64_t in) {
|
||||
return htobe64(in);
|
||||
}
|
||||
|
||||
template<>
|
||||
inline int64_t convertToBigEndian(int64_t in) {
|
||||
return htobe64(in);
|
||||
}
|
||||
|
||||
template<>
|
||||
inline uint8_t convertToLittleEndian(uint8_t in) {
|
||||
return in;
|
||||
}
|
||||
|
||||
template<>
|
||||
inline int8_t convertToLittleEndian(int8_t in) {
|
||||
return in;
|
||||
}
|
||||
|
||||
template<>
|
||||
inline uint16_t convertToLittleEndian(uint16_t in) {
|
||||
return htole16(in);
|
||||
}
|
||||
|
||||
template<>
|
||||
inline int16_t convertToLittleEndian(int16_t in) {
|
||||
return htole16(in);
|
||||
}
|
||||
|
||||
template<>
|
||||
inline uint32_t convertToLittleEndian(uint32_t in) {
|
||||
return htole32(in);
|
||||
}
|
||||
|
||||
template<>
|
||||
inline int32_t convertToLittleEndian(int32_t in) {
|
||||
return htole32(in);
|
||||
}
|
||||
|
||||
template<>
|
||||
inline uint64_t convertToLittleEndian(uint64_t in) {
|
||||
return htole64(in);
|
||||
}
|
||||
|
||||
template<>
|
||||
inline int64_t convertToLittleEndian(int64_t in) {
|
||||
return htole64(in);
|
||||
}
|
||||
|
||||
} /*namespace img_utils*/
|
||||
} /*namespace android*/
|
||||
|
||||
#endif /*IMG_UTILS_ENDIAN_UTILS*/
|
||||
|
@ -0,0 +1,76 @@
|
||||
/*
|
||||
* Copyright 2014 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef IMG_UTILS_FILE_INPUT_H
|
||||
#define IMG_UTILS_FILE_INPUT_H
|
||||
|
||||
#include <img_utils/Input.h>
|
||||
|
||||
#include <cutils/compiler.h>
|
||||
#include <utils/Errors.h>
|
||||
#include <utils/String8.h>
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
|
||||
namespace android {
|
||||
namespace img_utils {
|
||||
|
||||
/**
|
||||
* Utility class for reading from a file.
|
||||
*/
|
||||
class ANDROID_API FileInput : public Input {
|
||||
public:
|
||||
/**
|
||||
* Create a file input for the given path.
|
||||
*/
|
||||
explicit FileInput(String8 path);
|
||||
|
||||
virtual ~FileInput();
|
||||
|
||||
/**
|
||||
* Open a file descriptor to the path given in the constructor.
|
||||
*
|
||||
* Returns OK on success, or a negative error code.
|
||||
*/
|
||||
virtual status_t open();
|
||||
|
||||
/**
|
||||
* Read bytes from the file into the given buffer. At most, the number
|
||||
* of bytes given in the count argument will be read. Bytes will be written
|
||||
* into the given buffer starting at the index given in the offset argument.
|
||||
*
|
||||
* Returns the number of bytes read, or NOT_ENOUGH_DATA if at the end of the file. If an
|
||||
* error has occurred, this will return a negative error code other than NOT_ENOUGH_DATA.
|
||||
*/
|
||||
virtual ssize_t read(uint8_t* buf, size_t offset, size_t count);
|
||||
|
||||
/**
|
||||
* Close the file descriptor to the path given in the constructor.
|
||||
*
|
||||
* Returns OK on success, or a negative error code.
|
||||
*/
|
||||
virtual status_t close();
|
||||
private:
|
||||
FILE *mFp;
|
||||
String8 mPath;
|
||||
bool mOpen;
|
||||
};
|
||||
|
||||
} /*namespace img_utils*/
|
||||
} /*namespace android*/
|
||||
|
||||
|
||||
#endif /*IMG_UTILS_INPUT_H*/
|
@ -0,0 +1,46 @@
|
||||
/*
|
||||
* Copyright 2014 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef IMG_UTILS_FILE_OUTPUT_H
|
||||
#define IMG_UTILS_FILE_OUTPUT_H
|
||||
|
||||
#include <img_utils/Output.h>
|
||||
#include <cutils/compiler.h>
|
||||
#include <utils/Errors.h>
|
||||
#include <utils/String8.h>
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
|
||||
namespace android {
|
||||
namespace img_utils {
|
||||
|
||||
class ANDROID_API FileOutput : public Output {
|
||||
public:
|
||||
explicit FileOutput(String8 path);
|
||||
virtual ~FileOutput();
|
||||
virtual status_t open();
|
||||
virtual status_t write(const uint8_t* buf, size_t offset, size_t count);
|
||||
virtual status_t close();
|
||||
private:
|
||||
FILE *mFp;
|
||||
String8 mPath;
|
||||
bool mOpen;
|
||||
};
|
||||
|
||||
} /*namespace img_utils*/
|
||||
} /*namespace android*/
|
||||
|
||||
#endif /*IMG_UTILS_FILE_OUTPUT_H*/
|
@ -0,0 +1,71 @@
|
||||
/*
|
||||
* Copyright 2014 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef IMG_UTILS_INPUT_H
|
||||
#define IMG_UTILS_INPUT_H
|
||||
|
||||
#include <cutils/compiler.h>
|
||||
#include <utils/Errors.h>
|
||||
#include <stdint.h>
|
||||
|
||||
namespace android {
|
||||
namespace img_utils {
|
||||
|
||||
/**
|
||||
* Utility class used as a source of bytes.
|
||||
*/
|
||||
class ANDROID_API Input {
|
||||
public:
|
||||
virtual ~Input();
|
||||
|
||||
/**
|
||||
* Open this Input.
|
||||
*
|
||||
* Returns OK on success, or a negative error code.
|
||||
*/
|
||||
virtual status_t open();
|
||||
|
||||
/**
|
||||
* Read bytes into the given buffer. At most, the number of bytes given in the
|
||||
* count argument will be read. Bytes will be written into the given buffer starting
|
||||
* at the index given in the offset argument.
|
||||
*
|
||||
* Returns the number of bytes read, or NOT_ENOUGH_DATA if at the end of the file. If an
|
||||
* error has occurred, this will return a negative error code other than NOT_ENOUGH_DATA.
|
||||
*/
|
||||
virtual ssize_t read(uint8_t* buf, size_t offset, size_t count) = 0;
|
||||
|
||||
/**
|
||||
* Skips bytes in the input.
|
||||
*
|
||||
* Returns the number of bytes skipped, or NOT_ENOUGH_DATA if at the end of the file. If an
|
||||
* error has occurred, this will return a negative error code other than NOT_ENOUGH_DATA.
|
||||
*/
|
||||
virtual ssize_t skip(size_t count);
|
||||
|
||||
/**
|
||||
* Close the Input. It is not valid to call open on a previously closed Input.
|
||||
*
|
||||
* Returns OK on success, or a negative error code.
|
||||
*/
|
||||
virtual status_t close();
|
||||
};
|
||||
|
||||
} /*namespace img_utils*/
|
||||
} /*namespace android*/
|
||||
|
||||
|
||||
#endif /*IMG_UTILS_INPUT_H*/
|
@ -0,0 +1,57 @@
|
||||
/*
|
||||
* Copyright 2014 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef IMG_UTILS_ORDERABLE
|
||||
#define IMG_UTILS_ORDERABLE
|
||||
|
||||
#include <cutils/compiler.h>
|
||||
#include <stdint.h>
|
||||
|
||||
namespace android {
|
||||
namespace img_utils {
|
||||
|
||||
#define COMPARE_DEF(op) \
|
||||
inline bool operator op (const Orderable& orderable) const;
|
||||
|
||||
/**
|
||||
* Subclasses of Orderable can be compared and sorted. This is
|
||||
* intended to be used to create sorted arrays of TIFF entries
|
||||
* and IFDs.
|
||||
*/
|
||||
class ANDROID_API Orderable {
|
||||
public:
|
||||
virtual ~Orderable();
|
||||
|
||||
/**
|
||||
* Comparison operatotors are based on the value returned
|
||||
* from this method.
|
||||
*/
|
||||
virtual uint32_t getComparableValue() const = 0;
|
||||
|
||||
COMPARE_DEF(>)
|
||||
COMPARE_DEF(<)
|
||||
COMPARE_DEF(>=)
|
||||
COMPARE_DEF(<=)
|
||||
COMPARE_DEF(==)
|
||||
COMPARE_DEF(!=)
|
||||
};
|
||||
|
||||
#undef COMPARE_DEF
|
||||
|
||||
} /*namespace img_utils*/
|
||||
} /*namespace android*/
|
||||
|
||||
#endif /*IMG_UTILS_ORDERABLE*/
|
@ -0,0 +1,61 @@
|
||||
/*
|
||||
* Copyright 2014 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef IMG_UTILS_OUTPUT_H
|
||||
#define IMG_UTILS_OUTPUT_H
|
||||
|
||||
#include <cutils/compiler.h>
|
||||
#include <utils/Errors.h>
|
||||
#include <stdint.h>
|
||||
|
||||
namespace android {
|
||||
namespace img_utils {
|
||||
|
||||
/**
|
||||
* Utility class used to output bytes.
|
||||
*/
|
||||
class ANDROID_API Output {
|
||||
public:
|
||||
virtual ~Output();
|
||||
|
||||
/**
|
||||
* Open this Output.
|
||||
*
|
||||
* Returns OK on success, or a negative error code.
|
||||
*/
|
||||
virtual status_t open();
|
||||
|
||||
/**
|
||||
* Write bytes from the given buffer. The number of bytes given in the count
|
||||
* argument will be written. Bytes will be written from the given buffer starting
|
||||
* at the index given in the offset argument.
|
||||
*
|
||||
* Returns OK on success, or a negative error code.
|
||||
*/
|
||||
virtual status_t write(const uint8_t* buf, size_t offset, size_t count) = 0;
|
||||
|
||||
/**
|
||||
* Close this Output. It is not valid to call open on a previously closed Output.
|
||||
*
|
||||
* Returns OK on success, or a negative error code.
|
||||
*/
|
||||
virtual status_t close();
|
||||
};
|
||||
|
||||
} /*namespace img_utils*/
|
||||
} /*namespace android*/
|
||||
|
||||
#endif /*IMG_UTILS_OUTPUT_H*/
|
@ -0,0 +1,44 @@
|
||||
/*
|
||||
* Copyright 2014 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef IMG_UTILS_PAIR_H
|
||||
#define IMG_UTILS_PAIR_H
|
||||
|
||||
#include <cutils/compiler.h>
|
||||
|
||||
namespace android {
|
||||
namespace img_utils {
|
||||
|
||||
/**
|
||||
* Generic pair utility class. Nothing special here.
|
||||
*/
|
||||
template<typename F, typename S>
|
||||
class ANDROID_API Pair {
|
||||
public:
|
||||
F first;
|
||||
S second;
|
||||
|
||||
Pair() {}
|
||||
|
||||
Pair(const Pair& o) : first(o.first), second(o.second) {}
|
||||
|
||||
Pair(const F& f, const S& s) : first(f), second(s) {}
|
||||
};
|
||||
|
||||
} /*namespace img_utils*/
|
||||
} /*namespace android*/
|
||||
|
||||
#endif /*IMG_UTILS_PAIR_H*/
|
@ -0,0 +1,53 @@
|
||||
/*
|
||||
* Copyright 2014 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef IMG_UTILS_SORTED_ENTRY_VECTOR_H
|
||||
#define IMG_UTILS_SORTED_ENTRY_VECTOR_H
|
||||
|
||||
#include <img_utils/TiffEntry.h>
|
||||
|
||||
#include <utils/StrongPointer.h>
|
||||
#include <utils/SortedVector.h>
|
||||
|
||||
namespace android {
|
||||
namespace img_utils {
|
||||
|
||||
/**
|
||||
* Subclass of SortedVector that has been extended to
|
||||
* do comparisons/lookups based on the tag ID of the entries.
|
||||
*/
|
||||
class SortedEntryVector : public SortedVector<sp<TiffEntry> > {
|
||||
public:
|
||||
virtual ~SortedEntryVector();
|
||||
|
||||
/**
|
||||
* Returns the index of the entry with the given tag ID, or
|
||||
* -1 if none exists.
|
||||
*/
|
||||
ssize_t indexOfTag(uint16_t tag) const;
|
||||
|
||||
protected:
|
||||
/**
|
||||
* Compare tag ID.
|
||||
*/
|
||||
virtual int do_compare(const void* lhs, const void* rhs) const;
|
||||
};
|
||||
|
||||
|
||||
} /*namespace img_utils*/
|
||||
} /*namespace android*/
|
||||
|
||||
#endif /*IMG_UTILS_SORTED_ENTRY_VECTOR_H*/
|
@ -0,0 +1,53 @@
|
||||
/*
|
||||
* Copyright 2014 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef IMG_UTILS_STRIP_SOURCE_H
|
||||
#define IMG_UTILS_STRIP_SOURCE_H
|
||||
|
||||
#include <img_utils/Output.h>
|
||||
|
||||
#include <cutils/compiler.h>
|
||||
#include <utils/Errors.h>
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
namespace android {
|
||||
namespace img_utils {
|
||||
|
||||
/**
|
||||
* This class acts as a data source for strips set in a TiffIfd.
|
||||
*/
|
||||
class ANDROID_API StripSource {
|
||||
public:
|
||||
virtual ~StripSource();
|
||||
|
||||
/**
|
||||
* Write count bytes to the stream.
|
||||
*
|
||||
* Returns OK on success, or a negative error code.
|
||||
*/
|
||||
virtual status_t writeToStream(Output& stream, uint32_t count) = 0;
|
||||
|
||||
/**
|
||||
* Return the source IFD.
|
||||
*/
|
||||
virtual uint32_t getIfd() const = 0;
|
||||
};
|
||||
|
||||
} /*namespace img_utils*/
|
||||
} /*namespace android*/
|
||||
|
||||
#endif /*IMG_UTILS_STRIP_SOURCE_H*/
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,130 @@
|
||||
/*
|
||||
* Copyright 2014 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef IMG_UTILS_TIFF_ENTRY
|
||||
#define IMG_UTILS_TIFF_ENTRY
|
||||
|
||||
#include <img_utils/TiffWritable.h>
|
||||
#include <img_utils/TiffHelpers.h>
|
||||
#include <img_utils/EndianUtils.h>
|
||||
|
||||
#include <cutils/compiler.h>
|
||||
// #include <utils/String8.h>
|
||||
#include <utils/Errors.h>
|
||||
#include <stdint.h>
|
||||
|
||||
namespace android {
|
||||
namespace img_utils {
|
||||
|
||||
#define COMPARE_DEF(op) \
|
||||
inline bool operator op (const TiffEntry& entry) const;
|
||||
|
||||
/**
|
||||
* This class holds a single TIFF IFD entry.
|
||||
*
|
||||
* Subclasses are expected to support assignment and copying operations.
|
||||
*/
|
||||
class ANDROID_API TiffEntry : public TiffWritable {
|
||||
public:
|
||||
virtual ~TiffEntry();
|
||||
|
||||
/**
|
||||
* Write the 12-byte IFD entry to the output. The given offset will be
|
||||
* set as the tag value if the size of the tag value exceeds the max
|
||||
* size for the TIFF Value field (4 bytes), and should be word aligned.
|
||||
*
|
||||
* Returns OK on success, or a negative error code on failure.
|
||||
*/
|
||||
virtual status_t writeTagInfo(uint32_t offset, /*out*/EndianOutput* out) const = 0;
|
||||
|
||||
/**
|
||||
* Get the count set for this entry. This corresponds to the TIFF Count
|
||||
* field.
|
||||
*/
|
||||
virtual uint32_t getCount() const = 0;
|
||||
|
||||
/**
|
||||
* Get the tag id set for this entry. This corresponds to the TIFF Tag
|
||||
* field.
|
||||
*/
|
||||
virtual uint16_t getTag() const = 0;
|
||||
|
||||
/**
|
||||
* Get the type set for this entry. This corresponds to the TIFF Type
|
||||
* field.
|
||||
*/
|
||||
virtual TagType getType() const = 0;
|
||||
|
||||
/**
|
||||
* Get the defined endianness for this entry. If this is defined,
|
||||
* the tag value will be written with the given byte order.
|
||||
*/
|
||||
virtual Endianness getEndianness() const = 0;
|
||||
|
||||
/**
|
||||
* Get the value for this entry. This corresponds to the TIFF Value
|
||||
* field.
|
||||
*
|
||||
* Returns NULL if the value is NULL, or if the type used does not
|
||||
* match the type of this tag.
|
||||
*/
|
||||
template<typename T>
|
||||
const T* getData() const;
|
||||
|
||||
virtual std::string toString() const;
|
||||
|
||||
/**
|
||||
* Force the type used here to be a valid TIFF type.
|
||||
*
|
||||
* Returns NULL if the given value is NULL, or if the type given does
|
||||
* not match the type of the value given.
|
||||
*/
|
||||
template<typename T>
|
||||
static const T* forceValidType(TagType type, const T* value);
|
||||
|
||||
virtual const void* getDataHelper() const = 0;
|
||||
|
||||
COMPARE_DEF(>)
|
||||
COMPARE_DEF(<)
|
||||
|
||||
protected:
|
||||
enum {
|
||||
MAX_PRINT_STRING_LENGTH = 256
|
||||
};
|
||||
};
|
||||
|
||||
#define COMPARE(op) \
|
||||
bool TiffEntry::operator op (const TiffEntry& entry) const { \
|
||||
return getComparableValue() op entry.getComparableValue(); \
|
||||
}
|
||||
|
||||
COMPARE(>)
|
||||
COMPARE(<)
|
||||
|
||||
|
||||
template<typename T>
|
||||
const T* TiffEntry::getData() const {
|
||||
const T* value = reinterpret_cast<const T*>(getDataHelper());
|
||||
return forceValidType<T>(getType(), value);
|
||||
}
|
||||
|
||||
#undef COMPARE
|
||||
#undef COMPARE_DEF
|
||||
|
||||
} /*namespace img_utils*/
|
||||
} /*namespace android*/
|
||||
|
||||
#endif /*IMG_UTILS_TIFF_ENTRY*/
|
@ -0,0 +1,219 @@
|
||||
/*
|
||||
* Copyright 2014 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef IMG_UTILS_TIFF_ENTRY_IMPL
|
||||
#define IMG_UTILS_TIFF_ENTRY_IMPL
|
||||
|
||||
#include <img_utils/TiffIfd.h>
|
||||
#include <img_utils/TiffEntry.h>
|
||||
#include <img_utils/TiffHelpers.h>
|
||||
#include <img_utils/Output.h>
|
||||
#include <img_utils/EndianUtils.h>
|
||||
|
||||
#include <utils/Log.h>
|
||||
#include <utils/Errors.h>
|
||||
// #include <utils/Vector.h>
|
||||
#include <utils/StrongPointer.h>
|
||||
#include <stdint.h>
|
||||
#include <vector>
|
||||
|
||||
namespace android {
|
||||
namespace img_utils {
|
||||
|
||||
template<typename T>
|
||||
class TiffEntryImpl : public TiffEntry {
|
||||
public:
|
||||
TiffEntryImpl(uint16_t tag, TagType type, uint32_t count, Endianness end, const T* data);
|
||||
virtual ~TiffEntryImpl();
|
||||
|
||||
status_t writeData(uint32_t offset, /*out*/EndianOutput* out) const;
|
||||
status_t writeTagInfo(uint32_t offset, /*out*/EndianOutput* out) const;
|
||||
|
||||
uint32_t getCount() const;
|
||||
uint16_t getTag() const;
|
||||
TagType getType() const;
|
||||
Endianness getEndianness() const;
|
||||
size_t getSize() const;
|
||||
uint32_t getComparableValue() const;
|
||||
|
||||
protected:
|
||||
const void* getDataHelper() const;
|
||||
uint32_t getActualSize() const;
|
||||
|
||||
uint16_t mTag;
|
||||
uint16_t mType;
|
||||
uint32_t mCount;
|
||||
Endianness mEnd;
|
||||
std::vector<T> mData;
|
||||
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
TiffEntryImpl<T>::TiffEntryImpl(uint16_t tag, TagType type, uint32_t count, Endianness end,
|
||||
const T* data)
|
||||
: mTag(tag), mType(static_cast<uint16_t>(type)), mCount(count), mEnd(end) {
|
||||
count = (type == RATIONAL || type == SRATIONAL) ? count * 2 : count;
|
||||
auto it = mData.insert(mData.end(), data, data + count);
|
||||
// LOG_ALWAYS_FATAL_IF(index < 0, "%s: Could not allocate vector for data.", __FUNCTION__);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
TiffEntryImpl<T>::~TiffEntryImpl() {}
|
||||
|
||||
template<typename T>
|
||||
uint32_t TiffEntryImpl<T>::getCount() const {
|
||||
return mCount;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
uint16_t TiffEntryImpl<T>::getTag() const {
|
||||
return mTag;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
TagType TiffEntryImpl<T>::getType() const {
|
||||
return static_cast<TagType>(mType);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
const void* TiffEntryImpl<T>::getDataHelper() const {
|
||||
return reinterpret_cast<const void*>(&mData[0]);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
size_t TiffEntryImpl<T>::getSize() const {
|
||||
uint32_t total = getActualSize();
|
||||
WORD_ALIGN(total)
|
||||
return (total <= OFFSET_SIZE) ? 0 : total;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
uint32_t TiffEntryImpl<T>::getActualSize() const {
|
||||
uint32_t total = sizeof(T) * mCount;
|
||||
if (getType() == RATIONAL || getType() == SRATIONAL) {
|
||||
// 2 ints stored for each rational, multiply by 2
|
||||
total <<= 1;
|
||||
}
|
||||
return total;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
Endianness TiffEntryImpl<T>::getEndianness() const {
|
||||
return mEnd;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
uint32_t TiffEntryImpl<T>::getComparableValue() const {
|
||||
return mTag;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
status_t TiffEntryImpl<T>::writeTagInfo(uint32_t offset, /*out*/EndianOutput* out) const {
|
||||
assert((offset % TIFF_WORD_SIZE) == 0);
|
||||
status_t ret = OK;
|
||||
BAIL_ON_FAIL(out->write(&mTag, 0, 1), ret);
|
||||
BAIL_ON_FAIL(out->write(&mType, 0, 1), ret);
|
||||
BAIL_ON_FAIL(out->write(&mCount, 0, 1), ret);
|
||||
|
||||
uint32_t dataSize = getActualSize();
|
||||
if (dataSize > OFFSET_SIZE) {
|
||||
BAIL_ON_FAIL(out->write(&offset, 0, 1), ret);
|
||||
} else {
|
||||
uint32_t count = mCount;
|
||||
if (getType() == RATIONAL || getType() == SRATIONAL) {
|
||||
/**
|
||||
* Rationals are stored as an array of ints. Each
|
||||
* rational is represented by 2 ints. To recover the
|
||||
* size of the array here, multiply the count by 2.
|
||||
*/
|
||||
count <<= 1;
|
||||
}
|
||||
BAIL_ON_FAIL(out->write(&mData[0], 0, count), ret);
|
||||
ZERO_TILL_WORD(out, dataSize, ret);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
status_t TiffEntryImpl<T>::writeData(uint32_t /*offset*/, EndianOutput* out) const {
|
||||
status_t ret = OK;
|
||||
|
||||
// Some tags have fixed-endian value output
|
||||
Endianness tmp = UNDEFINED_ENDIAN;
|
||||
if (mEnd != UNDEFINED_ENDIAN) {
|
||||
tmp = out->getEndianness();
|
||||
out->setEndianness(mEnd);
|
||||
}
|
||||
|
||||
uint32_t count = mCount;
|
||||
if (getType() == RATIONAL || getType() == SRATIONAL) {
|
||||
/**
|
||||
* Rationals are stored as an array of ints. Each
|
||||
* rational is represented by 2 ints. To recover the
|
||||
* size of the array here, multiply the count by 2.
|
||||
*/
|
||||
count <<= 1;
|
||||
}
|
||||
|
||||
BAIL_ON_FAIL(out->write(&mData[0], 0, count), ret);
|
||||
|
||||
if (mEnd != UNDEFINED_ENDIAN) {
|
||||
out->setEndianness(tmp);
|
||||
}
|
||||
|
||||
// Write to next word alignment
|
||||
ZERO_TILL_WORD(out, sizeof(T) * count, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
template<>
|
||||
inline status_t TiffEntryImpl<sp<TiffIfd> >::writeTagInfo(uint32_t offset,
|
||||
/*out*/EndianOutput* out) const {
|
||||
assert((offset % TIFF_WORD_SIZE) == 0);
|
||||
status_t ret = OK;
|
||||
BAIL_ON_FAIL(out->write(&mTag, 0, 1), ret);
|
||||
BAIL_ON_FAIL(out->write(&mType, 0, 1), ret);
|
||||
BAIL_ON_FAIL(out->write(&mCount, 0, 1), ret);
|
||||
|
||||
BAIL_ON_FAIL(out->write(&offset, 0, 1), ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
template<>
|
||||
inline uint32_t TiffEntryImpl<sp<TiffIfd> >::getActualSize() const {
|
||||
uint32_t total = 0;
|
||||
for (size_t i = 0; i < mData.size(); ++i) {
|
||||
total += mData[i]->getSize();
|
||||
}
|
||||
return total;
|
||||
}
|
||||
|
||||
template<>
|
||||
inline status_t TiffEntryImpl<sp<TiffIfd> >::writeData(uint32_t offset, EndianOutput* out) const {
|
||||
status_t ret = OK;
|
||||
for (uint32_t i = 0; i < mCount; ++i) {
|
||||
BAIL_ON_FAIL(mData[i]->writeData(offset, out), ret);
|
||||
offset += mData[i]->getSize();
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
} /*namespace img_utils*/
|
||||
} /*namespace android*/
|
||||
|
||||
#endif /*IMG_UTILS_TIFF_ENTRY_IMPL*/
|
||||
|
||||
|
@ -0,0 +1,132 @@
|
||||
/*
|
||||
* Copyright 2014 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef IMG_UTILS_TIFF_HELPERS_H
|
||||
#define IMG_UTILS_TIFF_HELPERS_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
namespace android {
|
||||
namespace img_utils {
|
||||
|
||||
const uint8_t ZERO_WORD[] = {0, 0, 0, 0};
|
||||
|
||||
#define BAIL_ON_FAIL(x, flag) \
|
||||
if (((flag) = (x)) != OK) return flag;
|
||||
|
||||
#define BYTES_TILL_WORD(index) \
|
||||
((TIFF_WORD_SIZE - ((index) % TIFF_WORD_SIZE)) % TIFF_WORD_SIZE)
|
||||
|
||||
#define WORD_ALIGN(count) \
|
||||
count += BYTES_TILL_WORD(count);
|
||||
|
||||
#define ZERO_TILL_WORD(output, index, ret) \
|
||||
{ \
|
||||
size_t remaining = BYTES_TILL_WORD(index); \
|
||||
if (remaining > 0) { \
|
||||
BAIL_ON_FAIL((output)->write(ZERO_WORD, 0, remaining), ret); \
|
||||
} \
|
||||
}
|
||||
|
||||
/**
|
||||
* Basic TIFF header constants.
|
||||
*/
|
||||
enum {
|
||||
BAD_OFFSET = 0,
|
||||
TIFF_WORD_SIZE = 4, // Size in bytes
|
||||
IFD_HEADER_SIZE = 2, // Size in bytes
|
||||
IFD_FOOTER_SIZE = 4, // Size in bytes
|
||||
TIFF_ENTRY_SIZE = 12, // Size in bytes
|
||||
MAX_IFD_ENTRIES = UINT16_MAX,
|
||||
FILE_HEADER_SIZE = 8, // Size in bytes
|
||||
ENDIAN_MARKER_SIZE = 2, // Size in bytes
|
||||
TIFF_MARKER_SIZE = 2, // Size in bytes
|
||||
OFFSET_MARKER_SIZE = 4, // Size in bytes
|
||||
TIFF_FILE_MARKER = 42,
|
||||
BIG_ENDIAN_MARKER = 0x4D4Du,
|
||||
LITTLE_ENDIAN_MARKER = 0x4949u
|
||||
};
|
||||
|
||||
/**
|
||||
* Constants for the TIFF tag types.
|
||||
*/
|
||||
enum TagType {
|
||||
UNKNOWN_TAGTYPE = 0,
|
||||
BYTE=1,
|
||||
ASCII,
|
||||
SHORT,
|
||||
LONG,
|
||||
RATIONAL,
|
||||
SBYTE,
|
||||
UNDEFINED,
|
||||
SSHORT,
|
||||
SLONG,
|
||||
SRATIONAL,
|
||||
FLOAT,
|
||||
DOUBLE
|
||||
};
|
||||
|
||||
/**
|
||||
* Sizes of the TIFF entry fields (in bytes).
|
||||
*/
|
||||
enum {
|
||||
TAG_SIZE = 2,
|
||||
TYPE_SIZE = 2,
|
||||
COUNT_SIZE = 4,
|
||||
OFFSET_SIZE = 4
|
||||
};
|
||||
|
||||
/**
|
||||
* Convenience IFD id constants.
|
||||
*/
|
||||
enum {
|
||||
IFD_0 = 0,
|
||||
RAW_IFD,
|
||||
PROFILE_IFD,
|
||||
PREVIEW_IFD
|
||||
};
|
||||
|
||||
inline size_t getTypeSize(TagType type) {
|
||||
switch(type) {
|
||||
case UNDEFINED:
|
||||
case ASCII:
|
||||
case BYTE:
|
||||
case SBYTE:
|
||||
return 1;
|
||||
case SHORT:
|
||||
case SSHORT:
|
||||
return 2;
|
||||
case LONG:
|
||||
case SLONG:
|
||||
case FLOAT:
|
||||
return 4;
|
||||
case RATIONAL:
|
||||
case SRATIONAL:
|
||||
case DOUBLE:
|
||||
return 8;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
inline uint32_t calculateIfdSize(size_t numberOfEntries) {
|
||||
return IFD_HEADER_SIZE + IFD_FOOTER_SIZE + TIFF_ENTRY_SIZE * numberOfEntries;
|
||||
}
|
||||
|
||||
} /*namespace img_utils*/
|
||||
} /*namespace android*/
|
||||
|
||||
#endif /*IMG_UTILS_TIFF_HELPERS_H*/
|
@ -0,0 +1,164 @@
|
||||
/*
|
||||
* Copyright 2014 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef IMG_UTILS_TIFF_IFD_H
|
||||
#define IMG_UTILS_TIFF_IFD_H
|
||||
|
||||
#include <img_utils/TiffWritable.h>
|
||||
#include <img_utils/TiffEntry.h>
|
||||
#include <img_utils/Output.h>
|
||||
|
||||
#include <cutils/compiler.h>
|
||||
#include <utils/Errors.h>
|
||||
#include <utils/StrongPointer.h>
|
||||
#include <stdint.h>
|
||||
#include <map>
|
||||
|
||||
namespace android {
|
||||
namespace img_utils {
|
||||
|
||||
/**
|
||||
* This class holds a single TIFF Image File Directory (IFD) structure.
|
||||
*
|
||||
* This maps to the TIFF IFD structure that is logically composed of:
|
||||
* - A 2-byte field listing the number of entries.
|
||||
* - A list of 12-byte TIFF entries.
|
||||
* - A 4-byte offset to the next IFD.
|
||||
*/
|
||||
class ANDROID_API TiffIfd : public TiffWritable {
|
||||
public:
|
||||
explicit TiffIfd(uint32_t ifdId);
|
||||
virtual ~TiffIfd();
|
||||
|
||||
/**
|
||||
* Add a TiffEntry to this IFD or replace an existing entry with the
|
||||
* same tag ID. No validation is done.
|
||||
*
|
||||
* Returns OK on success, or a negative error code on failure.
|
||||
*/
|
||||
virtual status_t addEntry(const sp<TiffEntry>& entry);
|
||||
|
||||
/**
|
||||
* Set the pointer to the next IFD. This is used to create a linked
|
||||
* list of IFDs as defined by the TIFF 6.0 spec., and is not included
|
||||
* when calculating the size of IFD and entries for the getSize()
|
||||
* method (unlike SubIFDs).
|
||||
*/
|
||||
virtual void setNextIfd(const sp<TiffIfd>& ifd);
|
||||
|
||||
/**
|
||||
* Get the pointer to the next IFD, or NULL if none exists.
|
||||
*/
|
||||
virtual sp<TiffIfd> getNextIfd() const;
|
||||
|
||||
/**
|
||||
* Write the IFD data. This includes the IFD header, entries, footer,
|
||||
* and the corresponding values for each entry (recursively including
|
||||
* sub-IFDs). The written amount should end on a word boundary, and
|
||||
* the given offset should be word aligned.
|
||||
*
|
||||
* Returns OK on success, or a negative error code on failure.
|
||||
*/
|
||||
virtual status_t writeData(uint32_t offset, /*out*/EndianOutput* out) const;
|
||||
|
||||
/**
|
||||
* Get the size of the IFD. This includes the IFD header, entries, footer,
|
||||
* and the corresponding values for each entry (recursively including
|
||||
* any sub-IFDs).
|
||||
*/
|
||||
virtual size_t getSize() const;
|
||||
|
||||
/**
|
||||
* Get the id of this IFD.
|
||||
*/
|
||||
virtual uint32_t getId() const;
|
||||
|
||||
/**
|
||||
* Get an entry with the given tag ID.
|
||||
*
|
||||
* Returns a strong pointer to the entry if it exists, or an empty strong
|
||||
* pointer.
|
||||
*/
|
||||
virtual sp<TiffEntry> getEntry(uint16_t tag) const;
|
||||
|
||||
/**
|
||||
* Remove the entry with the given tag ID if it exists.
|
||||
*/
|
||||
virtual void removeEntry(uint16_t tag);
|
||||
|
||||
/**
|
||||
* Convenience method to validate and set strip-related image tags.
|
||||
*
|
||||
* This sets all strip related tags, but leaves offset values unitialized.
|
||||
* setStripOffsets must be called with the desired offset before writing.
|
||||
* The strip tag values are calculated from the existing tags for image
|
||||
* dimensions and pixel type set in the IFD.
|
||||
*
|
||||
* Does not handle planar image configurations (PlanarConfiguration != 1).
|
||||
*
|
||||
* Returns OK on success, or a negative error code.
|
||||
*/
|
||||
virtual status_t validateAndSetStripTags();
|
||||
|
||||
/**
|
||||
* Returns true if validateAndSetStripTags has been called, but not setStripOffsets.
|
||||
*/
|
||||
virtual bool uninitializedOffsets() const;
|
||||
|
||||
/**
|
||||
* Convenience method to set beginning offset for strips.
|
||||
*
|
||||
* Call this to update the strip offsets before calling writeData.
|
||||
*
|
||||
* Returns OK on success, or a negative error code.
|
||||
*/
|
||||
virtual status_t setStripOffset(uint32_t offset);
|
||||
|
||||
/**
|
||||
* Get the total size of the strips in bytes.
|
||||
*
|
||||
* This sums the byte count at each strip offset, and returns
|
||||
* the total count of bytes stored in strips for this IFD.
|
||||
*/
|
||||
virtual uint32_t getStripSize() const;
|
||||
|
||||
/**
|
||||
* Get a formatted string representing this IFD.
|
||||
*/
|
||||
virtual std::string toString() const;
|
||||
|
||||
/**
|
||||
* Print a formatted string representing this IFD to logcat.
|
||||
*/
|
||||
void log() const;
|
||||
|
||||
/**
|
||||
* Get value used to determine sort order.
|
||||
*/
|
||||
virtual uint32_t getComparableValue() const;
|
||||
|
||||
protected:
|
||||
virtual uint32_t checkAndGetOffset(uint32_t offset) const;
|
||||
std::map<uint16_t, sp<TiffEntry> > mEntries;
|
||||
sp<TiffIfd> mNextIfd;
|
||||
uint32_t mIfdId;
|
||||
bool mStripOffsetsInitialized;
|
||||
};
|
||||
|
||||
} /*namespace img_utils*/
|
||||
} /*namespace android*/
|
||||
|
||||
#endif /*IMG_UTILS_TIFF_IFD_H*/
|
@ -0,0 +1,60 @@
|
||||
/*
|
||||
* Copyright 2014 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef IMG_UTILS_TIFF_WRITABLE
|
||||
#define IMG_UTILS_TIFF_WRITABLE
|
||||
|
||||
#include <img_utils/Orderable.h>
|
||||
#include <img_utils/EndianUtils.h>
|
||||
#include <img_utils/Output.h>
|
||||
|
||||
#include <cutils/compiler.h>
|
||||
#include <utils/Errors.h>
|
||||
#include <utils/RefBase.h>
|
||||
#include <stdint.h>
|
||||
|
||||
namespace android {
|
||||
namespace img_utils {
|
||||
|
||||
/**
|
||||
* TiffWritable subclasses represent TIFF metadata objects that can be written
|
||||
* to an EndianOutput object. This is used for TIFF entries and IFDs.
|
||||
*/
|
||||
class ANDROID_API TiffWritable : public Orderable, public LightRefBase<TiffWritable> {
|
||||
public:
|
||||
TiffWritable();
|
||||
virtual ~TiffWritable();
|
||||
|
||||
/**
|
||||
* Write the data to the output. The given offset is used to calculate
|
||||
* the header offset for values written. The offset is defined
|
||||
* relative to the beginning of the TIFF header, and is word aligned.
|
||||
*
|
||||
* Returns OK on success, or a negative error code on failure.
|
||||
*/
|
||||
virtual status_t writeData(uint32_t offset, /*out*/EndianOutput* out) const = 0;
|
||||
|
||||
/**
|
||||
* Get the size of the data to write.
|
||||
*/
|
||||
virtual size_t getSize() const = 0;
|
||||
|
||||
};
|
||||
|
||||
} /*namespace img_utils*/
|
||||
} /*namespace android*/
|
||||
|
||||
#endif /*IMG_UTILS_TIFF_WRITABLE*/
|
@ -0,0 +1,328 @@
|
||||
/*
|
||||
* Copyright 2014 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef IMG_UTILS_TIFF_WRITER_H
|
||||
#define IMG_UTILS_TIFF_WRITER_H
|
||||
|
||||
#include <img_utils/EndianUtils.h>
|
||||
#include <img_utils/StripSource.h>
|
||||
#include <img_utils/TiffEntryImpl.h>
|
||||
#include <img_utils/TagDefinitions.h>
|
||||
#include <img_utils/TiffIfd.h>
|
||||
|
||||
#include <utils/Log.h>
|
||||
#include <utils/Errors.h>
|
||||
#include <utils/StrongPointer.h>
|
||||
|
||||
#include <cutils/compiler.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include <vector>
|
||||
#include <map>
|
||||
|
||||
namespace android {
|
||||
namespace img_utils {
|
||||
|
||||
class TiffEntry;
|
||||
class TiffIfd;
|
||||
class Output;
|
||||
|
||||
/**
|
||||
* This class holds a collection of TIFF IFDs that can be written as a
|
||||
* complete DNG file header.
|
||||
*
|
||||
* This maps to the TIFF header structure that is logically composed of:
|
||||
* - An 8-byte file header containing an endianness indicator, the TIFF
|
||||
* file marker, and the offset to the first IFD.
|
||||
* - A list of TIFF IFD structures.
|
||||
*/
|
||||
class ANDROID_API TiffWriter : public LightRefBase<TiffWriter> {
|
||||
public:
|
||||
enum SubIfdType {
|
||||
SUBIFD = 0,
|
||||
GPSINFO
|
||||
};
|
||||
|
||||
/**
|
||||
* Constructs a TiffWriter with the default tag mappings. This enables
|
||||
* all of the tags defined in TagDefinitions.h, and uses the following
|
||||
* mapping precedence to resolve collisions:
|
||||
* (highest precedence) TIFF/EP > DNG > EXIF 2.3 > TIFF 6.0
|
||||
*/
|
||||
TiffWriter();
|
||||
|
||||
/**
|
||||
* Constructs a TiffWriter with the given tag mappings. The mapping
|
||||
* precedence will be in the order that the definition maps are given,
|
||||
* where the lower index map gets precedence.
|
||||
*
|
||||
* This can be used with user-defined definitions, or definitions form
|
||||
* TagDefinitions.h
|
||||
*
|
||||
* The enabledDefinitions mapping object is owned by the caller, and must
|
||||
* stay alive for the lifespan of the constructed TiffWriter object.
|
||||
*/
|
||||
TiffWriter(std::map<uint16_t, const TagDefinition_t*>* enabledDefinitions,
|
||||
size_t length);
|
||||
|
||||
virtual ~TiffWriter();
|
||||
|
||||
/**
|
||||
* Write a TIFF header containing each IFD set. This will recursively
|
||||
* write all SubIFDs and tags.
|
||||
*
|
||||
* Any StripSources passed in will be written to the output as image strips
|
||||
* at the appropriate offests. The StripByteCounts, RowsPerStrip, and
|
||||
* StripOffsets tags must be set to use this. To set these tags in a
|
||||
* given IFD, use the addStrip method.
|
||||
*
|
||||
* Returns OK on success, or a negative error code on failure.
|
||||
*/
|
||||
virtual status_t write(Output* out, StripSource** sources, size_t sourcesCount,
|
||||
Endianness end = LITTLE);
|
||||
|
||||
/**
|
||||
* Write a TIFF header containing each IFD set. This will recursively
|
||||
* write all SubIFDs and tags.
|
||||
*
|
||||
* Image data for strips or tiles must be written separately at the
|
||||
* appropriate offsets. These offsets must not fall within the file
|
||||
* header written this way. The size of the header written is given
|
||||
* by the getTotalSize() method.
|
||||
*
|
||||
* Returns OK on success, or a negative error code on failure.
|
||||
*/
|
||||
virtual status_t write(Output* out, Endianness end = LITTLE);
|
||||
|
||||
/**
|
||||
* Get the total size in bytes of the TIFF header. This includes all
|
||||
* IFDs, tags, and values set for this TiffWriter.
|
||||
*/
|
||||
virtual uint32_t getTotalSize() const;
|
||||
|
||||
/**
|
||||
* Add an entry to the IFD with the given ID.
|
||||
*
|
||||
* Returns OK on success, or a negative error code on failure. Valid
|
||||
* error codes for this method are:
|
||||
* - BAD_INDEX - The given tag doesn't exist.
|
||||
* - BAD_VALUE - The given count doesn't match the required count for
|
||||
* this tag.
|
||||
* - BAD_TYPE - The type of the given data isn't compatible with the
|
||||
* type required for this tag.
|
||||
* - NAME_NOT_FOUND - No ifd exists with the given ID.
|
||||
*/
|
||||
virtual status_t addEntry(const sp<TiffEntry>& entry, uint32_t ifd);
|
||||
|
||||
/**
|
||||
* Build an entry for a known tag and add it to the IFD with the given ID.
|
||||
* This tag must be defined in one of the definition vectors this TIFF writer
|
||||
* was constructed with. The count and type are validated.
|
||||
*
|
||||
* Returns OK on success, or a negative error code on failure. Valid
|
||||
* error codes for this method are:
|
||||
* - BAD_INDEX - The given tag doesn't exist.
|
||||
* - BAD_VALUE - The given count doesn't match the required count for
|
||||
* this tag.
|
||||
* - BAD_TYPE - The type of the given data isn't compatible with the
|
||||
* type required for this tag.
|
||||
* - NAME_NOT_FOUND - No ifd exists with the given ID.
|
||||
*/
|
||||
template<typename T>
|
||||
status_t addEntry(uint16_t tag, uint32_t count, const T* data, uint32_t ifd);
|
||||
|
||||
/**
|
||||
* Build an entry for a known tag. This tag must be one of the tags
|
||||
* defined in one of the definition vectors this TIFF writer was constructed
|
||||
* with. The count and type are validated. If this succeeds, the resulting
|
||||
* entry will be placed in the outEntry pointer.
|
||||
*
|
||||
* Returns OK on success, or a negative error code on failure. Valid
|
||||
* error codes for this method are:
|
||||
* - BAD_INDEX - The given tag doesn't exist.
|
||||
* - BAD_VALUE - The given count doesn't match the required count for
|
||||
* this tag.
|
||||
* - BAD_TYPE - The type of the given data isn't compatible with the
|
||||
* type required for this tag.
|
||||
*/
|
||||
template<typename T>
|
||||
status_t buildEntry(uint16_t tag, uint32_t count, const T* data,
|
||||
/*out*/sp<TiffEntry>* outEntry) const;
|
||||
|
||||
/**
|
||||
* Convenience function to set the strip related tags for a given IFD.
|
||||
*
|
||||
* Call this before using a StripSource as an input to write.
|
||||
* The following tags must be set before calling this method:
|
||||
* - ImageWidth
|
||||
* - ImageLength
|
||||
* - SamplesPerPixel
|
||||
* - BitsPerSample
|
||||
*
|
||||
* Returns OK on success, or a negative error code.
|
||||
*/
|
||||
virtual status_t addStrip(uint32_t ifd);
|
||||
|
||||
/**
|
||||
* Return the TIFF entry with the given tag ID in the IFD with the given ID,
|
||||
* or an empty pointer if none exists.
|
||||
*/
|
||||
virtual sp<TiffEntry> getEntry(uint16_t tag, uint32_t ifd) const;
|
||||
|
||||
/**
|
||||
* Remove the TIFF entry with the given tag ID in the given IFD if it exists.
|
||||
*/
|
||||
virtual void removeEntry(uint16_t tag, uint32_t ifd);
|
||||
|
||||
/**
|
||||
* Create an empty IFD with the given ID and add it to the end of the
|
||||
* list of IFDs.
|
||||
*/
|
||||
virtual status_t addIfd(uint32_t ifd);
|
||||
|
||||
/**
|
||||
* Create an empty IFD with the given ID and add it as a SubIfd of the
|
||||
* parent IFD.
|
||||
*/
|
||||
virtual status_t addSubIfd(uint32_t parentIfd, uint32_t ifd, SubIfdType type = SUBIFD);
|
||||
|
||||
/**
|
||||
* Returns the default type for the given tag ID.
|
||||
*/
|
||||
virtual TagType getDefaultType(uint16_t tag) const;
|
||||
|
||||
/**
|
||||
* Returns the default count for a given tag ID, or 0 if this
|
||||
* tag normally has a variable count.
|
||||
*/
|
||||
virtual uint32_t getDefaultCount(uint16_t tag) const;
|
||||
|
||||
/**
|
||||
* Returns true if an IFD with the given ID exists.
|
||||
*/
|
||||
virtual bool hasIfd(uint32_t ifd) const;
|
||||
|
||||
/**
|
||||
* Returns true if a definition exist for the given tag ID.
|
||||
*/
|
||||
virtual bool checkIfDefined(uint16_t tag) const;
|
||||
|
||||
/**
|
||||
* Returns the name of the tag if a definition exists for the given tag
|
||||
* ID, or null if no definition exists.
|
||||
*/
|
||||
virtual const char* getTagName(uint16_t tag) const;
|
||||
|
||||
/**
|
||||
* Print the currently configured IFDs and entries to logcat.
|
||||
*/
|
||||
virtual void log() const;
|
||||
|
||||
/**
|
||||
* Build an entry. No validation is done.
|
||||
*
|
||||
* WARNING: Using this method can result in creating poorly formatted
|
||||
* TIFF files.
|
||||
*
|
||||
* Returns a TiffEntry with the given tag, type, count, endianness,
|
||||
* and data.
|
||||
*/
|
||||
template<typename T>
|
||||
static sp<TiffEntry> uncheckedBuildEntry(uint16_t tag, TagType type,
|
||||
uint32_t count, Endianness end, const T* data);
|
||||
|
||||
/**
|
||||
* Utility function to build atag-to-definition mapping from a given
|
||||
* array of tag definitions.
|
||||
*/
|
||||
#if 0
|
||||
static KeyedVector<uint16_t, const TagDefinition_t*> buildTagMap(
|
||||
const TagDefinition_t* definitions, size_t length);
|
||||
#endif
|
||||
|
||||
protected:
|
||||
enum {
|
||||
DEFAULT_NUM_TAG_MAPS = 4,
|
||||
};
|
||||
|
||||
sp<TiffIfd> findLastIfd();
|
||||
status_t writeFileHeader(EndianOutput& out);
|
||||
const TagDefinition_t* lookupDefinition(uint16_t tag) const;
|
||||
status_t calculateOffsets();
|
||||
|
||||
sp<TiffIfd> mIfd;
|
||||
std::map<uint32_t, sp<TiffIfd> > mNamedIfds;
|
||||
std::vector<std::map<uint16_t, const TagDefinition_t*> > mTagMaps;
|
||||
size_t mNumTagMaps;
|
||||
#if 0
|
||||
static KeyedVector<uint16_t, const TagDefinition_t*> sTagMaps[];
|
||||
#endif
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
status_t TiffWriter::buildEntry(uint16_t tag, uint32_t count, const T* data,
|
||||
/*out*/sp<TiffEntry>* outEntry) const {
|
||||
const TagDefinition_t* definition = lookupDefinition(tag);
|
||||
|
||||
if (definition == NULL) {
|
||||
ALOGE("%s: No such tag exists for id %x.", __FUNCTION__, tag);
|
||||
return BAD_INDEX;
|
||||
}
|
||||
|
||||
uint32_t fixedCount = definition->fixedCount;
|
||||
if (fixedCount > 0 && fixedCount != count) {
|
||||
ALOGE("%s: Invalid count %d for tag %x (expects %d).", __FUNCTION__, count, tag,
|
||||
fixedCount);
|
||||
return BAD_VALUE;
|
||||
}
|
||||
|
||||
TagType fixedType = definition->defaultType;
|
||||
if (TiffEntry::forceValidType(fixedType, data) == NULL) {
|
||||
ALOGE("%s: Invalid type used for tag value for tag %x.", __FUNCTION__, tag);
|
||||
return BAD_TYPE;
|
||||
}
|
||||
|
||||
*outEntry = new TiffEntryImpl<T>(tag, fixedType, count,
|
||||
definition->fixedEndian, data);
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
status_t TiffWriter::addEntry(uint16_t tag, uint32_t count, const T* data, uint32_t ifd) {
|
||||
sp<TiffEntry> outEntry;
|
||||
|
||||
status_t ret = buildEntry<T>(tag, count, data, &outEntry);
|
||||
if (ret != OK) {
|
||||
ALOGE("%s: Could not build entry for tag %x.", __FUNCTION__, tag);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return addEntry(outEntry, ifd);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
sp<TiffEntry> TiffWriter::uncheckedBuildEntry(uint16_t tag, TagType type, uint32_t count,
|
||||
Endianness end, const T* data) {
|
||||
TiffEntryImpl<T>* entry = new TiffEntryImpl<T>(tag, type, count, end, data);
|
||||
return sp<TiffEntry>(entry);
|
||||
}
|
||||
|
||||
} /*namespace img_utils*/
|
||||
} /*namespace android*/
|
||||
|
||||
|
||||
#endif /*IMG_UTILS_TIFF_WRITER_H*/
|
@ -0,0 +1,54 @@
|
||||
/*
|
||||
* Copyright 2014 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include <img_utils/ByteArrayOutput.h>
|
||||
|
||||
#include <utils/Log.h>
|
||||
|
||||
namespace android {
|
||||
namespace img_utils {
|
||||
|
||||
ByteArrayOutput::ByteArrayOutput() {}
|
||||
|
||||
ByteArrayOutput::~ByteArrayOutput() {}
|
||||
|
||||
status_t ByteArrayOutput::open() {
|
||||
return OK;
|
||||
}
|
||||
|
||||
status_t ByteArrayOutput::write(const uint8_t* buf, size_t offset, size_t count) {
|
||||
if (mByteArray.insert(mByteArray.end(), buf + offset, buf + offset + count) == mByteArray.end()) {
|
||||
ALOGE("%s: Failed to write to ByteArrayOutput.", __FUNCTION__);
|
||||
return BAD_VALUE;
|
||||
}
|
||||
return OK;
|
||||
}
|
||||
|
||||
status_t ByteArrayOutput::close() {
|
||||
mByteArray.clear();
|
||||
return OK;
|
||||
}
|
||||
|
||||
size_t ByteArrayOutput::getSize() const {
|
||||
return mByteArray.size();
|
||||
}
|
||||
|
||||
const uint8_t* ByteArrayOutput::getArray() const {
|
||||
return &mByteArray[0];
|
||||
}
|
||||
|
||||
} /*namespace img_utils*/
|
||||
} /*namespace android*/
|
@ -0,0 +1,496 @@
|
||||
/*
|
||||
* Copyright 2014 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include <img_utils/DngUtils.h>
|
||||
|
||||
#include <inttypes.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <vector>
|
||||
#include <math.h>
|
||||
|
||||
namespace android {
|
||||
namespace img_utils {
|
||||
|
||||
OpcodeListBuilder::OpcodeListBuilder() : mCount(0), mOpList(), mEndianOut(&mOpList, BIG) {
|
||||
if(mEndianOut.open() != OK) {
|
||||
ALOGE("%s: Open failed.", __FUNCTION__);
|
||||
}
|
||||
}
|
||||
|
||||
OpcodeListBuilder::~OpcodeListBuilder() {
|
||||
if(mEndianOut.close() != OK) {
|
||||
ALOGE("%s: Close failed.", __FUNCTION__);
|
||||
}
|
||||
}
|
||||
|
||||
size_t OpcodeListBuilder::getSize() const {
|
||||
return mOpList.getSize() + sizeof(mCount);
|
||||
}
|
||||
|
||||
uint32_t OpcodeListBuilder::getCount() const {
|
||||
return mCount;
|
||||
}
|
||||
|
||||
status_t OpcodeListBuilder::buildOpList(uint8_t* buf) const {
|
||||
uint32_t count = convertToBigEndian(mCount);
|
||||
memcpy(buf, &count, sizeof(count));
|
||||
memcpy(buf + sizeof(count), mOpList.getArray(), mOpList.getSize());
|
||||
return OK;
|
||||
}
|
||||
|
||||
status_t OpcodeListBuilder::addGainMapsForMetadata(uint32_t lsmWidth,
|
||||
uint32_t lsmHeight,
|
||||
uint32_t activeAreaTop,
|
||||
uint32_t activeAreaLeft,
|
||||
uint32_t activeAreaBottom,
|
||||
uint32_t activeAreaRight,
|
||||
CfaLayout cfa,
|
||||
const float* lensShadingMap) {
|
||||
status_t err = OK;
|
||||
uint32_t activeAreaWidth = activeAreaRight - activeAreaLeft;
|
||||
uint32_t activeAreaHeight = activeAreaBottom - activeAreaTop;
|
||||
|
||||
switch (cfa) {
|
||||
case CFA_RGGB:
|
||||
case CFA_GRBG:
|
||||
case CFA_GBRG:
|
||||
case CFA_BGGR:
|
||||
err = addBayerGainMapsForMetadata(lsmWidth, lsmHeight, activeAreaWidth,
|
||||
activeAreaHeight, cfa, lensShadingMap);
|
||||
break;
|
||||
case CFA_NONE:
|
||||
err = addMonochromeGainMapsForMetadata(lsmWidth, lsmHeight, activeAreaWidth,
|
||||
activeAreaHeight, lensShadingMap);
|
||||
break;
|
||||
default:
|
||||
ALOGE("%s: Unknown CFA layout %d", __FUNCTION__, cfa);
|
||||
err = BAD_VALUE;
|
||||
break;
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
status_t OpcodeListBuilder::addBayerGainMapsForMetadata(uint32_t lsmWidth,
|
||||
uint32_t lsmHeight,
|
||||
uint32_t activeAreaWidth,
|
||||
uint32_t activeAreaHeight,
|
||||
CfaLayout cfa,
|
||||
const float* lensShadingMap) {
|
||||
uint32_t redTop = 0;
|
||||
uint32_t redLeft = 0;
|
||||
uint32_t greenEvenTop = 0;
|
||||
uint32_t greenEvenLeft = 1;
|
||||
uint32_t greenOddTop = 1;
|
||||
uint32_t greenOddLeft = 0;
|
||||
uint32_t blueTop = 1;
|
||||
uint32_t blueLeft = 1;
|
||||
|
||||
switch(cfa) {
|
||||
case CFA_RGGB:
|
||||
redTop = 0;
|
||||
redLeft = 0;
|
||||
greenEvenTop = 0;
|
||||
greenEvenLeft = 1;
|
||||
greenOddTop = 1;
|
||||
greenOddLeft = 0;
|
||||
blueTop = 1;
|
||||
blueLeft = 1;
|
||||
break;
|
||||
case CFA_GRBG:
|
||||
redTop = 0;
|
||||
redLeft = 1;
|
||||
greenEvenTop = 0;
|
||||
greenEvenLeft = 0;
|
||||
greenOddTop = 1;
|
||||
greenOddLeft = 1;
|
||||
blueTop = 1;
|
||||
blueLeft = 0;
|
||||
break;
|
||||
case CFA_GBRG:
|
||||
redTop = 1;
|
||||
redLeft = 0;
|
||||
greenEvenTop = 0;
|
||||
greenEvenLeft = 0;
|
||||
greenOddTop = 1;
|
||||
greenOddLeft = 1;
|
||||
blueTop = 0;
|
||||
blueLeft = 1;
|
||||
break;
|
||||
case CFA_BGGR:
|
||||
redTop = 1;
|
||||
redLeft = 1;
|
||||
greenEvenTop = 0;
|
||||
greenEvenLeft = 1;
|
||||
greenOddTop = 1;
|
||||
greenOddLeft = 0;
|
||||
blueTop = 0;
|
||||
blueLeft = 0;
|
||||
break;
|
||||
default:
|
||||
ALOGE("%s: Unknown CFA layout %d", __FUNCTION__, cfa);
|
||||
return BAD_VALUE;
|
||||
}
|
||||
|
||||
std::vector<float> redMapVector(lsmWidth * lsmHeight);
|
||||
float *redMap = redMapVector.data();
|
||||
|
||||
std::vector<float> greenEvenMapVector(lsmWidth * lsmHeight);
|
||||
float *greenEvenMap = greenEvenMapVector.data();
|
||||
|
||||
std::vector<float> greenOddMapVector(lsmWidth * lsmHeight);
|
||||
float *greenOddMap = greenOddMapVector.data();
|
||||
|
||||
std::vector<float> blueMapVector(lsmWidth * lsmHeight);
|
||||
float *blueMap = blueMapVector.data();
|
||||
|
||||
double spacingV = 1.0 / std::max(1u, lsmHeight - 1);
|
||||
double spacingH = 1.0 / std::max(1u, lsmWidth - 1);
|
||||
|
||||
size_t lsmMapSize = lsmWidth * lsmHeight * 4;
|
||||
|
||||
// Split lens shading map channels into separate arrays
|
||||
size_t j = 0;
|
||||
for (size_t i = 0; i < lsmMapSize; i += 4, ++j) {
|
||||
redMap[j] = lensShadingMap[i + LSM_R_IND];
|
||||
greenEvenMap[j] = lensShadingMap[i + LSM_GE_IND];
|
||||
greenOddMap[j] = lensShadingMap[i + LSM_GO_IND];
|
||||
blueMap[j] = lensShadingMap[i + LSM_B_IND];
|
||||
}
|
||||
|
||||
status_t err = addGainMap(/*top*/redTop,
|
||||
/*left*/redLeft,
|
||||
/*bottom*/activeAreaHeight,
|
||||
/*right*/activeAreaWidth,
|
||||
/*plane*/0,
|
||||
/*planes*/1,
|
||||
/*rowPitch*/2,
|
||||
/*colPitch*/2,
|
||||
/*mapPointsV*/lsmHeight,
|
||||
/*mapPointsH*/lsmWidth,
|
||||
/*mapSpacingV*/spacingV,
|
||||
/*mapSpacingH*/spacingH,
|
||||
/*mapOriginV*/0,
|
||||
/*mapOriginH*/0,
|
||||
/*mapPlanes*/1,
|
||||
/*mapGains*/redMap);
|
||||
if (err != OK) return err;
|
||||
|
||||
err = addGainMap(/*top*/greenEvenTop,
|
||||
/*left*/greenEvenLeft,
|
||||
/*bottom*/activeAreaHeight,
|
||||
/*right*/activeAreaWidth,
|
||||
/*plane*/0,
|
||||
/*planes*/1,
|
||||
/*rowPitch*/2,
|
||||
/*colPitch*/2,
|
||||
/*mapPointsV*/lsmHeight,
|
||||
/*mapPointsH*/lsmWidth,
|
||||
/*mapSpacingV*/spacingV,
|
||||
/*mapSpacingH*/spacingH,
|
||||
/*mapOriginV*/0,
|
||||
/*mapOriginH*/0,
|
||||
/*mapPlanes*/1,
|
||||
/*mapGains*/greenEvenMap);
|
||||
if (err != OK) return err;
|
||||
|
||||
err = addGainMap(/*top*/greenOddTop,
|
||||
/*left*/greenOddLeft,
|
||||
/*bottom*/activeAreaHeight,
|
||||
/*right*/activeAreaWidth,
|
||||
/*plane*/0,
|
||||
/*planes*/1,
|
||||
/*rowPitch*/2,
|
||||
/*colPitch*/2,
|
||||
/*mapPointsV*/lsmHeight,
|
||||
/*mapPointsH*/lsmWidth,
|
||||
/*mapSpacingV*/spacingV,
|
||||
/*mapSpacingH*/spacingH,
|
||||
/*mapOriginV*/0,
|
||||
/*mapOriginH*/0,
|
||||
/*mapPlanes*/1,
|
||||
/*mapGains*/greenOddMap);
|
||||
if (err != OK) return err;
|
||||
|
||||
err = addGainMap(/*top*/blueTop,
|
||||
/*left*/blueLeft,
|
||||
/*bottom*/activeAreaHeight,
|
||||
/*right*/activeAreaWidth,
|
||||
/*plane*/0,
|
||||
/*planes*/1,
|
||||
/*rowPitch*/2,
|
||||
/*colPitch*/2,
|
||||
/*mapPointsV*/lsmHeight,
|
||||
/*mapPointsH*/lsmWidth,
|
||||
/*mapSpacingV*/spacingV,
|
||||
/*mapSpacingH*/spacingH,
|
||||
/*mapOriginV*/0,
|
||||
/*mapOriginH*/0,
|
||||
/*mapPlanes*/1,
|
||||
/*mapGains*/blueMap);
|
||||
return err;
|
||||
}
|
||||
|
||||
status_t OpcodeListBuilder::addMonochromeGainMapsForMetadata(uint32_t lsmWidth,
|
||||
uint32_t lsmHeight,
|
||||
uint32_t activeAreaWidth,
|
||||
uint32_t activeAreaHeight,
|
||||
const float* lensShadingMap) {
|
||||
std::vector<float> mapVector(lsmWidth * lsmHeight);
|
||||
float *map = mapVector.data();
|
||||
|
||||
double spacingV = 1.0 / std::max(1u, lsmHeight - 1);
|
||||
double spacingH = 1.0 / std::max(1u, lsmWidth - 1);
|
||||
|
||||
size_t lsmMapSize = lsmWidth * lsmHeight * 4;
|
||||
|
||||
// Split lens shading map channels into separate arrays
|
||||
size_t j = 0;
|
||||
for (size_t i = 0; i < lsmMapSize; i += 4, ++j) {
|
||||
map[j] = lensShadingMap[i];
|
||||
}
|
||||
|
||||
status_t err = addGainMap(/*top*/0,
|
||||
/*left*/0,
|
||||
/*bottom*/activeAreaHeight,
|
||||
/*right*/activeAreaWidth,
|
||||
/*plane*/0,
|
||||
/*planes*/1,
|
||||
/*rowPitch*/1,
|
||||
/*colPitch*/1,
|
||||
/*mapPointsV*/lsmHeight,
|
||||
/*mapPointsH*/lsmWidth,
|
||||
/*mapSpacingV*/spacingV,
|
||||
/*mapSpacingH*/spacingH,
|
||||
/*mapOriginV*/0,
|
||||
/*mapOriginH*/0,
|
||||
/*mapPlanes*/1,
|
||||
/*mapGains*/map);
|
||||
if (err != OK) return err;
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
status_t OpcodeListBuilder::addGainMap(uint32_t top,
|
||||
uint32_t left,
|
||||
uint32_t bottom,
|
||||
uint32_t right,
|
||||
uint32_t plane,
|
||||
uint32_t planes,
|
||||
uint32_t rowPitch,
|
||||
uint32_t colPitch,
|
||||
uint32_t mapPointsV,
|
||||
uint32_t mapPointsH,
|
||||
double mapSpacingV,
|
||||
double mapSpacingH,
|
||||
double mapOriginV,
|
||||
double mapOriginH,
|
||||
uint32_t mapPlanes,
|
||||
const float* mapGains) {
|
||||
|
||||
status_t err = addOpcodePreamble(GAIN_MAP_ID);
|
||||
if (err != OK) return err;
|
||||
|
||||
// Allow this opcode to be skipped if not supported
|
||||
uint32_t flags = FLAG_OPTIONAL;
|
||||
|
||||
err = mEndianOut.write(&flags, 0, 1);
|
||||
if (err != OK) return err;
|
||||
|
||||
const uint32_t NUMBER_INT_ARGS = 11;
|
||||
const uint32_t NUMBER_DOUBLE_ARGS = 4;
|
||||
|
||||
uint32_t totalSize = NUMBER_INT_ARGS * sizeof(uint32_t) + NUMBER_DOUBLE_ARGS * sizeof(double) +
|
||||
mapPointsV * mapPointsH * mapPlanes * sizeof(float);
|
||||
|
||||
err = mEndianOut.write(&totalSize, 0, 1);
|
||||
if (err != OK) return err;
|
||||
|
||||
// Batch writes as much as possible
|
||||
uint32_t settings1[] = { top,
|
||||
left,
|
||||
bottom,
|
||||
right,
|
||||
plane,
|
||||
planes,
|
||||
rowPitch,
|
||||
colPitch,
|
||||
mapPointsV,
|
||||
mapPointsH };
|
||||
|
||||
err = mEndianOut.write(settings1, 0, NELEMS(settings1));
|
||||
if (err != OK) return err;
|
||||
|
||||
double settings2[] = { mapSpacingV,
|
||||
mapSpacingH,
|
||||
mapOriginV,
|
||||
mapOriginH };
|
||||
|
||||
err = mEndianOut.write(settings2, 0, NELEMS(settings2));
|
||||
if (err != OK) return err;
|
||||
|
||||
err = mEndianOut.write(&mapPlanes, 0, 1);
|
||||
if (err != OK) return err;
|
||||
|
||||
err = mEndianOut.write(mapGains, 0, mapPointsV * mapPointsH * mapPlanes);
|
||||
if (err != OK) return err;
|
||||
|
||||
mCount++;
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
status_t OpcodeListBuilder::addWarpRectilinearForMetadata(const float* kCoeffs,
|
||||
uint32_t activeArrayWidth,
|
||||
uint32_t activeArrayHeight,
|
||||
float opticalCenterX,
|
||||
float opticalCenterY) {
|
||||
if (activeArrayWidth <= 1 || activeArrayHeight <= 1) {
|
||||
ALOGE("%s: Cannot add opcode for active array with dimensions w=%" PRIu32 ", h=%" PRIu32,
|
||||
__FUNCTION__, activeArrayWidth, activeArrayHeight);
|
||||
return BAD_VALUE;
|
||||
}
|
||||
|
||||
double normalizedOCX = opticalCenterX / static_cast<double>(activeArrayWidth);
|
||||
double normalizedOCY = opticalCenterY / static_cast<double>(activeArrayHeight);
|
||||
|
||||
normalizedOCX = CLAMP(normalizedOCX, 0, 1);
|
||||
normalizedOCY = CLAMP(normalizedOCY, 0, 1);
|
||||
|
||||
double coeffs[6] = {
|
||||
kCoeffs[0],
|
||||
kCoeffs[1],
|
||||
kCoeffs[2],
|
||||
kCoeffs[3],
|
||||
kCoeffs[4],
|
||||
kCoeffs[5]
|
||||
};
|
||||
|
||||
return addWarpRectilinear(/*numPlanes*/1,
|
||||
/*opticalCenterX*/normalizedOCX,
|
||||
/*opticalCenterY*/normalizedOCY,
|
||||
coeffs);
|
||||
}
|
||||
|
||||
status_t OpcodeListBuilder::addWarpRectilinear(uint32_t numPlanes,
|
||||
double opticalCenterX,
|
||||
double opticalCenterY,
|
||||
const double* kCoeffs) {
|
||||
|
||||
status_t err = addOpcodePreamble(WARP_RECTILINEAR_ID);
|
||||
if (err != OK) return err;
|
||||
|
||||
// Allow this opcode to be skipped if not supported
|
||||
uint32_t flags = FLAG_OPTIONAL;
|
||||
|
||||
err = mEndianOut.write(&flags, 0, 1);
|
||||
if (err != OK) return err;
|
||||
|
||||
const uint32_t NUMBER_CENTER_ARGS = 2;
|
||||
const uint32_t NUMBER_COEFFS = numPlanes * 6;
|
||||
uint32_t totalSize = (NUMBER_CENTER_ARGS + NUMBER_COEFFS) * sizeof(double) + sizeof(uint32_t);
|
||||
|
||||
err = mEndianOut.write(&totalSize, 0, 1);
|
||||
if (err != OK) return err;
|
||||
|
||||
err = mEndianOut.write(&numPlanes, 0, 1);
|
||||
if (err != OK) return err;
|
||||
|
||||
err = mEndianOut.write(kCoeffs, 0, NUMBER_COEFFS);
|
||||
if (err != OK) return err;
|
||||
|
||||
err = mEndianOut.write(&opticalCenterX, 0, 1);
|
||||
if (err != OK) return err;
|
||||
|
||||
err = mEndianOut.write(&opticalCenterY, 0, 1);
|
||||
if (err != OK) return err;
|
||||
|
||||
mCount++;
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
status_t OpcodeListBuilder::addBadPixelListForMetadata(const uint32_t* hotPixels,
|
||||
uint32_t xyPairCount,
|
||||
uint32_t colorFilterArrangement) {
|
||||
if (colorFilterArrangement > 3) {
|
||||
ALOGE("%s: Unknown color filter arrangement %" PRIu32, __FUNCTION__,
|
||||
colorFilterArrangement);
|
||||
return BAD_VALUE;
|
||||
}
|
||||
|
||||
return addBadPixelList(colorFilterArrangement, xyPairCount, 0, hotPixels, nullptr);
|
||||
}
|
||||
|
||||
status_t OpcodeListBuilder::addBadPixelList(uint32_t bayerPhase,
|
||||
uint32_t badPointCount,
|
||||
uint32_t badRectCount,
|
||||
const uint32_t* badPointRowColPairs,
|
||||
const uint32_t* badRectTopLeftBottomRightTuples) {
|
||||
|
||||
status_t err = addOpcodePreamble(FIX_BAD_PIXELS_LIST);
|
||||
if (err != OK) return err;
|
||||
|
||||
// Allow this opcode to be skipped if not supported
|
||||
uint32_t flags = FLAG_OPTIONAL;
|
||||
|
||||
err = mEndianOut.write(&flags, 0, 1);
|
||||
if (err != OK) return err;
|
||||
|
||||
const uint32_t NUM_NON_VARLEN_FIELDS = 3;
|
||||
const uint32_t SIZE_OF_POINT = 2;
|
||||
const uint32_t SIZE_OF_RECT = 4;
|
||||
|
||||
uint32_t totalSize = (NUM_NON_VARLEN_FIELDS + badPointCount * SIZE_OF_POINT +
|
||||
badRectCount * SIZE_OF_RECT) * sizeof(uint32_t);
|
||||
err = mEndianOut.write(&totalSize, 0, 1);
|
||||
if (err != OK) return err;
|
||||
|
||||
err = mEndianOut.write(&bayerPhase, 0, 1);
|
||||
if (err != OK) return err;
|
||||
|
||||
err = mEndianOut.write(&badPointCount, 0, 1);
|
||||
if (err != OK) return err;
|
||||
|
||||
err = mEndianOut.write(&badRectCount, 0, 1);
|
||||
if (err != OK) return err;
|
||||
|
||||
if (badPointCount > 0) {
|
||||
err = mEndianOut.write(badPointRowColPairs, 0, SIZE_OF_POINT * badPointCount);
|
||||
if (err != OK) return err;
|
||||
}
|
||||
|
||||
if (badRectCount > 0) {
|
||||
err = mEndianOut.write(badRectTopLeftBottomRightTuples, 0, SIZE_OF_RECT * badRectCount);
|
||||
if (err != OK) return err;
|
||||
}
|
||||
|
||||
mCount++;
|
||||
return OK;
|
||||
}
|
||||
|
||||
status_t OpcodeListBuilder::addOpcodePreamble(uint32_t opcodeId) {
|
||||
status_t err = mEndianOut.write(&opcodeId, 0, 1);
|
||||
if (err != OK) return err;
|
||||
|
||||
uint8_t version[] = {1, 3, 0, 0};
|
||||
err = mEndianOut.write(version, 0, NELEMS(version));
|
||||
if (err != OK) return err;
|
||||
return OK;
|
||||
}
|
||||
|
||||
} /*namespace img_utils*/
|
||||
} /*namespace android*/
|
@ -0,0 +1,83 @@
|
||||
/*
|
||||
* Copyright 2014 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include <img_utils/EndianUtils.h>
|
||||
|
||||
namespace android {
|
||||
namespace img_utils {
|
||||
|
||||
EndianOutput::EndianOutput(Output* out, Endianness end)
|
||||
: mOffset(0), mOutput(out), mEndian(end) {}
|
||||
|
||||
EndianOutput::~EndianOutput() {}
|
||||
|
||||
status_t EndianOutput::open() {
|
||||
mOffset = 0;
|
||||
return mOutput->open();
|
||||
}
|
||||
|
||||
status_t EndianOutput::close() {
|
||||
return mOutput->close();
|
||||
}
|
||||
|
||||
void EndianOutput::setEndianness(Endianness end) {
|
||||
mEndian = end;
|
||||
}
|
||||
|
||||
uint32_t EndianOutput::getCurrentOffset() const {
|
||||
return mOffset;
|
||||
}
|
||||
|
||||
Endianness EndianOutput::getEndianness() const {
|
||||
return mEndian;
|
||||
}
|
||||
|
||||
status_t EndianOutput::write(const uint8_t* buf, size_t offset, size_t count) {
|
||||
status_t res = OK;
|
||||
if((res = mOutput->write(buf, offset, count)) == OK) {
|
||||
mOffset += count;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
status_t EndianOutput::write(const int8_t* buf, size_t offset, size_t count) {
|
||||
return write(reinterpret_cast<const uint8_t*>(buf), offset, count);
|
||||
}
|
||||
|
||||
#define DEFINE_WRITE(_type_) \
|
||||
status_t EndianOutput::write(const _type_* buf, size_t offset, size_t count) { \
|
||||
return writeHelper<_type_>(buf, offset, count); \
|
||||
}
|
||||
|
||||
DEFINE_WRITE(uint16_t)
|
||||
DEFINE_WRITE(int16_t)
|
||||
DEFINE_WRITE(uint32_t)
|
||||
DEFINE_WRITE(int32_t)
|
||||
DEFINE_WRITE(uint64_t)
|
||||
DEFINE_WRITE(int64_t)
|
||||
|
||||
status_t EndianOutput::write(const float* buf, size_t offset, size_t count) {
|
||||
assert(sizeof(float) == sizeof(uint32_t));
|
||||
return writeHelper<uint32_t>(reinterpret_cast<const uint32_t*>(buf), offset, count);
|
||||
}
|
||||
|
||||
status_t EndianOutput::write(const double* buf, size_t offset, size_t count) {
|
||||
assert(sizeof(double) == sizeof(uint64_t));
|
||||
return writeHelper<uint64_t>(reinterpret_cast<const uint64_t*>(buf), offset, count);
|
||||
}
|
||||
|
||||
} /*namespace img_utils*/
|
||||
} /*namespace android*/
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue