Compare commits
321 Commits
temp-deriv
...
temp-geome
Author | SHA1 | Date | |
---|---|---|---|
41238bba45 | |||
0732e3de35 | |||
5cd33108fc | |||
36397a721a | |||
1173d1ca9e | |||
f7a6dd7218 | |||
9934a71172 | |||
e46dcc499c | |||
4be8061baf | |||
c20d7676fc | |||
507fdd0e3e | |||
9f68f5c1e1 | |||
dcfea4a1e5 | |||
e30315ba95 | |||
75fc6e3b2b | |||
00baf875ef | |||
2c216413d5 | |||
1e29f64987 | |||
03490618a2 | |||
09e77d2c89 | |||
165a2da753 | |||
267a9e14f5 | |||
b79f209041 | |||
bbc5e26051 | |||
ff07a4afb8 | |||
83b7f7dfb7 | |||
40d579b69f | |||
6a7f6f2867 | |||
5fee9dae5d | |||
ba3a0dc9ba | |||
2966871a7a | |||
88f845c881 | |||
![]() |
670453d1ec | ||
8ab6450abb | |||
9b2b6674ba | |||
258b15da74 | |||
74052a9f02 | |||
9d08c169d1 | |||
5788f608d3 | |||
476be3746e | |||
b01e9ad4f0 | |||
abe1a061f8 | |||
651fe243e6 | |||
f707783d5f | |||
![]() |
ef5782e297 | ||
bcac17196a | |||
f0c3ec3dc8 | |||
c7354cc64b | |||
20ee6c0f16 | |||
fd905c1059 | |||
7388f9df71 | |||
583df9a5f8 | |||
74557ca4f7 | |||
fe2ceef729 | |||
4781ab0969 | |||
fd4c01a75c | |||
960337f17a | |||
a5c44265a3 | |||
2a5f22c1af | |||
2e9fb211c6 | |||
7f4530dad2 | |||
8125731cae | |||
d3fa576aa7 | |||
406d9749d8 | |||
8922d177c1 | |||
d72fc36ec5 | |||
7017844c5e | |||
7a028d5b99 | |||
62e2fdf40b | |||
2ebf4fbbfb | |||
3256f0d52c | |||
60f7275f7f | |||
cec588d757 | |||
3e5869e083 | |||
5f9bec93e6 | |||
afa30f1a9d | |||
8c6337e587 | |||
1b1f8da5dd | |||
670c1fdf64 | |||
7092d6a7a3 | |||
46aa70cb48 | |||
ed2c4825d3 | |||
a1b01edf45 | |||
9dfc81ccf1 | |||
![]() |
350ad4bcb1 | ||
d518b0fda5 | |||
5b91a52944 | |||
2cc5af9c55 | |||
2cdebf293d | |||
5812bc7d89 | |||
f7616c6eaf | |||
167525dc8d | |||
93f8c9b823 | |||
a023c1a34c | |||
534f4e90fd | |||
96064c3bb7 | |||
0a6ed7f035 | |||
f4f8b6dde3 | |||
ba996ddb3a | |||
0f60dbe4bf | |||
d4d03f736b | |||
fade765bf3 | |||
f59ff9e03a | |||
018fffbe77 | |||
42c5303409 | |||
5f1f233dc9 | |||
![]() |
74f3edc343 | ||
28e83bca9d | |||
![]() |
85623f6a55 | ||
9ef24d5aaa | |||
1d6764dddd | |||
507b8fa527 | |||
91e42d81fe | |||
4cd9a1164b | |||
352385d109 | |||
5ab2252a66 | |||
56bf4f3fb3 | |||
793335f3e2 | |||
d89fb77d89 | |||
9957096f35 | |||
79bc4962d3 | |||
cf2933c38a | |||
eb20250d2a | |||
1e7b2d0bc6 | |||
7f07eff588 | |||
576c392241 | |||
5991c5c928 | |||
![]() |
493628e541 | ||
4fece16d45 | |||
368647bd25 | |||
![]() |
122fefcc85 | ||
3dab6f8b7b | |||
f247a14468 | |||
70e73974b5 | |||
548e2e2f25 | |||
3e67e2a36e | |||
53b82efed6 | |||
90520026e9 | |||
7d827d0e9e | |||
dcd7dacc4f | |||
cfd7b4d1cd | |||
80f7f1070f | |||
996586860b | |||
3f7b585a08 | |||
e5c1e13ef0 | |||
b1ef55abdb | |||
8351e2d4b9 | |||
![]() |
4069016de8 | ||
![]() |
9d046019e2 | ||
![]() |
cd1e6df534 | ||
04e816bd11 | |||
c6a831cfbc | |||
077beb738c | |||
0700441578 | |||
![]() |
eaada56591 | ||
53daf2a0db | |||
cdb0b3cedc | |||
be6b3923f5 | |||
6114d909d6 | |||
68ff213cd4 | |||
7f9b6b1dc9 | |||
2283b6ef69 | |||
07a9d39f57 | |||
dc3e9048ae | |||
73d53b3bd8 | |||
745576b16e | |||
55fe91b83b | |||
94d826f6d6 | |||
51b731d479 | |||
5fa962c7f6 | |||
543783fc61 | |||
a70a715f67 | |||
1f95b07b32 | |||
d25ab68cf4 | |||
8df968d69f | |||
2e19509e60 | |||
![]() |
a45af290f3 | ||
84a4f2ae68 | |||
fc0de69471 | |||
f0ad78e17c | |||
4292bb060d | |||
8771f015f5 | |||
9ce950daab | |||
bf799cb12c | |||
30cb4326fe | |||
1540f1df07 | |||
0983e66e03 | |||
5db5966cdb | |||
6842c549bb | |||
ba75ea8012 | |||
6afe2d373a | |||
a6a8ca9212 | |||
1aa59464b6 | |||
9cb5f0a228 | |||
d230c9b96c | |||
5a67407d5a | |||
e20b31504a | |||
91825ebfe2 | |||
9e09214979 | |||
cfd11af981 | |||
09a8f5ebca | |||
![]() |
b6c07d69e2 | ||
171ba42439 | |||
3669a3e2e9 | |||
240e721dd3 | |||
9ba1ff1c63 | |||
1775ea74c1 | |||
b9cd2f4531 | |||
e9e53ff3a6 | |||
![]() |
e12ad2bce0 | ||
2b9eea17cc | |||
74c50d0c77 | |||
cbf033c055 | |||
89757f918c | |||
a1bc7729f2 | |||
b4f5128b21 | |||
242a278b56 | |||
c950e08cbb | |||
1ba15f1f7f | |||
7a34bd7c28 | |||
239a7d93ae | |||
74979459cb | |||
9c8382e618 | |||
00f218602d | |||
ac4d45dbf1 | |||
111a77e818 | |||
a9fc9ce0ae | |||
b30f89918e | |||
e72dc1e6c6 | |||
c14770370f | |||
649916f098 | |||
456d3cc85e | |||
84da76a96c | |||
8618c4159c | |||
9828882b8e | |||
d2869943d2 | |||
cfd766cebd | |||
11efc9087b | |||
995bb0860a | |||
37793b90be | |||
9dc0c44aa1 | |||
bd79691599 | |||
753f1cf0ad | |||
b22b037229 | |||
3bc406274b | |||
f117ea2624 | |||
becc36cce5 | |||
4addcf1efc | |||
3a907e7425 | |||
ae005393dc | |||
b9e54566e3 | |||
ed84161529 | |||
f882bee431 | |||
d5c727c6ea | |||
ffd5b0d91e | |||
0729376a13 | |||
d4c673d4c6 | |||
7c8ec99b9a | |||
7bccbce512 | |||
921138cf5f | |||
87842d6388 | |||
fde150fee4 | |||
e1d9b095e4 | |||
3d4a844a50 | |||
8b2fb7aeed | |||
6ebd34c802 | |||
ba5961b4cd | |||
3d3a5bb892 | |||
bf030decd4 | |||
8d0fbcd6df | |||
fe35551df2 | |||
0dd9a4a576 | |||
e0442a955b | |||
a592f7e6cb | |||
b12be5a872 | |||
f3fb1df192 | |||
5668901ced | |||
6bc01222c6 | |||
99e1866712 | |||
b5d154f400 | |||
663b0bb04c | |||
511ff8b6b4 | |||
c2a8676544 | |||
78bd155f5c | |||
c2e73a1225 | |||
23dfcc5ac7 | |||
aa86710242 | |||
b9833860e5 | |||
e0ba6a4411 | |||
59889d9b4f | |||
97fabc3c1c | |||
![]() |
57812c7e4f | ||
6a662ffda8 | |||
1668f883fb | |||
63b7ff9f4e | |||
f951aa063f | |||
9b28ab0d0b | |||
83c87b6564 | |||
50072596d1 | |||
05aea4bb51 | |||
f993a47248 | |||
73b023bbab | |||
95ecd5804d | |||
0c42b40aee | |||
0084954ade | |||
8464641e77 | |||
4f875a31c9 | |||
46b8b36eff | |||
475f0f5ece | |||
9a501e1ece | |||
3915392560 | |||
fdb116a0b6 | |||
ca8d1900ff | |||
edce7ee71d | |||
ffd63bc495 | |||
fbf093dee1 | |||
414747f40d | |||
e3995e5050 | |||
6ac36103ea | |||
fcd7c0cfcc | |||
93788a9b8d |
@@ -161,6 +161,7 @@ PenaltyBreakString: 1000000
|
|||||||
# "^\s+[A-Z][A-Z0-9_]+\s*\([^\n]*\)\n\s*\{"
|
# "^\s+[A-Z][A-Z0-9_]+\s*\([^\n]*\)\n\s*\{"
|
||||||
ForEachMacros:
|
ForEachMacros:
|
||||||
- BEGIN_ANIMFILTER_SUBCHANNELS
|
- BEGIN_ANIMFILTER_SUBCHANNELS
|
||||||
|
- BKE_pbvh_vertex_iter_begin
|
||||||
- BLI_FOREACH_SPARSE_RANGE
|
- BLI_FOREACH_SPARSE_RANGE
|
||||||
- BLI_SMALLSTACK_ITER_BEGIN
|
- BLI_SMALLSTACK_ITER_BEGIN
|
||||||
- BMO_ITER
|
- BMO_ITER
|
||||||
|
@@ -506,7 +506,7 @@ check_descriptions: .FORCE
|
|||||||
#
|
#
|
||||||
|
|
||||||
source_archive: .FORCE
|
source_archive: .FORCE
|
||||||
./build_files/utils/make_source_archive.sh
|
python3 ./build_files/utils/make_source_archive.py
|
||||||
|
|
||||||
INKSCAPE_BIN?="inkscape"
|
INKSCAPE_BIN?="inkscape"
|
||||||
icons: .FORCE
|
icons: .FORCE
|
||||||
|
@@ -66,7 +66,7 @@ endif()
|
|||||||
if(XCODE_VERSION)
|
if(XCODE_VERSION)
|
||||||
# Construct SDKs path ourselves, because xcode-select path could be ambiguous.
|
# Construct SDKs path ourselves, because xcode-select path could be ambiguous.
|
||||||
# Both /Applications/Xcode.app/Contents/Developer or /Applications/Xcode.app would be allowed.
|
# Both /Applications/Xcode.app/Contents/Developer or /Applications/Xcode.app would be allowed.
|
||||||
set(XCODE_SDK_DIR ${XCODE_DEVELOPER_DIR}/Platforms/MacOSX.platform//Developer/SDKs)
|
set(XCODE_SDK_DIR ${XCODE_DEVELOPER_DIR}/Platforms/MacOSX.platform/Developer/SDKs)
|
||||||
|
|
||||||
# Detect SDK version to use
|
# Detect SDK version to use
|
||||||
if(NOT DEFINED OSX_SYSTEM)
|
if(NOT DEFINED OSX_SYSTEM)
|
||||||
|
198
build_files/utils/make_source_archive.py
Executable file
198
build_files/utils/make_source_archive.py
Executable file
@@ -0,0 +1,198 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
|
import dataclasses
|
||||||
|
import os
|
||||||
|
import re
|
||||||
|
import subprocess
|
||||||
|
from pathlib import Path
|
||||||
|
from typing import Iterable, TextIO
|
||||||
|
|
||||||
|
# This script can run from any location,
|
||||||
|
# output is created in the $CWD
|
||||||
|
#
|
||||||
|
# NOTE: while the Python part of this script is portable,
|
||||||
|
# it relies on external commands typically found on GNU/Linux.
|
||||||
|
# Support for other platforms could be added by moving GNU `tar` & `md5sum` use to Python.
|
||||||
|
|
||||||
|
SKIP_NAMES = {
|
||||||
|
".gitignore",
|
||||||
|
".gitmodules",
|
||||||
|
".arcconfig",
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def main() -> None:
|
||||||
|
output_dir = Path(".").absolute()
|
||||||
|
blender_srcdir = Path(__file__).absolute().parent.parent.parent
|
||||||
|
print(f"Source dir: {blender_srcdir}")
|
||||||
|
|
||||||
|
version = parse_blender_version(blender_srcdir)
|
||||||
|
manifest = output_dir / f"blender-{version}-manifest.txt"
|
||||||
|
tarball = output_dir / f"blender-{version}.tar.xz"
|
||||||
|
|
||||||
|
os.chdir(blender_srcdir)
|
||||||
|
create_manifest(version, manifest)
|
||||||
|
create_tarball(version, tarball, manifest)
|
||||||
|
create_checksum_file(tarball)
|
||||||
|
cleanup(manifest)
|
||||||
|
print("Done!")
|
||||||
|
|
||||||
|
|
||||||
|
@dataclasses.dataclass
|
||||||
|
class BlenderVersion:
|
||||||
|
version: int # 293 for 2.93.1
|
||||||
|
patch: int # 1 for 2.93.1
|
||||||
|
cycle: str # 'alpha', 'beta', 'release', maybe others.
|
||||||
|
|
||||||
|
@property
|
||||||
|
def is_release(self) -> bool:
|
||||||
|
return self.cycle == "release"
|
||||||
|
|
||||||
|
def __str__(self) -> str:
|
||||||
|
"""Convert to version string.
|
||||||
|
|
||||||
|
>>> str(BlenderVersion(293, 1, "alpha"))
|
||||||
|
'2.93.1-alpha'
|
||||||
|
>>> str(BlenderVersion(327, 0, "release"))
|
||||||
|
'3.27.0'
|
||||||
|
"""
|
||||||
|
version_major = self.version // 100
|
||||||
|
version_minor = self.version % 100
|
||||||
|
as_string = f"{version_major}.{version_minor}.{self.patch}"
|
||||||
|
if self.is_release:
|
||||||
|
return as_string
|
||||||
|
return f"{as_string}-{self.cycle}"
|
||||||
|
|
||||||
|
|
||||||
|
def parse_blender_version(blender_srcdir: Path) -> BlenderVersion:
|
||||||
|
version_path = blender_srcdir / "source/blender/blenkernel/BKE_blender_version.h"
|
||||||
|
|
||||||
|
version_info = {}
|
||||||
|
line_re = re.compile(r"^#define (BLENDER_VERSION[A-Z_]*)\s+([0-9a-z]+)$")
|
||||||
|
|
||||||
|
with version_path.open(encoding="utf-8") as version_file:
|
||||||
|
for line in version_file:
|
||||||
|
match = line_re.match(line.strip())
|
||||||
|
if not match:
|
||||||
|
continue
|
||||||
|
version_info[match.group(1)] = match.group(2)
|
||||||
|
|
||||||
|
return BlenderVersion(
|
||||||
|
int(version_info["BLENDER_VERSION"]),
|
||||||
|
int(version_info["BLENDER_VERSION_PATCH"]),
|
||||||
|
version_info["BLENDER_VERSION_CYCLE"],
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
### Manifest creation
|
||||||
|
|
||||||
|
|
||||||
|
def create_manifest(version: BlenderVersion, outpath: Path) -> None:
|
||||||
|
print(f'Building manifest of files: "{outpath}"...', end="", flush=True)
|
||||||
|
with outpath.open("w", encoding="utf-8") as outfile:
|
||||||
|
main_files_to_manifest(outfile)
|
||||||
|
submodules_to_manifest(version, outfile)
|
||||||
|
print("OK")
|
||||||
|
|
||||||
|
|
||||||
|
def main_files_to_manifest(outfile: TextIO) -> None:
|
||||||
|
for path in git_ls_files():
|
||||||
|
print(path, file=outfile)
|
||||||
|
|
||||||
|
|
||||||
|
def submodules_to_manifest(version: BlenderVersion, outfile: TextIO) -> None:
|
||||||
|
skip_addon_contrib = version.is_release
|
||||||
|
|
||||||
|
for line in git_command("submodule"):
|
||||||
|
submodule = line.split()[1]
|
||||||
|
|
||||||
|
# Don't use native slashes as GIT for MS-Windows outputs forward slashes.
|
||||||
|
if skip_addon_contrib and submodule == "release/scripts/addons_contrib":
|
||||||
|
continue
|
||||||
|
|
||||||
|
for path in git_ls_files(Path(submodule)):
|
||||||
|
print(path, file=outfile)
|
||||||
|
|
||||||
|
|
||||||
|
def create_tarball(version: BlenderVersion, tarball: Path, manifest: Path) -> None:
|
||||||
|
print(f'Creating archive: "{tarball}" ...', end="", flush=True)
|
||||||
|
# Requires GNU `tar`, since `--transform` is used.
|
||||||
|
command = [
|
||||||
|
"tar",
|
||||||
|
"--transform",
|
||||||
|
f"s,^,blender-{version}/,g",
|
||||||
|
"--use-compress-program=xz -9",
|
||||||
|
"--create",
|
||||||
|
f"--file={tarball}",
|
||||||
|
f"--files-from={manifest}",
|
||||||
|
# Without owner/group args, extracting the files as root will
|
||||||
|
# use ownership from the tar archive:
|
||||||
|
"--owner=0",
|
||||||
|
"--group=0",
|
||||||
|
]
|
||||||
|
subprocess.run(command, check=True, timeout=300)
|
||||||
|
print("OK")
|
||||||
|
|
||||||
|
|
||||||
|
def create_checksum_file(tarball: Path) -> None:
|
||||||
|
md5_path = tarball.with_name(tarball.name + ".md5sum")
|
||||||
|
print(f'Creating checksum: "{md5_path}" ...', end="", flush=True)
|
||||||
|
command = [
|
||||||
|
"md5sum",
|
||||||
|
# The name is enough, as the tarball resides in the same dir as the MD5
|
||||||
|
# file, and that's the current working directory.
|
||||||
|
tarball.name,
|
||||||
|
]
|
||||||
|
md5_cmd = subprocess.run(
|
||||||
|
command, stdout=subprocess.PIPE, check=True, text=True, timeout=300
|
||||||
|
)
|
||||||
|
with md5_path.open("w", encoding="utf-8") as outfile:
|
||||||
|
outfile.write(md5_cmd.stdout)
|
||||||
|
print("OK")
|
||||||
|
|
||||||
|
|
||||||
|
def cleanup(manifest: Path) -> None:
|
||||||
|
print("Cleaning up ...", end="", flush=True)
|
||||||
|
if manifest.exists():
|
||||||
|
manifest.unlink()
|
||||||
|
print("OK")
|
||||||
|
|
||||||
|
|
||||||
|
## Low-level commands
|
||||||
|
|
||||||
|
|
||||||
|
def git_ls_files(directory: Path = Path(".")) -> Iterable[Path]:
|
||||||
|
"""Generator, yields lines of output from 'git ls-files'.
|
||||||
|
|
||||||
|
Only lines that are actually files (so no directories, sockets, etc.) are
|
||||||
|
returned, and never one from SKIP_NAMES.
|
||||||
|
"""
|
||||||
|
for line in git_command("-C", str(directory), "ls-files"):
|
||||||
|
path = directory / line
|
||||||
|
if not path.is_file() or path.name in SKIP_NAMES:
|
||||||
|
continue
|
||||||
|
yield path
|
||||||
|
|
||||||
|
|
||||||
|
def git_command(*cli_args) -> Iterable[str]:
|
||||||
|
"""Generator, yields lines of output from a Git command."""
|
||||||
|
command = ("git", *cli_args)
|
||||||
|
|
||||||
|
# import shlex
|
||||||
|
# print(">", " ".join(shlex.quote(arg) for arg in command))
|
||||||
|
|
||||||
|
git = subprocess.run(
|
||||||
|
command, stdout=subprocess.PIPE, check=True, text=True, timeout=30
|
||||||
|
)
|
||||||
|
for line in git.stdout.split("\n"):
|
||||||
|
if line:
|
||||||
|
yield line
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
import doctest
|
||||||
|
|
||||||
|
if doctest.testmod().failed:
|
||||||
|
raise SystemExit("ERROR: Self-test failed, refusing to run")
|
||||||
|
|
||||||
|
main()
|
@@ -1,82 +0,0 @@
|
|||||||
#!/bin/sh
|
|
||||||
|
|
||||||
# This script can run from any location,
|
|
||||||
# output is created in the $CWD
|
|
||||||
|
|
||||||
BASE_DIR="$PWD"
|
|
||||||
|
|
||||||
blender_srcdir=$(dirname -- $0)/../..
|
|
||||||
blender_version=$(grep "BLENDER_VERSION\s" "$blender_srcdir/source/blender/blenkernel/BKE_blender_version.h" | awk '{print $3}')
|
|
||||||
blender_version_patch=$(grep "BLENDER_VERSION_PATCH\s" "$blender_srcdir/source/blender/blenkernel/BKE_blender_version.h" | awk '{print $3}')
|
|
||||||
blender_version_cycle=$(grep "BLENDER_VERSION_CYCLE\s" "$blender_srcdir/source/blender/blenkernel/BKE_blender_version.h" | awk '{print $3}')
|
|
||||||
|
|
||||||
VERSION=$(expr $blender_version / 100).$(expr $blender_version % 100).$blender_version_patch
|
|
||||||
if [ "$blender_version_cycle" = "release" ] ; then
|
|
||||||
SUBMODULE_EXCLUDE="^\(release/scripts/addons_contrib\)$"
|
|
||||||
else
|
|
||||||
VERSION=$VERSION-$blender_version_cycle
|
|
||||||
SUBMODULE_EXCLUDE="^$" # dummy regex
|
|
||||||
fi
|
|
||||||
|
|
||||||
MANIFEST="blender-$VERSION-manifest.txt"
|
|
||||||
TARBALL="blender-$VERSION.tar.xz"
|
|
||||||
|
|
||||||
cd "$blender_srcdir"
|
|
||||||
|
|
||||||
# not so nice, but works
|
|
||||||
FILTER_FILES_PY=\
|
|
||||||
"import os, sys; "\
|
|
||||||
"[print(l[:-1]) for l in sys.stdin.readlines() "\
|
|
||||||
"if os.path.isfile(l[:-1]) "\
|
|
||||||
"if os.path.basename(l[:-1]) not in {"\
|
|
||||||
"'.gitignore', "\
|
|
||||||
"'.gitmodules', "\
|
|
||||||
"'.arcconfig', "\
|
|
||||||
"}"\
|
|
||||||
"]"
|
|
||||||
|
|
||||||
# Build master list
|
|
||||||
echo -n "Building manifest of files: \"$BASE_DIR/$MANIFEST\" ..."
|
|
||||||
git ls-files | python3 -c "$FILTER_FILES_PY" > $BASE_DIR/$MANIFEST
|
|
||||||
|
|
||||||
# Enumerate submodules
|
|
||||||
for lcv in $(git submodule | awk '{print $2}' | grep -v "$SUBMODULE_EXCLUDE"); do
|
|
||||||
cd "$BASE_DIR"
|
|
||||||
cd "$blender_srcdir/$lcv"
|
|
||||||
git ls-files | python3 -c "$FILTER_FILES_PY" | awk '$0="'"$lcv"/'"$0' >> $BASE_DIR/$MANIFEST
|
|
||||||
cd "$BASE_DIR"
|
|
||||||
done
|
|
||||||
echo "OK"
|
|
||||||
|
|
||||||
|
|
||||||
# Create the tarball
|
|
||||||
#
|
|
||||||
# Without owner/group args, extracting the files as root will
|
|
||||||
# use ownership from the tar archive.
|
|
||||||
cd "$blender_srcdir"
|
|
||||||
echo -n "Creating archive: \"$BASE_DIR/$TARBALL\" ..."
|
|
||||||
tar \
|
|
||||||
--transform "s,^,blender-$VERSION/,g" \
|
|
||||||
--use-compress-program="xz -9" \
|
|
||||||
--create \
|
|
||||||
--file="$BASE_DIR/$TARBALL" \
|
|
||||||
--files-from="$BASE_DIR/$MANIFEST" \
|
|
||||||
--owner=0 \
|
|
||||||
--group=0
|
|
||||||
|
|
||||||
echo "OK"
|
|
||||||
|
|
||||||
|
|
||||||
# Create checksum file
|
|
||||||
cd "$BASE_DIR"
|
|
||||||
echo -n "Creating checksum: \"$BASE_DIR/$TARBALL.md5sum\" ..."
|
|
||||||
md5sum "$TARBALL" > "$TARBALL.md5sum"
|
|
||||||
echo "OK"
|
|
||||||
|
|
||||||
|
|
||||||
# Cleanup
|
|
||||||
echo -n "Cleaning up ..."
|
|
||||||
rm "$BASE_DIR/$MANIFEST"
|
|
||||||
echo "OK"
|
|
||||||
|
|
||||||
echo "Done!"
|
|
4
extern/mantaflow/CMakeLists.txt
vendored
4
extern/mantaflow/CMakeLists.txt
vendored
@@ -32,9 +32,7 @@ if(MSVC_CLANG AND WITH_OPENMP AND CMAKE_CXX_COMPILER_VERSION VERSION_LESS "9.0.1
|
|||||||
endif()
|
endif()
|
||||||
|
|
||||||
# Exporting functions from the blender binary gives linker warnings on Apple arm64 systems.
|
# Exporting functions from the blender binary gives linker warnings on Apple arm64 systems.
|
||||||
# For now and until Apple arm64 is officially supported, these will just be silenced here.
|
# Silence them here.
|
||||||
# TODO (sebbas): Check if official arm64 devices give linker warnings without this block.
|
|
||||||
|
|
||||||
if(APPLE AND ("${CMAKE_OSX_ARCHITECTURES}" STREQUAL "arm64"))
|
if(APPLE AND ("${CMAKE_OSX_ARCHITECTURES}" STREQUAL "arm64"))
|
||||||
if(CMAKE_COMPILER_IS_GNUCXX OR "${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang")
|
if(CMAKE_COMPILER_IS_GNUCXX OR "${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang")
|
||||||
string(APPEND CMAKE_C_FLAGS " -fvisibility=hidden")
|
string(APPEND CMAKE_C_FLAGS " -fvisibility=hidden")
|
||||||
|
@@ -1,4 +1,4 @@
|
|||||||
/*
|
/*
|
||||||
* This program is free software; you can redistribute it and/or
|
* This program is free software; you can redistribute it and/or
|
||||||
* modify it under the terms of the GNU General Public License
|
* modify it under the terms of the GNU General Public License
|
||||||
* as published by the Free Software Foundation; either version 2
|
* as published by the Free Software Foundation; either version 2
|
||||||
@@ -38,8 +38,13 @@
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if defined(_MSC_VER)
|
#if defined(_MSC_VER)
|
||||||
|
# include <Windows.h>
|
||||||
|
|
||||||
|
# include <VersionHelpers.h> /* This needs to be included after Windows.h. */
|
||||||
# include <io.h>
|
# include <io.h>
|
||||||
# include <windows.h>
|
# if !defined(ENABLE_VIRTUAL_TERMINAL_PROCESSING)
|
||||||
|
# define ENABLE_VIRTUAL_TERMINAL_PROCESSING 0x0004
|
||||||
|
# endif
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/* For printing timestamp. */
|
/* For printing timestamp. */
|
||||||
@@ -228,6 +233,9 @@ enum eCLogColor {
|
|||||||
#define COLOR_LEN (COLOR_RESET + 1)
|
#define COLOR_LEN (COLOR_RESET + 1)
|
||||||
|
|
||||||
static const char *clg_color_table[COLOR_LEN] = {NULL};
|
static const char *clg_color_table[COLOR_LEN] = {NULL};
|
||||||
|
#ifdef _WIN32
|
||||||
|
static DWORD clg_previous_console_mode = 0;
|
||||||
|
#endif
|
||||||
|
|
||||||
static void clg_color_table_init(bool use_color)
|
static void clg_color_table_init(bool use_color)
|
||||||
{
|
{
|
||||||
@@ -295,19 +303,27 @@ static enum eCLogColor clg_severity_to_color(enum CLG_Severity severity)
|
|||||||
* - `foo` exact match of `foo`.
|
* - `foo` exact match of `foo`.
|
||||||
* - `foo.bar` exact match for `foo.bar`
|
* - `foo.bar` exact match for `foo.bar`
|
||||||
* - `foo.*` match for `foo` & `foo.bar` & `foo.bar.baz`
|
* - `foo.*` match for `foo` & `foo.bar` & `foo.bar.baz`
|
||||||
|
* - `*bar*` match for `foo.bar` & `baz.bar` & `foo.barbaz`
|
||||||
* - `*` matches everything.
|
* - `*` matches everything.
|
||||||
*/
|
*/
|
||||||
static bool clg_ctx_filter_check(CLogContext *ctx, const char *identifier)
|
static bool clg_ctx_filter_check(CLogContext *ctx, const char *identifier)
|
||||||
{
|
{
|
||||||
const int identifier_len = strlen(identifier);
|
const size_t identifier_len = strlen(identifier);
|
||||||
for (uint i = 0; i < 2; i++) {
|
for (uint i = 0; i < 2; i++) {
|
||||||
const CLG_IDFilter *flt = ctx->filters[i];
|
const CLG_IDFilter *flt = ctx->filters[i];
|
||||||
while (flt != NULL) {
|
while (flt != NULL) {
|
||||||
const int len = strlen(flt->match);
|
const size_t len = strlen(flt->match);
|
||||||
if (STREQ(flt->match, "*") || ((len == identifier_len) && (STREQ(identifier, flt->match)))) {
|
if (STREQ(flt->match, "*") || ((len == identifier_len) && (STREQ(identifier, flt->match)))) {
|
||||||
return (bool)i;
|
return (bool)i;
|
||||||
}
|
}
|
||||||
if ((len >= 2) && (STREQLEN(".*", &flt->match[len - 2], 2))) {
|
if (flt->match[0] == '*' && flt->match[len - 1] == '*') {
|
||||||
|
char *match = MEM_callocN(sizeof(char) * len - 1, __func__);
|
||||||
|
memcpy(match, flt->match + 1, len - 2);
|
||||||
|
if (strstr(identifier, match) != NULL) {
|
||||||
|
return (bool)i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if ((len >= 2) && (STREQLEN(".*", &flt->match[len - 2], 2))) {
|
||||||
if (((identifier_len == len - 2) && STREQLEN(identifier, flt->match, len - 2)) ||
|
if (((identifier_len == len - 2) && STREQLEN(identifier, flt->match, len - 2)) ||
|
||||||
((identifier_len >= len - 1) && STREQLEN(identifier, flt->match, len - 1))) {
|
((identifier_len >= len - 1) && STREQLEN(identifier, flt->match, len - 1))) {
|
||||||
return (bool)i;
|
return (bool)i;
|
||||||
@@ -548,13 +564,22 @@ static void CLG_ctx_output_set(CLogContext *ctx, void *file_handle)
|
|||||||
#if defined(__unix__) || defined(__APPLE__)
|
#if defined(__unix__) || defined(__APPLE__)
|
||||||
ctx->use_color = isatty(ctx->output);
|
ctx->use_color = isatty(ctx->output);
|
||||||
#elif defined(WIN32)
|
#elif defined(WIN32)
|
||||||
/* Windows Terminal supports color like the Linux terminals do while the standard console does
|
/* As of Windows 10 build 18298 all the standard consoles supports color
|
||||||
* not, the way to tell the two apart is to look at the `WT_SESSION` environment variable which
|
* like the Linux Terminal do, but it needs to be turned on.
|
||||||
* will only be defined for Windows Terminal. */
|
* To turn on colors we need to enable virtual terminal processing by passing the flag
|
||||||
|
* ENABLE_VIRTUAL_TERMINAL_PROCESSING into SetConsoleMode.
|
||||||
|
* If the system doesn't support virtual terminal processing it will fail silently and the flag
|
||||||
|
* will not be set. */
|
||||||
|
|
||||||
/* #getenv is used here rather than #BLI_getenv since it would be a bad level call
|
GetConsoleMode(GetStdHandle(STD_OUTPUT_HANDLE), &clg_previous_console_mode);
|
||||||
* and there are no benefits for using it in this context. */
|
|
||||||
ctx->use_color = isatty(ctx->output) && getenv("WT_SESSION");
|
ctx->use_color = 0;
|
||||||
|
if (IsWindows10OrGreater() && isatty(ctx->output)) {
|
||||||
|
DWORD mode = clg_previous_console_mode | ENABLE_VIRTUAL_TERMINAL_PROCESSING;
|
||||||
|
if (SetConsoleMode(GetStdHandle(STD_OUTPUT_HANDLE), mode)) {
|
||||||
|
ctx->use_color = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -638,6 +663,9 @@ static CLogContext *CLG_ctx_init(void)
|
|||||||
|
|
||||||
static void CLG_ctx_free(CLogContext *ctx)
|
static void CLG_ctx_free(CLogContext *ctx)
|
||||||
{
|
{
|
||||||
|
#if defined(WIN32)
|
||||||
|
SetConsoleMode(GetStdHandle(STD_OUTPUT_HANDLE), clg_previous_console_mode);
|
||||||
|
#endif
|
||||||
while (ctx->types != NULL) {
|
while (ctx->types != NULL) {
|
||||||
CLG_LogType *item = ctx->types;
|
CLG_LogType *item = ctx->types;
|
||||||
ctx->types = item->next;
|
ctx->types = item->next;
|
||||||
|
@@ -226,6 +226,9 @@ def update_render_passes(self, context):
|
|||||||
view_layer = context.view_layer
|
view_layer = context.view_layer
|
||||||
view_layer.update_render_passes()
|
view_layer.update_render_passes()
|
||||||
|
|
||||||
|
def poll_object_is_camera(self, obj):
|
||||||
|
return obj.type == 'CAMERA'
|
||||||
|
|
||||||
|
|
||||||
class CyclesRenderSettings(bpy.types.PropertyGroup):
|
class CyclesRenderSettings(bpy.types.PropertyGroup):
|
||||||
|
|
||||||
@@ -538,7 +541,7 @@ class CyclesRenderSettings(bpy.types.PropertyGroup):
|
|||||||
description="Camera to use as reference point when subdividing geometry, useful to avoid crawling "
|
description="Camera to use as reference point when subdividing geometry, useful to avoid crawling "
|
||||||
"artifacts in animations when the scene camera is moving",
|
"artifacts in animations when the scene camera is moving",
|
||||||
type=bpy.types.Object,
|
type=bpy.types.Object,
|
||||||
poll=lambda self, obj: obj.type == 'CAMERA',
|
poll=poll_object_is_camera,
|
||||||
)
|
)
|
||||||
offscreen_dicing_scale: FloatProperty(
|
offscreen_dicing_scale: FloatProperty(
|
||||||
name="Offscreen Dicing Scale",
|
name="Offscreen Dicing Scale",
|
||||||
|
@@ -27,8 +27,8 @@ BVHOptiX::BVHOptiX(const BVHParams ¶ms_,
|
|||||||
Device *device)
|
Device *device)
|
||||||
: BVH(params_, geometry_, objects_),
|
: BVH(params_, geometry_, objects_),
|
||||||
traversable_handle(0),
|
traversable_handle(0),
|
||||||
as_data(device, params_.top_level ? "optix tlas" : "optix blas"),
|
as_data(device, params_.top_level ? "optix tlas" : "optix blas", false),
|
||||||
motion_transform_data(device, "optix motion transform")
|
motion_transform_data(device, "optix motion transform", false)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -854,7 +854,7 @@ CUDADevice::CUDAMem *CUDADevice::generic_alloc(device_memory &mem, size_t pitch_
|
|||||||
|
|
||||||
void *shared_pointer = 0;
|
void *shared_pointer = 0;
|
||||||
|
|
||||||
if (mem_alloc_result != CUDA_SUCCESS && can_map_host) {
|
if (mem_alloc_result != CUDA_SUCCESS && can_map_host && mem.type != MEM_DEVICE_ONLY) {
|
||||||
if (mem.shared_pointer) {
|
if (mem.shared_pointer) {
|
||||||
/* Another device already allocated host memory. */
|
/* Another device already allocated host memory. */
|
||||||
mem_alloc_result = CUDA_SUCCESS;
|
mem_alloc_result = CUDA_SUCCESS;
|
||||||
@@ -877,9 +877,15 @@ CUDADevice::CUDAMem *CUDADevice::generic_alloc(device_memory &mem, size_t pitch_
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (mem_alloc_result != CUDA_SUCCESS) {
|
if (mem_alloc_result != CUDA_SUCCESS) {
|
||||||
|
if (mem.type == MEM_DEVICE_ONLY) {
|
||||||
|
status = " failed, out of device memory";
|
||||||
|
set_error("System is out of GPU memory");
|
||||||
|
}
|
||||||
|
else {
|
||||||
status = " failed, out of device and host memory";
|
status = " failed, out of device and host memory";
|
||||||
set_error("System is out of GPU and shared host memory");
|
set_error("System is out of GPU and shared host memory");
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (mem.name) {
|
if (mem.name) {
|
||||||
VLOG(1) << "Buffer allocate: " << mem.name << ", "
|
VLOG(1) << "Buffer allocate: " << mem.name << ", "
|
||||||
|
@@ -396,8 +396,7 @@ class CPUDevice : public Device {
|
|||||||
<< string_human_readable_size(mem.memory_size()) << ")";
|
<< string_human_readable_size(mem.memory_size()) << ")";
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mem.type == MEM_DEVICE_ONLY) {
|
if (mem.type == MEM_DEVICE_ONLY || !mem.host_pointer) {
|
||||||
assert(!mem.host_pointer);
|
|
||||||
size_t alignment = MIN_ALIGNMENT_CPU_DATA_TYPES;
|
size_t alignment = MIN_ALIGNMENT_CPU_DATA_TYPES;
|
||||||
void *data = util_aligned_malloc(mem.memory_size(), alignment);
|
void *data = util_aligned_malloc(mem.memory_size(), alignment);
|
||||||
mem.device_pointer = (device_ptr)data;
|
mem.device_pointer = (device_ptr)data;
|
||||||
@@ -459,7 +458,7 @@ class CPUDevice : public Device {
|
|||||||
tex_free((device_texture &)mem);
|
tex_free((device_texture &)mem);
|
||||||
}
|
}
|
||||||
else if (mem.device_pointer) {
|
else if (mem.device_pointer) {
|
||||||
if (mem.type == MEM_DEVICE_ONLY) {
|
if (mem.type == MEM_DEVICE_ONLY || !mem.host_pointer) {
|
||||||
util_aligned_free((void *)mem.device_pointer);
|
util_aligned_free((void *)mem.device_pointer);
|
||||||
}
|
}
|
||||||
mem.device_pointer = 0;
|
mem.device_pointer = 0;
|
||||||
|
@@ -171,7 +171,8 @@ class DenoisingTask {
|
|||||||
bool gpu_temporary_mem;
|
bool gpu_temporary_mem;
|
||||||
|
|
||||||
DenoiseBuffers(Device *device)
|
DenoiseBuffers(Device *device)
|
||||||
: mem(device, "denoising pixel buffer"), temporary_mem(device, "denoising temporary mem")
|
: mem(device, "denoising pixel buffer"),
|
||||||
|
temporary_mem(device, "denoising temporary mem", true)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
} buffer;
|
} buffer;
|
||||||
|
@@ -270,8 +270,8 @@ class device_memory {
|
|||||||
|
|
||||||
template<typename T> class device_only_memory : public device_memory {
|
template<typename T> class device_only_memory : public device_memory {
|
||||||
public:
|
public:
|
||||||
device_only_memory(Device *device, const char *name)
|
device_only_memory(Device *device, const char *name, bool allow_host_memory_fallback = false)
|
||||||
: device_memory(device, name, MEM_DEVICE_ONLY)
|
: device_memory(device, name, allow_host_memory_fallback ? MEM_READ_WRITE : MEM_DEVICE_ONLY)
|
||||||
{
|
{
|
||||||
data_type = device_type_traits<T>::data_type;
|
data_type = device_type_traits<T>::data_type;
|
||||||
data_elements = max(device_type_traits<T>::num_elements, 1);
|
data_elements = max(device_type_traits<T>::num_elements, 1);
|
||||||
|
@@ -197,8 +197,8 @@ class OptiXDevice : public CUDADevice {
|
|||||||
OptiXDevice(DeviceInfo &info_, Stats &stats_, Profiler &profiler_, bool background_)
|
OptiXDevice(DeviceInfo &info_, Stats &stats_, Profiler &profiler_, bool background_)
|
||||||
: CUDADevice(info_, stats_, profiler_, background_),
|
: CUDADevice(info_, stats_, profiler_, background_),
|
||||||
sbt_data(this, "__sbt", MEM_READ_ONLY),
|
sbt_data(this, "__sbt", MEM_READ_ONLY),
|
||||||
launch_params(this, "__params"),
|
launch_params(this, "__params", false),
|
||||||
denoiser_state(this, "__denoiser_state")
|
denoiser_state(this, "__denoiser_state", true)
|
||||||
{
|
{
|
||||||
// Store number of CUDA streams in device info
|
// Store number of CUDA streams in device info
|
||||||
info.cpu_threads = DebugFlags().optix.cuda_streams;
|
info.cpu_threads = DebugFlags().optix.cuda_streams;
|
||||||
@@ -878,8 +878,8 @@ class OptiXDevice : public CUDADevice {
|
|||||||
device_ptr input_ptr = rtile.buffer + pixel_offset;
|
device_ptr input_ptr = rtile.buffer + pixel_offset;
|
||||||
|
|
||||||
// Copy tile data into a common buffer if necessary
|
// Copy tile data into a common buffer if necessary
|
||||||
device_only_memory<float> input(this, "denoiser input");
|
device_only_memory<float> input(this, "denoiser input", true);
|
||||||
device_vector<TileInfo> tile_info_mem(this, "denoiser tile info", MEM_READ_WRITE);
|
device_vector<TileInfo> tile_info_mem(this, "denoiser tile info", MEM_READ_ONLY);
|
||||||
|
|
||||||
bool contiguous_memory = true;
|
bool contiguous_memory = true;
|
||||||
for (int i = 0; i < RenderTileNeighbors::SIZE; i++) {
|
for (int i = 0; i < RenderTileNeighbors::SIZE; i++) {
|
||||||
@@ -924,7 +924,7 @@ class OptiXDevice : public CUDADevice {
|
|||||||
}
|
}
|
||||||
|
|
||||||
# if OPTIX_DENOISER_NO_PIXEL_STRIDE
|
# if OPTIX_DENOISER_NO_PIXEL_STRIDE
|
||||||
device_only_memory<float> input_rgb(this, "denoiser input rgb");
|
device_only_memory<float> input_rgb(this, "denoiser input rgb", true);
|
||||||
input_rgb.alloc_to_device(rect_size.x * rect_size.y * 3 * task.denoising.input_passes);
|
input_rgb.alloc_to_device(rect_size.x * rect_size.y * 3 * task.denoising.input_passes);
|
||||||
|
|
||||||
void *input_args[] = {&input_rgb.device_pointer,
|
void *input_args[] = {&input_rgb.device_pointer,
|
||||||
@@ -1146,6 +1146,13 @@ class OptiXDevice : public CUDADevice {
|
|||||||
const OptixBuildInput &build_input,
|
const OptixBuildInput &build_input,
|
||||||
uint16_t num_motion_steps)
|
uint16_t num_motion_steps)
|
||||||
{
|
{
|
||||||
|
/* Allocate and build acceleration structures only one at a time, to prevent parallel builds
|
||||||
|
* from running out of memory (since both original and compacted acceleration structure memory
|
||||||
|
* may be allocated at the same time for the duration of this function). The builds would
|
||||||
|
* otherwise happen on the same CUDA stream anyway. */
|
||||||
|
static thread_mutex mutex;
|
||||||
|
thread_scoped_lock lock(mutex);
|
||||||
|
|
||||||
const CUDAContextScope scope(cuContext);
|
const CUDAContextScope scope(cuContext);
|
||||||
|
|
||||||
// Compute memory usage
|
// Compute memory usage
|
||||||
@@ -1170,11 +1177,12 @@ class OptiXDevice : public CUDADevice {
|
|||||||
optixAccelComputeMemoryUsage(context, &options, &build_input, 1, &sizes));
|
optixAccelComputeMemoryUsage(context, &options, &build_input, 1, &sizes));
|
||||||
|
|
||||||
// Allocate required output buffers
|
// Allocate required output buffers
|
||||||
device_only_memory<char> temp_mem(this, "optix temp as build mem");
|
device_only_memory<char> temp_mem(this, "optix temp as build mem", true);
|
||||||
temp_mem.alloc_to_device(align_up(sizes.tempSizeInBytes, 8) + 8);
|
temp_mem.alloc_to_device(align_up(sizes.tempSizeInBytes, 8) + 8);
|
||||||
if (!temp_mem.device_pointer)
|
if (!temp_mem.device_pointer)
|
||||||
return false; // Make sure temporary memory allocation succeeded
|
return false; // Make sure temporary memory allocation succeeded
|
||||||
|
|
||||||
|
// Acceleration structure memory has to be allocated on the device (not allowed to be on host)
|
||||||
device_only_memory<char> &out_data = bvh->as_data;
|
device_only_memory<char> &out_data = bvh->as_data;
|
||||||
if (operation == OPTIX_BUILD_OPERATION_BUILD) {
|
if (operation == OPTIX_BUILD_OPERATION_BUILD) {
|
||||||
assert(out_data.device == this);
|
assert(out_data.device == this);
|
||||||
@@ -1222,7 +1230,7 @@ class OptiXDevice : public CUDADevice {
|
|||||||
|
|
||||||
// There is no point compacting if the size does not change
|
// There is no point compacting if the size does not change
|
||||||
if (compacted_size < sizes.outputSizeInBytes) {
|
if (compacted_size < sizes.outputSizeInBytes) {
|
||||||
device_only_memory<char> compacted_data(this, "optix compacted as");
|
device_only_memory<char> compacted_data(this, "optix compacted as", false);
|
||||||
compacted_data.alloc_to_device(compacted_size);
|
compacted_data.alloc_to_device(compacted_size);
|
||||||
if (!compacted_data.device_pointer)
|
if (!compacted_data.device_pointer)
|
||||||
// Do not compact if memory allocation for compacted acceleration structure fails
|
// Do not compact if memory allocation for compacted acceleration structure fails
|
||||||
@@ -1242,6 +1250,7 @@ class OptiXDevice : public CUDADevice {
|
|||||||
|
|
||||||
std::swap(out_data.device_size, compacted_data.device_size);
|
std::swap(out_data.device_size, compacted_data.device_size);
|
||||||
std::swap(out_data.device_pointer, compacted_data.device_pointer);
|
std::swap(out_data.device_pointer, compacted_data.device_pointer);
|
||||||
|
// Original acceleration structure memory is freed when 'compacted_data' goes out of scope
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -68,8 +68,8 @@ ccl_device T kernel_tex_image_interp_bicubic(const TextureInfo &info, float x, f
|
|||||||
x = (x * info.width) - 0.5f;
|
x = (x * info.width) - 0.5f;
|
||||||
y = (y * info.height) - 0.5f;
|
y = (y * info.height) - 0.5f;
|
||||||
|
|
||||||
float px = floor(x);
|
float px = floorf(x);
|
||||||
float py = floor(y);
|
float py = floorf(y);
|
||||||
float fx = x - px;
|
float fx = x - px;
|
||||||
float fy = y - py;
|
float fy = y - py;
|
||||||
|
|
||||||
@@ -95,9 +95,9 @@ ccl_device T kernel_tex_image_interp_tricubic(const TextureInfo &info, float x,
|
|||||||
y = (y * info.height) - 0.5f;
|
y = (y * info.height) - 0.5f;
|
||||||
z = (z * info.depth) - 0.5f;
|
z = (z * info.depth) - 0.5f;
|
||||||
|
|
||||||
float px = floor(x);
|
float px = floorf(x);
|
||||||
float py = floor(y);
|
float py = floorf(y);
|
||||||
float pz = floor(z);
|
float pz = floorf(z);
|
||||||
float fx = x - px;
|
float fx = x - px;
|
||||||
float fy = y - py;
|
float fy = y - py;
|
||||||
float fz = z - pz;
|
float fz = z - pz;
|
||||||
@@ -127,9 +127,9 @@ ccl_device T kernel_tex_image_interp_tricubic(const TextureInfo &info, float x,
|
|||||||
template<typename T, typename S>
|
template<typename T, typename S>
|
||||||
ccl_device T kernel_tex_image_interp_tricubic_nanovdb(S &s, float x, float y, float z)
|
ccl_device T kernel_tex_image_interp_tricubic_nanovdb(S &s, float x, float y, float z)
|
||||||
{
|
{
|
||||||
float px = floor(x);
|
float px = floorf(x);
|
||||||
float py = floor(y);
|
float py = floorf(y);
|
||||||
float pz = floor(z);
|
float pz = floorf(z);
|
||||||
float fx = x - px;
|
float fx = x - px;
|
||||||
float fy = y - py;
|
float fy = y - py;
|
||||||
float fz = z - pz;
|
float fz = z - pz;
|
||||||
|
@@ -402,16 +402,16 @@ static void add_uvs(AlembicProcedural *proc,
|
|||||||
}
|
}
|
||||||
|
|
||||||
const ISampleSelector iss = ISampleSelector(time);
|
const ISampleSelector iss = ISampleSelector(time);
|
||||||
const IV2fGeomParam::Sample sample = uvs.getExpandedValue(iss);
|
|
||||||
|
|
||||||
const IV2fGeomParam::Sample uvsample = uvs.getIndexedValue(iss);
|
const IV2fGeomParam::Sample uvsample = uvs.getIndexedValue(iss);
|
||||||
|
|
||||||
if (!uvsample.valid()) {
|
if (!uvsample.valid()) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
const array<int3> *triangles = cached_data.triangles.data_for_time_no_check(time);
|
const array<int3> *triangles =
|
||||||
const array<int3> *triangles_loops = cached_data.triangles_loops.data_for_time_no_check(time);
|
cached_data.triangles.data_for_time_no_check(time).get_data_or_null();
|
||||||
|
const array<int3> *triangles_loops =
|
||||||
|
cached_data.triangles_loops.data_for_time_no_check(time).get_data_or_null();
|
||||||
|
|
||||||
if (!triangles || !triangles_loops) {
|
if (!triangles || !triangles_loops) {
|
||||||
continue;
|
continue;
|
||||||
@@ -458,7 +458,8 @@ static void add_normals(const Int32ArraySamplePtr face_indices,
|
|||||||
*normals.getTimeSampling());
|
*normals.getTimeSampling());
|
||||||
attr.std = ATTR_STD_VERTEX_NORMAL;
|
attr.std = ATTR_STD_VERTEX_NORMAL;
|
||||||
|
|
||||||
const array<float3> *vertices = cached_data.vertices.data_for_time_no_check(time);
|
const array<float3> *vertices =
|
||||||
|
cached_data.vertices.data_for_time_no_check(time).get_data_or_null();
|
||||||
|
|
||||||
if (!vertices) {
|
if (!vertices) {
|
||||||
return;
|
return;
|
||||||
@@ -493,7 +494,8 @@ static void add_normals(const Int32ArraySamplePtr face_indices,
|
|||||||
*normals.getTimeSampling());
|
*normals.getTimeSampling());
|
||||||
attr.std = ATTR_STD_VERTEX_NORMAL;
|
attr.std = ATTR_STD_VERTEX_NORMAL;
|
||||||
|
|
||||||
const array<float3> *vertices = cached_data.vertices.data_for_time_no_check(time);
|
const array<float3> *vertices =
|
||||||
|
cached_data.vertices.data_for_time_no_check(time).get_data_or_null();
|
||||||
|
|
||||||
if (!vertices) {
|
if (!vertices) {
|
||||||
return;
|
return;
|
||||||
@@ -717,11 +719,15 @@ void AlembicObject::read_face_sets(SchemaType &schema,
|
|||||||
|
|
||||||
void AlembicObject::load_all_data(AlembicProcedural *proc,
|
void AlembicObject::load_all_data(AlembicProcedural *proc,
|
||||||
IPolyMeshSchema &schema,
|
IPolyMeshSchema &schema,
|
||||||
float scale,
|
|
||||||
Progress &progress)
|
Progress &progress)
|
||||||
{
|
{
|
||||||
cached_data.clear();
|
cached_data.clear();
|
||||||
|
|
||||||
|
/* Only load data for the original Geometry. */
|
||||||
|
if (instance_of) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const TimeSamplingPtr time_sampling = schema.getTimeSampling();
|
const TimeSamplingPtr time_sampling = schema.getTimeSampling();
|
||||||
cached_data.set_time_sampling(*time_sampling);
|
cached_data.set_time_sampling(*time_sampling);
|
||||||
|
|
||||||
@@ -780,22 +786,18 @@ void AlembicObject::load_all_data(AlembicProcedural *proc,
|
|||||||
add_uvs(proc, uvs, cached_data, progress);
|
add_uvs(proc, uvs, cached_data, progress);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (progress.get_cancel()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
setup_transform_cache(scale);
|
|
||||||
|
|
||||||
data_loaded = true;
|
data_loaded = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void AlembicObject::load_all_data(AlembicProcedural *proc,
|
void AlembicObject::load_all_data(AlembicProcedural *proc, ISubDSchema &schema, Progress &progress)
|
||||||
ISubDSchema &schema,
|
|
||||||
float scale,
|
|
||||||
Progress &progress)
|
|
||||||
{
|
{
|
||||||
cached_data.clear();
|
cached_data.clear();
|
||||||
|
|
||||||
|
/* Only load data for the original Geometry. */
|
||||||
|
if (instance_of) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
AttributeRequestSet requested_attributes = get_requested_attributes();
|
AttributeRequestSet requested_attributes = get_requested_attributes();
|
||||||
|
|
||||||
const TimeSamplingPtr time_sampling = schema.getTimeSampling();
|
const TimeSamplingPtr time_sampling = schema.getTimeSampling();
|
||||||
@@ -920,19 +922,21 @@ void AlembicObject::load_all_data(AlembicProcedural *proc,
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
setup_transform_cache(scale);
|
|
||||||
|
|
||||||
data_loaded = true;
|
data_loaded = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void AlembicObject::load_all_data(AlembicProcedural *proc,
|
void AlembicObject::load_all_data(AlembicProcedural *proc,
|
||||||
const ICurvesSchema &schema,
|
const ICurvesSchema &schema,
|
||||||
float scale,
|
|
||||||
Progress &progress,
|
Progress &progress,
|
||||||
float default_radius)
|
float default_radius)
|
||||||
{
|
{
|
||||||
cached_data.clear();
|
cached_data.clear();
|
||||||
|
|
||||||
|
/* Only load data for the original Geometry. */
|
||||||
|
if (instance_of) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const TimeSamplingPtr time_sampling = schema.getTimeSampling();
|
const TimeSamplingPtr time_sampling = schema.getTimeSampling();
|
||||||
cached_data.set_time_sampling(*time_sampling);
|
cached_data.set_time_sampling(*time_sampling);
|
||||||
|
|
||||||
@@ -1007,8 +1011,6 @@ void AlembicObject::load_all_data(AlembicProcedural *proc,
|
|||||||
|
|
||||||
// TODO(@kevindietrich): attributes, need example files
|
// TODO(@kevindietrich): attributes, need example files
|
||||||
|
|
||||||
setup_transform_cache(scale);
|
|
||||||
|
|
||||||
data_loaded = true;
|
data_loaded = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1017,6 +1019,14 @@ void AlembicObject::setup_transform_cache(float scale)
|
|||||||
cached_data.transforms.clear();
|
cached_data.transforms.clear();
|
||||||
cached_data.transforms.invalidate_last_loaded_time();
|
cached_data.transforms.invalidate_last_loaded_time();
|
||||||
|
|
||||||
|
if (scale == 0.0f) {
|
||||||
|
scale = 1.0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (xform_time_sampling) {
|
||||||
|
cached_data.transforms.set_time_sampling(*xform_time_sampling);
|
||||||
|
}
|
||||||
|
|
||||||
if (xform_samples.size() == 0) {
|
if (xform_samples.size() == 0) {
|
||||||
Transform tfm = transform_scale(make_float3(scale));
|
Transform tfm = transform_scale(make_float3(scale));
|
||||||
cached_data.transforms.add_data(tfm, 0.0);
|
cached_data.transforms.add_data(tfm, 0.0);
|
||||||
@@ -1103,9 +1113,10 @@ void AlembicObject::read_attribute(const ICompoundProperty &arb_geom_params,
|
|||||||
attribute.element = ATTR_ELEMENT_CORNER;
|
attribute.element = ATTR_ELEMENT_CORNER;
|
||||||
attribute.type_desc = TypeFloat2;
|
attribute.type_desc = TypeFloat2;
|
||||||
|
|
||||||
const array<int3> *triangles = cached_data.triangles.data_for_time_no_check(time);
|
const array<int3> *triangles =
|
||||||
const array<int3> *triangles_loops = cached_data.triangles_loops.data_for_time_no_check(
|
cached_data.triangles.data_for_time_no_check(time).get_data_or_null();
|
||||||
time);
|
const array<int3> *triangles_loops =
|
||||||
|
cached_data.triangles_loops.data_for_time_no_check(time).get_data_or_null();
|
||||||
|
|
||||||
if (!triangles || !triangles_loops) {
|
if (!triangles || !triangles_loops) {
|
||||||
return;
|
return;
|
||||||
@@ -1158,7 +1169,8 @@ void AlembicObject::read_attribute(const ICompoundProperty &arb_geom_params,
|
|||||||
attribute.element = ATTR_ELEMENT_CORNER_BYTE;
|
attribute.element = ATTR_ELEMENT_CORNER_BYTE;
|
||||||
attribute.type_desc = TypeRGBA;
|
attribute.type_desc = TypeRGBA;
|
||||||
|
|
||||||
const array<int3> *triangles = cached_data.triangles.data_for_time_no_check(time);
|
const array<int3> *triangles =
|
||||||
|
cached_data.triangles.data_for_time_no_check(time).get_data_or_null();
|
||||||
|
|
||||||
if (!triangles) {
|
if (!triangles) {
|
||||||
return;
|
return;
|
||||||
@@ -1214,7 +1226,8 @@ void AlembicObject::read_attribute(const ICompoundProperty &arb_geom_params,
|
|||||||
attribute.element = ATTR_ELEMENT_CORNER_BYTE;
|
attribute.element = ATTR_ELEMENT_CORNER_BYTE;
|
||||||
attribute.type_desc = TypeRGBA;
|
attribute.type_desc = TypeRGBA;
|
||||||
|
|
||||||
const array<int3> *triangles = cached_data.triangles.data_for_time_no_check(time);
|
const array<int3> *triangles =
|
||||||
|
cached_data.triangles.data_for_time_no_check(time).get_data_or_null();
|
||||||
|
|
||||||
if (!triangles) {
|
if (!triangles) {
|
||||||
return;
|
return;
|
||||||
@@ -1253,7 +1266,7 @@ static void update_attributes(AttributeSet &attributes, CachedData &cached_data,
|
|||||||
set<Attribute *> cached_attributes;
|
set<Attribute *> cached_attributes;
|
||||||
|
|
||||||
for (CachedData::CachedAttribute &attribute : cached_data.attributes) {
|
for (CachedData::CachedAttribute &attribute : cached_data.attributes) {
|
||||||
const array<char> *attr_data = attribute.data.data_for_time(frame_time);
|
const array<char> *attr_data = attribute.data.data_for_time(frame_time).get_data_or_null();
|
||||||
|
|
||||||
Attribute *attr = nullptr;
|
Attribute *attr = nullptr;
|
||||||
if (attribute.std != ATTR_STD_NONE) {
|
if (attribute.std != ATTR_STD_NONE) {
|
||||||
@@ -1278,6 +1291,7 @@ static void update_attributes(AttributeSet &attributes, CachedData &cached_data,
|
|||||||
}
|
}
|
||||||
|
|
||||||
memcpy(attr->data(), attr_data->data(), attr_data->size());
|
memcpy(attr->data(), attr_data->data(), attr_data->size());
|
||||||
|
attr->modified = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* remove any attributes not in cached_attributes */
|
/* remove any attributes not in cached_attributes */
|
||||||
@@ -1285,6 +1299,7 @@ static void update_attributes(AttributeSet &attributes, CachedData &cached_data,
|
|||||||
for (it = attributes.attributes.begin(); it != attributes.attributes.end();) {
|
for (it = attributes.attributes.begin(); it != attributes.attributes.end();) {
|
||||||
if (cached_attributes.find(&(*it)) == cached_attributes.end()) {
|
if (cached_attributes.find(&(*it)) == cached_attributes.end()) {
|
||||||
attributes.attributes.erase(it++);
|
attributes.attributes.erase(it++);
|
||||||
|
attributes.modified = true;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1358,11 +1373,16 @@ void AlembicProcedural::generate(Scene *scene, Progress &progress)
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool need_shader_updates = false;
|
bool need_shader_updates = false;
|
||||||
|
bool need_data_updates = false;
|
||||||
|
|
||||||
/* Check for changes in shaders (newly requested attributes). */
|
|
||||||
foreach (Node *object_node, objects) {
|
foreach (Node *object_node, objects) {
|
||||||
AlembicObject *object = static_cast<AlembicObject *>(object_node);
|
AlembicObject *object = static_cast<AlembicObject *>(object_node);
|
||||||
|
|
||||||
|
if (object->is_modified()) {
|
||||||
|
need_data_updates = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Check for changes in shaders (e.g. newly requested attributes). */
|
||||||
foreach (Node *shader_node, object->get_used_shaders()) {
|
foreach (Node *shader_node, object->get_used_shaders()) {
|
||||||
Shader *shader = static_cast<Shader *>(shader_node);
|
Shader *shader = static_cast<Shader *>(shader_node);
|
||||||
|
|
||||||
@@ -1373,7 +1393,7 @@ void AlembicProcedural::generate(Scene *scene, Progress &progress)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!is_modified() && !need_shader_updates) {
|
if (!is_modified() && !need_shader_updates && !need_data_updates) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1397,6 +1417,8 @@ void AlembicProcedural::generate(Scene *scene, Progress &progress)
|
|||||||
|
|
||||||
const chrono_t frame_time = (chrono_t)((frame - frame_offset) / frame_rate);
|
const chrono_t frame_time = (chrono_t)((frame - frame_offset) / frame_rate);
|
||||||
|
|
||||||
|
build_caches(progress);
|
||||||
|
|
||||||
foreach (Node *node, objects) {
|
foreach (Node *node, objects) {
|
||||||
AlembicObject *object = static_cast<AlembicObject *>(node);
|
AlembicObject *object = static_cast<AlembicObject *>(node);
|
||||||
|
|
||||||
@@ -1405,19 +1427,19 @@ void AlembicProcedural::generate(Scene *scene, Progress &progress)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* skip constant objects */
|
/* skip constant objects */
|
||||||
if (object->has_data_loaded() && object->is_constant() && !object->is_modified() &&
|
if (object->is_constant() && !object->is_modified() && !object->need_shader_update &&
|
||||||
!object->need_shader_update && !scale_is_modified()) {
|
!scale_is_modified()) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (object->schema_type == AlembicObject::POLY_MESH) {
|
if (object->schema_type == AlembicObject::POLY_MESH) {
|
||||||
read_mesh(scene, object, frame_time, progress);
|
read_mesh(object, frame_time);
|
||||||
}
|
}
|
||||||
else if (object->schema_type == AlembicObject::CURVES) {
|
else if (object->schema_type == AlembicObject::CURVES) {
|
||||||
read_curves(scene, object, frame_time, progress);
|
read_curves(object, frame_time);
|
||||||
}
|
}
|
||||||
else if (object->schema_type == AlembicObject::SUBD) {
|
else if (object->schema_type == AlembicObject::SUBD) {
|
||||||
read_subd(scene, object, frame_time, progress);
|
read_subd(object, frame_time);
|
||||||
}
|
}
|
||||||
|
|
||||||
object->clear_modified();
|
object->clear_modified();
|
||||||
@@ -1471,7 +1493,7 @@ void AlembicProcedural::load_objects(Progress &progress)
|
|||||||
IObject root = archive.getTop();
|
IObject root = archive.getTop();
|
||||||
|
|
||||||
for (size_t i = 0; i < root.getNumChildren(); ++i) {
|
for (size_t i = 0; i < root.getNumChildren(); ++i) {
|
||||||
walk_hierarchy(root, root.getChildHeader(i), nullptr, object_map, progress);
|
walk_hierarchy(root, root.getChildHeader(i), {}, object_map, progress);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Create nodes in the scene. */
|
/* Create nodes in the scene. */
|
||||||
@@ -1480,6 +1502,7 @@ void AlembicProcedural::load_objects(Progress &progress)
|
|||||||
|
|
||||||
Geometry *geometry = nullptr;
|
Geometry *geometry = nullptr;
|
||||||
|
|
||||||
|
if (!abc_object->instance_of) {
|
||||||
if (abc_object->schema_type == AlembicObject::CURVES) {
|
if (abc_object->schema_type == AlembicObject::CURVES) {
|
||||||
geometry = scene_->create_node<Hair>();
|
geometry = scene_->create_node<Hair>();
|
||||||
}
|
}
|
||||||
@@ -1496,6 +1519,7 @@ void AlembicProcedural::load_objects(Progress &progress)
|
|||||||
|
|
||||||
array<Node *> used_shaders = abc_object->get_used_shaders();
|
array<Node *> used_shaders = abc_object->get_used_shaders();
|
||||||
geometry->set_used_shaders(used_shaders);
|
geometry->set_used_shaders(used_shaders);
|
||||||
|
}
|
||||||
|
|
||||||
Object *object = scene_->create_node<Object>();
|
Object *object = scene_->create_node<Object>();
|
||||||
object->set_owner(this);
|
object->set_owner(this);
|
||||||
@@ -1504,43 +1528,44 @@ void AlembicProcedural::load_objects(Progress &progress)
|
|||||||
|
|
||||||
abc_object->set_object(object);
|
abc_object->set_object(object);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Share geometries between instances. */
|
||||||
|
foreach (Node *node, objects) {
|
||||||
|
AlembicObject *abc_object = static_cast<AlembicObject *>(node);
|
||||||
|
|
||||||
|
if (abc_object->instance_of) {
|
||||||
|
abc_object->get_object()->set_geometry(
|
||||||
|
abc_object->instance_of->get_object()->get_geometry());
|
||||||
|
abc_object->schema_type = abc_object->instance_of->schema_type;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void AlembicProcedural::read_mesh(Scene *scene,
|
void AlembicProcedural::read_mesh(AlembicObject *abc_object, Abc::chrono_t frame_time)
|
||||||
AlembicObject *abc_object,
|
|
||||||
Abc::chrono_t frame_time,
|
|
||||||
Progress &progress)
|
|
||||||
{
|
{
|
||||||
IPolyMesh polymesh(abc_object->iobject, Alembic::Abc::kWrapExisting);
|
|
||||||
|
|
||||||
Mesh *mesh = static_cast<Mesh *>(abc_object->get_object()->get_geometry());
|
|
||||||
|
|
||||||
CachedData &cached_data = abc_object->get_cached_data();
|
CachedData &cached_data = abc_object->get_cached_data();
|
||||||
IPolyMeshSchema schema = polymesh.getSchema();
|
|
||||||
|
|
||||||
if (!abc_object->has_data_loaded()) {
|
|
||||||
abc_object->load_all_data(this, schema, scale, progress);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
if (abc_object->need_shader_update) {
|
|
||||||
abc_object->update_shader_attributes(schema.getArbGeomParams(), progress);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (scale_is_modified()) {
|
|
||||||
abc_object->setup_transform_cache(scale);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* update sockets */
|
/* update sockets */
|
||||||
|
|
||||||
Object *object = abc_object->get_object();
|
Object *object = abc_object->get_object();
|
||||||
cached_data.transforms.copy_to_socket(frame_time, object, object->get_tfm_socket());
|
cached_data.transforms.copy_to_socket(frame_time, object, object->get_tfm_socket());
|
||||||
|
|
||||||
|
if (object->is_modified()) {
|
||||||
|
object->tag_update(scene_);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Only update sockets for the original Geometry. */
|
||||||
|
if (abc_object->instance_of) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Mesh *mesh = static_cast<Mesh *>(object->get_geometry());
|
||||||
|
|
||||||
cached_data.vertices.copy_to_socket(frame_time, mesh, mesh->get_verts_socket());
|
cached_data.vertices.copy_to_socket(frame_time, mesh, mesh->get_verts_socket());
|
||||||
|
|
||||||
cached_data.shader.copy_to_socket(frame_time, mesh, mesh->get_shader_socket());
|
cached_data.shader.copy_to_socket(frame_time, mesh, mesh->get_shader_socket());
|
||||||
|
|
||||||
array<int3> *triangle_data = cached_data.triangles.data_for_time(frame_time);
|
array<int3> *triangle_data = cached_data.triangles.data_for_time(frame_time).get_data_or_null();
|
||||||
if (triangle_data) {
|
if (triangle_data) {
|
||||||
array<int> triangles;
|
array<int> triangles;
|
||||||
array<bool> smooth;
|
array<bool> smooth;
|
||||||
@@ -1566,7 +1591,7 @@ void AlembicProcedural::read_mesh(Scene *scene,
|
|||||||
|
|
||||||
/* we don't yet support arbitrary attributes, for now add vertex
|
/* we don't yet support arbitrary attributes, for now add vertex
|
||||||
* coordinates as generated coordinates if requested */
|
* coordinates as generated coordinates if requested */
|
||||||
if (mesh->need_attribute(scene, ATTR_STD_GENERATED)) {
|
if (mesh->need_attribute(scene_, ATTR_STD_GENERATED)) {
|
||||||
Attribute *attr = mesh->attributes.add(ATTR_STD_GENERATED);
|
Attribute *attr = mesh->attributes.add(ATTR_STD_GENERATED);
|
||||||
memcpy(
|
memcpy(
|
||||||
attr->data_float3(), mesh->get_verts().data(), sizeof(float3) * mesh->get_verts().size());
|
attr->data_float3(), mesh->get_verts().data(), sizeof(float3) * mesh->get_verts().size());
|
||||||
@@ -1574,39 +1599,12 @@ void AlembicProcedural::read_mesh(Scene *scene,
|
|||||||
|
|
||||||
if (mesh->is_modified()) {
|
if (mesh->is_modified()) {
|
||||||
bool need_rebuild = mesh->triangles_is_modified();
|
bool need_rebuild = mesh->triangles_is_modified();
|
||||||
mesh->tag_update(scene, need_rebuild);
|
mesh->tag_update(scene_, need_rebuild);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void AlembicProcedural::read_subd(Scene *scene,
|
void AlembicProcedural::read_subd(AlembicObject *abc_object, Abc::chrono_t frame_time)
|
||||||
AlembicObject *abc_object,
|
|
||||||
Abc::chrono_t frame_time,
|
|
||||||
Progress &progress)
|
|
||||||
{
|
{
|
||||||
ISubD subd_mesh(abc_object->iobject, Alembic::Abc::kWrapExisting);
|
|
||||||
ISubDSchema schema = subd_mesh.getSchema();
|
|
||||||
|
|
||||||
Mesh *mesh = static_cast<Mesh *>(abc_object->get_object()->get_geometry());
|
|
||||||
|
|
||||||
/* Alembic is OpenSubDiv compliant, there is no option to set another subdivision type. */
|
|
||||||
mesh->set_subdivision_type(Mesh::SubdivisionType::SUBDIVISION_CATMULL_CLARK);
|
|
||||||
|
|
||||||
if (!abc_object->has_data_loaded()) {
|
|
||||||
abc_object->load_all_data(this, schema, scale, progress);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
if (abc_object->need_shader_update) {
|
|
||||||
abc_object->update_shader_attributes(schema.getArbGeomParams(), progress);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (scale_is_modified()) {
|
|
||||||
abc_object->setup_transform_cache(scale);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
mesh->set_subd_max_level(abc_object->get_subd_max_level());
|
|
||||||
mesh->set_subd_dicing_rate(abc_object->get_subd_dicing_rate());
|
|
||||||
|
|
||||||
CachedData &cached_data = abc_object->get_cached_data();
|
CachedData &cached_data = abc_object->get_cached_data();
|
||||||
|
|
||||||
if (abc_object->subd_max_level_is_modified() || abc_object->subd_dicing_rate_is_modified()) {
|
if (abc_object->subd_max_level_is_modified() || abc_object->subd_dicing_rate_is_modified()) {
|
||||||
@@ -1614,6 +1612,22 @@ void AlembicProcedural::read_subd(Scene *scene,
|
|||||||
cached_data.invalidate_last_loaded_time();
|
cached_data.invalidate_last_loaded_time();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Update sockets. */
|
||||||
|
|
||||||
|
Object *object = abc_object->get_object();
|
||||||
|
cached_data.transforms.copy_to_socket(frame_time, object, object->get_tfm_socket());
|
||||||
|
|
||||||
|
if (object->is_modified()) {
|
||||||
|
object->tag_update(scene_);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Only update sockets for the original Geometry. */
|
||||||
|
if (abc_object->instance_of) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Mesh *mesh = static_cast<Mesh *>(object->get_geometry());
|
||||||
|
|
||||||
/* Cycles overwrites the original triangles when computing displacement, so we always have to
|
/* Cycles overwrites the original triangles when computing displacement, so we always have to
|
||||||
* repass the data if something is animated (vertices most likely) to avoid buffer overflows. */
|
* repass the data if something is animated (vertices most likely) to avoid buffer overflows. */
|
||||||
if (!cached_data.is_constant()) {
|
if (!cached_data.is_constant()) {
|
||||||
@@ -1626,10 +1640,10 @@ void AlembicProcedural::read_subd(Scene *scene,
|
|||||||
|
|
||||||
mesh->clear_non_sockets();
|
mesh->clear_non_sockets();
|
||||||
|
|
||||||
/* Update sockets. */
|
/* Alembic is OpenSubDiv compliant, there is no option to set another subdivision type. */
|
||||||
|
mesh->set_subdivision_type(Mesh::SubdivisionType::SUBDIVISION_CATMULL_CLARK);
|
||||||
Object *object = abc_object->get_object();
|
mesh->set_subd_max_level(abc_object->get_subd_max_level());
|
||||||
cached_data.transforms.copy_to_socket(frame_time, object, object->get_tfm_socket());
|
mesh->set_subd_dicing_rate(abc_object->get_subd_dicing_rate());
|
||||||
|
|
||||||
cached_data.vertices.copy_to_socket(frame_time, mesh, mesh->get_verts_socket());
|
cached_data.vertices.copy_to_socket(frame_time, mesh, mesh->get_verts_socket());
|
||||||
|
|
||||||
@@ -1666,7 +1680,7 @@ void AlembicProcedural::read_subd(Scene *scene,
|
|||||||
|
|
||||||
/* we don't yet support arbitrary attributes, for now add vertex
|
/* we don't yet support arbitrary attributes, for now add vertex
|
||||||
* coordinates as generated coordinates if requested */
|
* coordinates as generated coordinates if requested */
|
||||||
if (mesh->need_attribute(scene, ATTR_STD_GENERATED)) {
|
if (mesh->need_attribute(scene_, ATTR_STD_GENERATED)) {
|
||||||
Attribute *attr = mesh->attributes.add(ATTR_STD_GENERATED);
|
Attribute *attr = mesh->attributes.add(ATTR_STD_GENERATED);
|
||||||
memcpy(
|
memcpy(
|
||||||
attr->data_float3(), mesh->get_verts().data(), sizeof(float3) * mesh->get_verts().size());
|
attr->data_float3(), mesh->get_verts().data(), sizeof(float3) * mesh->get_verts().size());
|
||||||
@@ -1680,30 +1694,12 @@ void AlembicProcedural::read_subd(Scene *scene,
|
|||||||
(mesh->subd_start_corner_is_modified()) ||
|
(mesh->subd_start_corner_is_modified()) ||
|
||||||
(mesh->subd_face_corners_is_modified());
|
(mesh->subd_face_corners_is_modified());
|
||||||
|
|
||||||
mesh->tag_update(scene, need_rebuild);
|
mesh->tag_update(scene_, need_rebuild);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void AlembicProcedural::read_curves(Scene *scene,
|
void AlembicProcedural::read_curves(AlembicObject *abc_object, Abc::chrono_t frame_time)
|
||||||
AlembicObject *abc_object,
|
|
||||||
Abc::chrono_t frame_time,
|
|
||||||
Progress &progress)
|
|
||||||
{
|
{
|
||||||
ICurves curves(abc_object->iobject, Alembic::Abc::kWrapExisting);
|
|
||||||
Hair *hair = static_cast<Hair *>(abc_object->get_object()->get_geometry());
|
|
||||||
|
|
||||||
ICurvesSchema schema = curves.getSchema();
|
|
||||||
|
|
||||||
if (!abc_object->has_data_loaded() || default_radius_is_modified() ||
|
|
||||||
abc_object->radius_scale_is_modified()) {
|
|
||||||
abc_object->load_all_data(this, schema, scale, progress, default_radius);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
if (scale_is_modified()) {
|
|
||||||
abc_object->setup_transform_cache(scale);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
CachedData &cached_data = abc_object->get_cached_data();
|
CachedData &cached_data = abc_object->get_cached_data();
|
||||||
|
|
||||||
/* update sockets */
|
/* update sockets */
|
||||||
@@ -1711,6 +1707,17 @@ void AlembicProcedural::read_curves(Scene *scene,
|
|||||||
Object *object = abc_object->get_object();
|
Object *object = abc_object->get_object();
|
||||||
cached_data.transforms.copy_to_socket(frame_time, object, object->get_tfm_socket());
|
cached_data.transforms.copy_to_socket(frame_time, object, object->get_tfm_socket());
|
||||||
|
|
||||||
|
if (object->is_modified()) {
|
||||||
|
object->tag_update(scene_);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Only update sockets for the original Geometry. */
|
||||||
|
if (abc_object->instance_of) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Hair *hair = static_cast<Hair *>(object->get_geometry());
|
||||||
|
|
||||||
cached_data.curve_keys.copy_to_socket(frame_time, hair, hair->get_curve_keys_socket());
|
cached_data.curve_keys.copy_to_socket(frame_time, hair, hair->get_curve_keys_socket());
|
||||||
|
|
||||||
cached_data.curve_radius.copy_to_socket(frame_time, hair, hair->get_curve_radius_socket());
|
cached_data.curve_radius.copy_to_socket(frame_time, hair, hair->get_curve_radius_socket());
|
||||||
@@ -1725,7 +1732,7 @@ void AlembicProcedural::read_curves(Scene *scene,
|
|||||||
|
|
||||||
/* we don't yet support arbitrary attributes, for now add first keys as generated coordinates if
|
/* we don't yet support arbitrary attributes, for now add first keys as generated coordinates if
|
||||||
* requested */
|
* requested */
|
||||||
if (hair->need_attribute(scene, ATTR_STD_GENERATED)) {
|
if (hair->need_attribute(scene_, ATTR_STD_GENERATED)) {
|
||||||
Attribute *attr_generated = hair->attributes.add(ATTR_STD_GENERATED);
|
Attribute *attr_generated = hair->attributes.add(ATTR_STD_GENERATED);
|
||||||
float3 *generated = attr_generated->data_float3();
|
float3 *generated = attr_generated->data_float3();
|
||||||
|
|
||||||
@@ -1735,13 +1742,13 @@ void AlembicProcedural::read_curves(Scene *scene,
|
|||||||
}
|
}
|
||||||
|
|
||||||
const bool rebuild = (hair->curve_keys_is_modified() || hair->curve_radius_is_modified());
|
const bool rebuild = (hair->curve_keys_is_modified() || hair->curve_radius_is_modified());
|
||||||
hair->tag_update(scene, rebuild);
|
hair->tag_update(scene_, rebuild);
|
||||||
}
|
}
|
||||||
|
|
||||||
void AlembicProcedural::walk_hierarchy(
|
void AlembicProcedural::walk_hierarchy(
|
||||||
IObject parent,
|
IObject parent,
|
||||||
const ObjectHeader &header,
|
const ObjectHeader &header,
|
||||||
MatrixSampleMap *xform_samples,
|
MatrixSamplesData matrix_samples_data,
|
||||||
const unordered_map<std::string, AlembicObject *> &object_map,
|
const unordered_map<std::string, AlembicObject *> &object_map,
|
||||||
Progress &progress)
|
Progress &progress)
|
||||||
{
|
{
|
||||||
@@ -1763,7 +1770,7 @@ void AlembicProcedural::walk_hierarchy(
|
|||||||
MatrixSampleMap local_xform_samples;
|
MatrixSampleMap local_xform_samples;
|
||||||
|
|
||||||
MatrixSampleMap *temp_xform_samples = nullptr;
|
MatrixSampleMap *temp_xform_samples = nullptr;
|
||||||
if (xform_samples == nullptr) {
|
if (matrix_samples_data.samples == nullptr) {
|
||||||
/* If there is no parent transforms, fill the map directly. */
|
/* If there is no parent transforms, fill the map directly. */
|
||||||
temp_xform_samples = &concatenated_xform_samples;
|
temp_xform_samples = &concatenated_xform_samples;
|
||||||
}
|
}
|
||||||
@@ -1778,11 +1785,13 @@ void AlembicProcedural::walk_hierarchy(
|
|||||||
temp_xform_samples->insert({sample_time, sample.getMatrix()});
|
temp_xform_samples->insert({sample_time, sample.getMatrix()});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (xform_samples != nullptr) {
|
if (matrix_samples_data.samples != nullptr) {
|
||||||
concatenate_xform_samples(*xform_samples, local_xform_samples, concatenated_xform_samples);
|
concatenate_xform_samples(
|
||||||
|
*matrix_samples_data.samples, local_xform_samples, concatenated_xform_samples);
|
||||||
}
|
}
|
||||||
|
|
||||||
xform_samples = &concatenated_xform_samples;
|
matrix_samples_data.samples = &concatenated_xform_samples;
|
||||||
|
matrix_samples_data.time_sampling = ts;
|
||||||
}
|
}
|
||||||
|
|
||||||
next_object = xform;
|
next_object = xform;
|
||||||
@@ -1798,8 +1807,9 @@ void AlembicProcedural::walk_hierarchy(
|
|||||||
abc_object->iobject = subd;
|
abc_object->iobject = subd;
|
||||||
abc_object->schema_type = AlembicObject::SUBD;
|
abc_object->schema_type = AlembicObject::SUBD;
|
||||||
|
|
||||||
if (xform_samples) {
|
if (matrix_samples_data.samples) {
|
||||||
abc_object->xform_samples = *xform_samples;
|
abc_object->xform_samples = *matrix_samples_data.samples;
|
||||||
|
abc_object->xform_time_sampling = matrix_samples_data.time_sampling;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1816,8 +1826,9 @@ void AlembicProcedural::walk_hierarchy(
|
|||||||
abc_object->iobject = mesh;
|
abc_object->iobject = mesh;
|
||||||
abc_object->schema_type = AlembicObject::POLY_MESH;
|
abc_object->schema_type = AlembicObject::POLY_MESH;
|
||||||
|
|
||||||
if (xform_samples) {
|
if (matrix_samples_data.samples) {
|
||||||
abc_object->xform_samples = *xform_samples;
|
abc_object->xform_samples = *matrix_samples_data.samples;
|
||||||
|
abc_object->xform_time_sampling = matrix_samples_data.time_sampling;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1834,8 +1845,9 @@ void AlembicProcedural::walk_hierarchy(
|
|||||||
abc_object->iobject = curves;
|
abc_object->iobject = curves;
|
||||||
abc_object->schema_type = AlembicObject::CURVES;
|
abc_object->schema_type = AlembicObject::CURVES;
|
||||||
|
|
||||||
if (xform_samples) {
|
if (matrix_samples_data.samples) {
|
||||||
abc_object->xform_samples = *xform_samples;
|
abc_object->xform_samples = *matrix_samples_data.samples;
|
||||||
|
abc_object->xform_time_sampling = matrix_samples_data.time_sampling;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1844,15 +1856,92 @@ void AlembicProcedural::walk_hierarchy(
|
|||||||
else if (IFaceSet::matches(header)) {
|
else if (IFaceSet::matches(header)) {
|
||||||
// ignore the face set, it will be read along with the data
|
// ignore the face set, it will be read along with the data
|
||||||
}
|
}
|
||||||
|
else if (IPoints::matches(header)) {
|
||||||
|
// unsupported for now
|
||||||
|
}
|
||||||
|
else if (INuPatch::matches(header)) {
|
||||||
|
// unsupported for now
|
||||||
|
}
|
||||||
else {
|
else {
|
||||||
// unsupported type for now (Points, NuPatch)
|
|
||||||
next_object = parent.getChild(header.getName());
|
next_object = parent.getChild(header.getName());
|
||||||
|
|
||||||
|
if (next_object.isInstanceRoot()) {
|
||||||
|
unordered_map<std::string, AlembicObject *>::const_iterator iter;
|
||||||
|
|
||||||
|
/* Was this object asked to be rendered? */
|
||||||
|
iter = object_map.find(next_object.getFullName());
|
||||||
|
|
||||||
|
if (iter != object_map.end()) {
|
||||||
|
AlembicObject *abc_object = iter->second;
|
||||||
|
|
||||||
|
/* Only try to render an instance if the original object is also rendered. */
|
||||||
|
iter = object_map.find(next_object.instanceSourcePath());
|
||||||
|
|
||||||
|
if (iter != object_map.end()) {
|
||||||
|
abc_object->iobject = next_object;
|
||||||
|
abc_object->instance_of = iter->second;
|
||||||
|
|
||||||
|
if (matrix_samples_data.samples) {
|
||||||
|
abc_object->xform_samples = *matrix_samples_data.samples;
|
||||||
|
abc_object->xform_time_sampling = matrix_samples_data.time_sampling;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (next_object.valid()) {
|
if (next_object.valid()) {
|
||||||
for (size_t i = 0; i < next_object.getNumChildren(); ++i) {
|
for (size_t i = 0; i < next_object.getNumChildren(); ++i) {
|
||||||
walk_hierarchy(
|
walk_hierarchy(
|
||||||
next_object, next_object.getChildHeader(i), xform_samples, object_map, progress);
|
next_object, next_object.getChildHeader(i), matrix_samples_data, object_map, progress);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void AlembicProcedural::build_caches(Progress &progress)
|
||||||
|
{
|
||||||
|
for (Node *node : objects) {
|
||||||
|
AlembicObject *object = static_cast<AlembicObject *>(node);
|
||||||
|
|
||||||
|
if (progress.get_cancel()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (object->schema_type == AlembicObject::POLY_MESH) {
|
||||||
|
if (!object->has_data_loaded()) {
|
||||||
|
IPolyMesh polymesh(object->iobject, Alembic::Abc::kWrapExisting);
|
||||||
|
IPolyMeshSchema schema = polymesh.getSchema();
|
||||||
|
object->load_all_data(this, schema, progress);
|
||||||
|
}
|
||||||
|
else if (object->need_shader_update) {
|
||||||
|
IPolyMesh polymesh(object->iobject, Alembic::Abc::kWrapExisting);
|
||||||
|
IPolyMeshSchema schema = polymesh.getSchema();
|
||||||
|
object->update_shader_attributes(schema.getArbGeomParams(), progress);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (object->schema_type == AlembicObject::CURVES) {
|
||||||
|
if (!object->has_data_loaded() || default_radius_is_modified() ||
|
||||||
|
object->radius_scale_is_modified()) {
|
||||||
|
ICurves curves(object->iobject, Alembic::Abc::kWrapExisting);
|
||||||
|
ICurvesSchema schema = curves.getSchema();
|
||||||
|
object->load_all_data(this, schema, progress, default_radius);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (object->schema_type == AlembicObject::SUBD) {
|
||||||
|
if (!object->has_data_loaded()) {
|
||||||
|
ISubD subd_mesh(object->iobject, Alembic::Abc::kWrapExisting);
|
||||||
|
ISubDSchema schema = subd_mesh.getSchema();
|
||||||
|
object->load_all_data(this, schema, progress);
|
||||||
|
}
|
||||||
|
else if (object->need_shader_update) {
|
||||||
|
ISubD subd_mesh(object->iobject, Alembic::Abc::kWrapExisting);
|
||||||
|
ISubDSchema schema = subd_mesh.getSchema();
|
||||||
|
object->update_shader_attributes(schema.getArbGeomParams(), progress);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (scale_is_modified() || object->get_cached_data().transforms.size() == 0) {
|
||||||
|
object->setup_transform_cache(scale);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -38,6 +38,11 @@ class Shader;
|
|||||||
|
|
||||||
using MatrixSampleMap = std::map<Alembic::Abc::chrono_t, Alembic::Abc::M44d>;
|
using MatrixSampleMap = std::map<Alembic::Abc::chrono_t, Alembic::Abc::M44d>;
|
||||||
|
|
||||||
|
struct MatrixSamplesData {
|
||||||
|
MatrixSampleMap *samples = nullptr;
|
||||||
|
Alembic::AbcCoreAbstract::TimeSamplingPtr time_sampling;
|
||||||
|
};
|
||||||
|
|
||||||
/* Helpers to detect if some type is a ccl::array. */
|
/* Helpers to detect if some type is a ccl::array. */
|
||||||
template<typename> struct is_array : public std::false_type {
|
template<typename> struct is_array : public std::false_type {
|
||||||
};
|
};
|
||||||
@@ -45,6 +50,78 @@ template<typename> struct is_array : public std::false_type {
|
|||||||
template<typename T> struct is_array<array<T>> : public std::true_type {
|
template<typename T> struct is_array<array<T>> : public std::true_type {
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/* Holds the data for a cache lookup at a given time, as well as informations to
|
||||||
|
* help disambiguate successes or failures to get data from the cache. */
|
||||||
|
template<typename T> class CacheLookupResult {
|
||||||
|
enum class State {
|
||||||
|
NEW_DATA,
|
||||||
|
ALREADY_LOADED,
|
||||||
|
NO_DATA_FOR_TIME,
|
||||||
|
};
|
||||||
|
|
||||||
|
T *data;
|
||||||
|
State state;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
/* Prevent default construction outside of the class: for a valid result, we
|
||||||
|
* should use the static functions below. */
|
||||||
|
CacheLookupResult() = default;
|
||||||
|
|
||||||
|
public:
|
||||||
|
static CacheLookupResult new_data(T *data_)
|
||||||
|
{
|
||||||
|
CacheLookupResult result;
|
||||||
|
result.data = data_;
|
||||||
|
result.state = State::NEW_DATA;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
static CacheLookupResult no_data_found_for_time()
|
||||||
|
{
|
||||||
|
CacheLookupResult result;
|
||||||
|
result.data = nullptr;
|
||||||
|
result.state = State::NO_DATA_FOR_TIME;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
static CacheLookupResult already_loaded()
|
||||||
|
{
|
||||||
|
CacheLookupResult result;
|
||||||
|
result.data = nullptr;
|
||||||
|
result.state = State::ALREADY_LOADED;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* This should only be call if new data is available. */
|
||||||
|
const T &get_data() const
|
||||||
|
{
|
||||||
|
assert(state == State::NEW_DATA);
|
||||||
|
assert(data != nullptr);
|
||||||
|
return *data;
|
||||||
|
}
|
||||||
|
|
||||||
|
T *get_data_or_null() const
|
||||||
|
{
|
||||||
|
// data_ should already be null if there is no new data so no need to check
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool has_new_data() const
|
||||||
|
{
|
||||||
|
return state == State::NEW_DATA;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool has_already_loaded() const
|
||||||
|
{
|
||||||
|
return state == State::ALREADY_LOADED;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool has_no_data_for_time() const
|
||||||
|
{
|
||||||
|
return state == State::NO_DATA_FOR_TIME;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
/* Store the data set for an animation at every time points, or at the beginning of the animation
|
/* Store the data set for an animation at every time points, or at the beginning of the animation
|
||||||
* for constant data.
|
* for constant data.
|
||||||
*
|
*
|
||||||
@@ -74,10 +151,10 @@ template<typename T> class DataStore {
|
|||||||
|
|
||||||
/* Get the data for the specified time.
|
/* Get the data for the specified time.
|
||||||
* Return nullptr if there is no data or if the data for this time was already loaded. */
|
* Return nullptr if there is no data or if the data for this time was already loaded. */
|
||||||
T *data_for_time(double time)
|
CacheLookupResult<T> data_for_time(double time)
|
||||||
{
|
{
|
||||||
if (size() == 0) {
|
if (size() == 0) {
|
||||||
return nullptr;
|
return CacheLookupResult<T>::no_data_found_for_time();
|
||||||
}
|
}
|
||||||
|
|
||||||
std::pair<size_t, Alembic::Abc::chrono_t> index_pair;
|
std::pair<size_t, Alembic::Abc::chrono_t> index_pair;
|
||||||
@@ -85,26 +162,26 @@ template<typename T> class DataStore {
|
|||||||
DataTimePair &data_pair = data[index_pair.first];
|
DataTimePair &data_pair = data[index_pair.first];
|
||||||
|
|
||||||
if (last_loaded_time == data_pair.time) {
|
if (last_loaded_time == data_pair.time) {
|
||||||
return nullptr;
|
return CacheLookupResult<T>::already_loaded();
|
||||||
}
|
}
|
||||||
|
|
||||||
last_loaded_time = data_pair.time;
|
last_loaded_time = data_pair.time;
|
||||||
|
|
||||||
return &data_pair.data;
|
return CacheLookupResult<T>::new_data(&data_pair.data);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* get the data for the specified time, but do not check if the data was already loaded for this
|
/* get the data for the specified time, but do not check if the data was already loaded for this
|
||||||
* time return nullptr if there is no data */
|
* time return nullptr if there is no data */
|
||||||
T *data_for_time_no_check(double time)
|
CacheLookupResult<T> data_for_time_no_check(double time)
|
||||||
{
|
{
|
||||||
if (size() == 0) {
|
if (size() == 0) {
|
||||||
return nullptr;
|
return CacheLookupResult<T>::no_data_found_for_time();
|
||||||
}
|
}
|
||||||
|
|
||||||
std::pair<size_t, Alembic::Abc::chrono_t> index_pair;
|
std::pair<size_t, Alembic::Abc::chrono_t> index_pair;
|
||||||
index_pair = time_sampling.getNearIndex(time, data.size());
|
index_pair = time_sampling.getNearIndex(time, data.size());
|
||||||
DataTimePair &data_pair = data[index_pair.first];
|
DataTimePair &data_pair = data[index_pair.first];
|
||||||
return &data_pair.data;
|
return CacheLookupResult<T>::new_data(&data_pair.data);
|
||||||
}
|
}
|
||||||
|
|
||||||
void add_data(T &data_, double time)
|
void add_data(T &data_, double time)
|
||||||
@@ -144,15 +221,15 @@ template<typename T> class DataStore {
|
|||||||
* data for this time or it was already loaded, do nothing. */
|
* data for this time or it was already loaded, do nothing. */
|
||||||
void copy_to_socket(double time, Node *node, const SocketType *socket)
|
void copy_to_socket(double time, Node *node, const SocketType *socket)
|
||||||
{
|
{
|
||||||
T *data_ = data_for_time(time);
|
CacheLookupResult<T> result = data_for_time(time);
|
||||||
|
|
||||||
if (data_ == nullptr) {
|
if (!result.has_new_data()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* TODO(kevindietrich): arrays are emptied when passed to the sockets, so for now we copy the
|
/* TODO(kevindietrich): arrays are emptied when passed to the sockets, so for now we copy the
|
||||||
* arrays to avoid reloading the data */
|
* arrays to avoid reloading the data */
|
||||||
T value = *data_;
|
T value = result.get_data();
|
||||||
node->set(*socket, value);
|
node->set(*socket, value);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -249,15 +326,12 @@ class AlembicObject : public Node {
|
|||||||
|
|
||||||
void load_all_data(AlembicProcedural *proc,
|
void load_all_data(AlembicProcedural *proc,
|
||||||
Alembic::AbcGeom::IPolyMeshSchema &schema,
|
Alembic::AbcGeom::IPolyMeshSchema &schema,
|
||||||
float scale,
|
|
||||||
Progress &progress);
|
Progress &progress);
|
||||||
void load_all_data(AlembicProcedural *proc,
|
void load_all_data(AlembicProcedural *proc,
|
||||||
Alembic::AbcGeom::ISubDSchema &schema,
|
Alembic::AbcGeom::ISubDSchema &schema,
|
||||||
float scale,
|
|
||||||
Progress &progress);
|
Progress &progress);
|
||||||
void load_all_data(AlembicProcedural *proc,
|
void load_all_data(AlembicProcedural *proc,
|
||||||
const Alembic::AbcGeom::ICurvesSchema &schema,
|
const Alembic::AbcGeom::ICurvesSchema &schema,
|
||||||
float scale,
|
|
||||||
Progress &progress,
|
Progress &progress,
|
||||||
float default_radius);
|
float default_radius);
|
||||||
|
|
||||||
@@ -274,6 +348,9 @@ class AlembicObject : public Node {
|
|||||||
|
|
||||||
bool need_shader_update = true;
|
bool need_shader_update = true;
|
||||||
|
|
||||||
|
AlembicObject *instance_of = nullptr;
|
||||||
|
|
||||||
|
Alembic::AbcCoreAbstract::TimeSamplingPtr xform_time_sampling;
|
||||||
MatrixSampleMap xform_samples;
|
MatrixSampleMap xform_samples;
|
||||||
Alembic::AbcGeom::IObject iobject;
|
Alembic::AbcGeom::IObject iobject;
|
||||||
|
|
||||||
@@ -384,30 +461,23 @@ class AlembicProcedural : public Procedural {
|
|||||||
* way for each IObject. */
|
* way for each IObject. */
|
||||||
void walk_hierarchy(Alembic::AbcGeom::IObject parent,
|
void walk_hierarchy(Alembic::AbcGeom::IObject parent,
|
||||||
const Alembic::AbcGeom::ObjectHeader &ohead,
|
const Alembic::AbcGeom::ObjectHeader &ohead,
|
||||||
MatrixSampleMap *xform_samples,
|
MatrixSamplesData matrix_samples_data,
|
||||||
const unordered_map<string, AlembicObject *> &object_map,
|
const unordered_map<string, AlembicObject *> &object_map,
|
||||||
Progress &progress);
|
Progress &progress);
|
||||||
|
|
||||||
/* Read the data for an IPolyMesh at the specified frame_time. Creates corresponding Geometry and
|
/* Read the data for an IPolyMesh at the specified frame_time. Creates corresponding Geometry and
|
||||||
* Object Nodes in the Cycles scene if none exist yet. */
|
* Object Nodes in the Cycles scene if none exist yet. */
|
||||||
void read_mesh(Scene *scene,
|
void read_mesh(AlembicObject *abc_object, Alembic::AbcGeom::Abc::chrono_t frame_time);
|
||||||
AlembicObject *abc_object,
|
|
||||||
Alembic::AbcGeom::Abc::chrono_t frame_time,
|
|
||||||
Progress &progress);
|
|
||||||
|
|
||||||
/* Read the data for an ICurves at the specified frame_time. Creates corresponding Geometry and
|
/* Read the data for an ICurves at the specified frame_time. Creates corresponding Geometry and
|
||||||
* Object Nodes in the Cycles scene if none exist yet. */
|
* Object Nodes in the Cycles scene if none exist yet. */
|
||||||
void read_curves(Scene *scene,
|
void read_curves(AlembicObject *abc_object, Alembic::AbcGeom::Abc::chrono_t frame_time);
|
||||||
AlembicObject *abc_object,
|
|
||||||
Alembic::AbcGeom::Abc::chrono_t frame_time,
|
|
||||||
Progress &progress);
|
|
||||||
|
|
||||||
/* Read the data for an ISubD at the specified frame_time. Creates corresponding Geometry and
|
/* Read the data for an ISubD at the specified frame_time. Creates corresponding Geometry and
|
||||||
* Object Nodes in the Cycles scene if none exist yet. */
|
* Object Nodes in the Cycles scene if none exist yet. */
|
||||||
void read_subd(Scene *scene,
|
void read_subd(AlembicObject *abc_object, Alembic::AbcGeom::Abc::chrono_t frame_time);
|
||||||
AlembicObject *abc_object,
|
|
||||||
Alembic::AbcGeom::Abc::chrono_t frame_time,
|
void build_caches(Progress &progress);
|
||||||
Progress &progress);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
CCL_NAMESPACE_END
|
CCL_NAMESPACE_END
|
||||||
|
@@ -1367,7 +1367,7 @@ void GeometryManager::device_update_bvh(Device *device,
|
|||||||
dscene->data.bvh.use_bvh_steps = (scene->params.num_bvh_time_steps != 0);
|
dscene->data.bvh.use_bvh_steps = (scene->params.num_bvh_time_steps != 0);
|
||||||
dscene->data.bvh.curve_subdivisions = scene->params.curve_subdivisions();
|
dscene->data.bvh.curve_subdivisions = scene->params.curve_subdivisions();
|
||||||
/* The scene handle is set in 'CPUDevice::const_copy_to' and 'OptiXDevice::const_copy_to' */
|
/* The scene handle is set in 'CPUDevice::const_copy_to' and 'OptiXDevice::const_copy_to' */
|
||||||
dscene->data.bvh.scene = NULL;
|
dscene->data.bvh.scene = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Set of flags used to help determining what data has been modified or needs reallocation, so we
|
/* Set of flags used to help determining what data has been modified or needs reallocation, so we
|
||||||
|
@@ -831,7 +831,8 @@ static bool to_scene_linear_transform(OCIO::ConstConfigRcPtr &config,
|
|||||||
|
|
||||||
void ShaderManager::init_xyz_transforms()
|
void ShaderManager::init_xyz_transforms()
|
||||||
{
|
{
|
||||||
/* Default to ITU-BT.709 in case no appropriate transform found. */
|
/* Default to ITU-BT.709 in case no appropriate transform found.
|
||||||
|
* Note XYZ here is defined as having a D65 white point. */
|
||||||
xyz_to_r = make_float3(3.2404542f, -1.5371385f, -0.4985314f);
|
xyz_to_r = make_float3(3.2404542f, -1.5371385f, -0.4985314f);
|
||||||
xyz_to_g = make_float3(-0.9692660f, 1.8760108f, 0.0415560f);
|
xyz_to_g = make_float3(-0.9692660f, 1.8760108f, 0.0415560f);
|
||||||
xyz_to_b = make_float3(0.0556434f, -0.2040259f, 1.0572252f);
|
xyz_to_b = make_float3(0.0556434f, -0.2040259f, 1.0572252f);
|
||||||
@@ -848,7 +849,7 @@ void ShaderManager::init_xyz_transforms()
|
|||||||
|
|
||||||
if (config->hasRole("aces_interchange")) {
|
if (config->hasRole("aces_interchange")) {
|
||||||
/* Standard OpenColorIO role, defined as ACES2065-1. */
|
/* Standard OpenColorIO role, defined as ACES2065-1. */
|
||||||
const Transform xyz_to_aces = make_transform(1.0498110175f,
|
const Transform xyz_E_to_aces = make_transform(1.0498110175f,
|
||||||
0.0f,
|
0.0f,
|
||||||
-0.0000974845f,
|
-0.0000974845f,
|
||||||
0.0f,
|
0.0f,
|
||||||
@@ -860,12 +861,15 @@ void ShaderManager::init_xyz_transforms()
|
|||||||
0.0f,
|
0.0f,
|
||||||
0.9912520182f,
|
0.9912520182f,
|
||||||
0.0f);
|
0.0f);
|
||||||
|
const Transform xyz_D65_to_E = make_transform(
|
||||||
|
1.0521111f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.9184170f, 0.0f);
|
||||||
|
|
||||||
Transform aces_to_rgb;
|
Transform aces_to_rgb;
|
||||||
if (!to_scene_linear_transform(config, "aces_interchange", aces_to_rgb)) {
|
if (!to_scene_linear_transform(config, "aces_interchange", aces_to_rgb)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
xyz_to_rgb = aces_to_rgb * xyz_to_aces;
|
xyz_to_rgb = aces_to_rgb * xyz_E_to_aces * xyz_D65_to_E;
|
||||||
}
|
}
|
||||||
else if (config->hasRole("XYZ")) {
|
else if (config->hasRole("XYZ")) {
|
||||||
/* Custom role used before the standard existed. */
|
/* Custom role used before the standard existed. */
|
||||||
|
@@ -278,7 +278,7 @@ GHOST_TSuccess GHOST_ContextCGL::initializeDrawingContext()
|
|||||||
#ifdef GHOST_WAIT_FOR_VSYNC
|
#ifdef GHOST_WAIT_FOR_VSYNC
|
||||||
{
|
{
|
||||||
GLint swapInt = 1;
|
GLint swapInt = 1;
|
||||||
/* wait for vsync, to avoid tearing artifacts */
|
/* Wait for vertical-sync, to avoid tearing artifacts. */
|
||||||
[m_openGLContext setValues:&swapInt forParameter:NSOpenGLCPSwapInterval];
|
[m_openGLContext setValues:&swapInt forParameter:NSOpenGLCPSwapInterval];
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
@@ -386,13 +386,11 @@ extern "C" int GHOST_HACK_getFirstFile(char buf[FIRSTFILEBUFLG])
|
|||||||
- (id)init
|
- (id)init
|
||||||
{
|
{
|
||||||
self = [super init];
|
self = [super init];
|
||||||
if (self) {
|
|
||||||
NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
|
NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
|
||||||
[center addObserver:self
|
[center addObserver:self
|
||||||
selector:@selector(windowWillClose:)
|
selector:@selector(windowWillClose:)
|
||||||
name:NSWindowWillCloseNotification
|
name:NSWindowWillCloseNotification
|
||||||
object:nil];
|
object:nil];
|
||||||
}
|
|
||||||
return self;
|
return self;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -563,7 +561,7 @@ GHOST_TSuccess GHOST_SystemCocoa::init()
|
|||||||
SetFrontProcess(&psn);
|
SetFrontProcess(&psn);
|
||||||
}*/
|
}*/
|
||||||
|
|
||||||
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
|
@autoreleasepool {
|
||||||
[NSApplication sharedApplication]; // initializes NSApp
|
[NSApplication sharedApplication]; // initializes NSApp
|
||||||
|
|
||||||
if ([NSApp mainMenu] == nil) {
|
if ([NSApp mainMenu] == nil) {
|
||||||
@@ -646,14 +644,13 @@ GHOST_TSuccess GHOST_SystemCocoa::init()
|
|||||||
[NSApp setDelegate:appDelegate];
|
[NSApp setDelegate:appDelegate];
|
||||||
}
|
}
|
||||||
|
|
||||||
// AppKit provides automatic window tabbing. Blender is a single-tabbed application without a
|
// AppKit provides automatic window tabbing. Blender is a single-tabbed application
|
||||||
// macOS tab bar, and should explicitly opt-out of this. This is also controlled by the macOS
|
// without a macOS tab bar, and should explicitly opt-out of this. This is also
|
||||||
// user default #NSWindowTabbingEnabled.
|
// controlled by the macOS user default #NSWindowTabbingEnabled.
|
||||||
NSWindow.allowsAutomaticWindowTabbing = NO;
|
NSWindow.allowsAutomaticWindowTabbing = NO;
|
||||||
|
|
||||||
[NSApp finishLaunching];
|
[NSApp finishLaunching];
|
||||||
|
}
|
||||||
[pool drain];
|
|
||||||
}
|
}
|
||||||
return success;
|
return success;
|
||||||
}
|
}
|
||||||
@@ -676,17 +673,14 @@ GHOST_TUns8 GHOST_SystemCocoa::getNumDisplays() const
|
|||||||
{
|
{
|
||||||
// Note that OS X supports monitor hot plug
|
// Note that OS X supports monitor hot plug
|
||||||
// We do not support multiple monitors at the moment
|
// We do not support multiple monitors at the moment
|
||||||
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
|
@autoreleasepool {
|
||||||
|
return NSScreen.screens.count;
|
||||||
GHOST_TUns8 count = [[NSScreen screens] count];
|
}
|
||||||
|
|
||||||
[pool drain];
|
|
||||||
return count;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void GHOST_SystemCocoa::getMainDisplayDimensions(GHOST_TUns32 &width, GHOST_TUns32 &height) const
|
void GHOST_SystemCocoa::getMainDisplayDimensions(GHOST_TUns32 &width, GHOST_TUns32 &height) const
|
||||||
{
|
{
|
||||||
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
|
@autoreleasepool {
|
||||||
// Get visible frame, that is frame excluding dock and top menu bar
|
// Get visible frame, that is frame excluding dock and top menu bar
|
||||||
NSRect frame = [[NSScreen mainScreen] visibleFrame];
|
NSRect frame = [[NSScreen mainScreen] visibleFrame];
|
||||||
|
|
||||||
@@ -698,8 +692,7 @@ void GHOST_SystemCocoa::getMainDisplayDimensions(GHOST_TUns32 &width, GHOST_TUns
|
|||||||
|
|
||||||
width = contentRect.size.width;
|
width = contentRect.size.width;
|
||||||
height = contentRect.size.height;
|
height = contentRect.size.height;
|
||||||
|
}
|
||||||
[pool drain];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void GHOST_SystemCocoa::getAllDisplayDimensions(GHOST_TUns32 &width, GHOST_TUns32 &height) const
|
void GHOST_SystemCocoa::getAllDisplayDimensions(GHOST_TUns32 &width, GHOST_TUns32 &height) const
|
||||||
@@ -720,8 +713,8 @@ GHOST_IWindow *GHOST_SystemCocoa::createWindow(const char *title,
|
|||||||
const bool is_dialog,
|
const bool is_dialog,
|
||||||
const GHOST_IWindow *parentWindow)
|
const GHOST_IWindow *parentWindow)
|
||||||
{
|
{
|
||||||
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
|
|
||||||
GHOST_IWindow *window = NULL;
|
GHOST_IWindow *window = NULL;
|
||||||
|
@autoreleasepool {
|
||||||
|
|
||||||
// Get the available rect for including window contents
|
// Get the available rect for including window contents
|
||||||
NSRect frame = [[NSScreen mainScreen] visibleFrame];
|
NSRect frame = [[NSScreen mainScreen] visibleFrame];
|
||||||
@@ -765,8 +758,7 @@ GHOST_IWindow *GHOST_SystemCocoa::createWindow(const char *title,
|
|||||||
delete window;
|
delete window;
|
||||||
window = NULL;
|
window = NULL;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
[pool drain];
|
|
||||||
return window;
|
return window;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -841,7 +833,7 @@ GHOST_TSuccess GHOST_SystemCocoa::setMouseCursorPosition(GHOST_TInt32 x, GHOST_T
|
|||||||
if (!window)
|
if (!window)
|
||||||
return GHOST_kFailure;
|
return GHOST_kFailure;
|
||||||
|
|
||||||
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
|
@autoreleasepool {
|
||||||
NSScreen *windowScreen = window->getScreen();
|
NSScreen *windowScreen = window->getScreen();
|
||||||
NSRect screenRect = [windowScreen frame];
|
NSRect screenRect = [windowScreen frame];
|
||||||
|
|
||||||
@@ -862,8 +854,7 @@ GHOST_TSuccess GHOST_SystemCocoa::setMouseCursorPosition(GHOST_TInt32 x, GHOST_T
|
|||||||
// however calling CGAssociateMouseAndMouseCursorPosition also removes the
|
// however calling CGAssociateMouseAndMouseCursorPosition also removes the
|
||||||
// delay, even if this is undocumented.
|
// delay, even if this is undocumented.
|
||||||
CGAssociateMouseAndMouseCursorPosition(true);
|
CGAssociateMouseAndMouseCursorPosition(true);
|
||||||
|
}
|
||||||
[pool drain];
|
|
||||||
return GHOST_kSuccess;
|
return GHOST_kSuccess;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -928,13 +919,12 @@ bool GHOST_SystemCocoa::processEvents(bool waitForEvent)
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
do {
|
do {
|
||||||
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
|
@autoreleasepool {
|
||||||
event = [NSApp nextEventMatchingMask:NSEventMaskAny
|
event = [NSApp nextEventMatchingMask:NSEventMaskAny
|
||||||
untilDate:[NSDate distantPast]
|
untilDate:[NSDate distantPast]
|
||||||
inMode:NSDefaultRunLoopMode
|
inMode:NSDefaultRunLoopMode
|
||||||
dequeue:YES];
|
dequeue:YES];
|
||||||
if (event == nil) {
|
if (event == nil) {
|
||||||
[pool drain];
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -962,8 +952,7 @@ bool GHOST_SystemCocoa::processEvents(bool waitForEvent)
|
|||||||
|
|
||||||
[NSApp sendEvent:event];
|
[NSApp sendEvent:event];
|
||||||
}
|
}
|
||||||
|
}
|
||||||
[pool drain];
|
|
||||||
} while (event != nil);
|
} while (event != nil);
|
||||||
#if 0
|
#if 0
|
||||||
} while (waitForEvent && !anyProcessed); // Needed only for timer implementation
|
} while (waitForEvent && !anyProcessed); // Needed only for timer implementation
|
||||||
@@ -1677,9 +1666,7 @@ GHOST_TSuccess GHOST_SystemCocoa::handleMouseEvent(void *eventPtr)
|
|||||||
NSEventPhase momentumPhase = NSEventPhaseNone;
|
NSEventPhase momentumPhase = NSEventPhaseNone;
|
||||||
NSEventPhase phase = NSEventPhaseNone;
|
NSEventPhase phase = NSEventPhaseNone;
|
||||||
|
|
||||||
if ([event respondsToSelector:@selector(momentumPhase)])
|
|
||||||
momentumPhase = [event momentumPhase];
|
momentumPhase = [event momentumPhase];
|
||||||
if ([event respondsToSelector:@selector(phase)])
|
|
||||||
phase = [event phase];
|
phase = [event phase];
|
||||||
|
|
||||||
/* when pressing a key while momentum scrolling continues after
|
/* when pressing a key while momentum scrolling continues after
|
||||||
@@ -1953,28 +1940,13 @@ GHOST_TUns8 *GHOST_SystemCocoa::getClipboard(bool selection) const
|
|||||||
GHOST_TUns8 *temp_buff;
|
GHOST_TUns8 *temp_buff;
|
||||||
size_t pastedTextSize;
|
size_t pastedTextSize;
|
||||||
|
|
||||||
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
|
@autoreleasepool {
|
||||||
|
|
||||||
NSPasteboard *pasteBoard = [NSPasteboard generalPasteboard];
|
NSPasteboard *pasteBoard = [NSPasteboard generalPasteboard];
|
||||||
|
|
||||||
if (pasteBoard == nil) {
|
|
||||||
[pool drain];
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
NSArray *supportedTypes = [NSArray arrayWithObjects:NSStringPboardType, nil];
|
|
||||||
|
|
||||||
NSString *bestType = [[NSPasteboard generalPasteboard] availableTypeFromArray:supportedTypes];
|
|
||||||
|
|
||||||
if (bestType == nil) {
|
|
||||||
[pool drain];
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
NSString *textPasted = [pasteBoard stringForType:NSStringPboardType];
|
NSString *textPasted = [pasteBoard stringForType:NSStringPboardType];
|
||||||
|
|
||||||
if (textPasted == nil) {
|
if (textPasted == nil) {
|
||||||
[pool drain];
|
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1983,7 +1955,6 @@ GHOST_TUns8 *GHOST_SystemCocoa::getClipboard(bool selection) const
|
|||||||
temp_buff = (GHOST_TUns8 *)malloc(pastedTextSize + 1);
|
temp_buff = (GHOST_TUns8 *)malloc(pastedTextSize + 1);
|
||||||
|
|
||||||
if (temp_buff == NULL) {
|
if (temp_buff == NULL) {
|
||||||
[pool drain];
|
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1992,8 +1963,6 @@ GHOST_TUns8 *GHOST_SystemCocoa::getClipboard(bool selection) const
|
|||||||
|
|
||||||
temp_buff[pastedTextSize] = '\0';
|
temp_buff[pastedTextSize] = '\0';
|
||||||
|
|
||||||
[pool drain];
|
|
||||||
|
|
||||||
if (temp_buff) {
|
if (temp_buff) {
|
||||||
return temp_buff;
|
return temp_buff;
|
||||||
}
|
}
|
||||||
@@ -2001,30 +1970,18 @@ GHOST_TUns8 *GHOST_SystemCocoa::getClipboard(bool selection) const
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void GHOST_SystemCocoa::putClipboard(GHOST_TInt8 *buffer, bool selection) const
|
void GHOST_SystemCocoa::putClipboard(GHOST_TInt8 *buffer, bool selection) const
|
||||||
{
|
{
|
||||||
NSString *textToCopy;
|
|
||||||
|
|
||||||
if (selection)
|
if (selection)
|
||||||
return; // for copying the selection, used on X11
|
return; // for copying the selection, used on X11
|
||||||
|
|
||||||
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
|
@autoreleasepool {
|
||||||
|
|
||||||
NSPasteboard *pasteBoard = [NSPasteboard generalPasteboard];
|
|
||||||
|
|
||||||
if (pasteBoard == nil) {
|
|
||||||
[pool drain];
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
NSArray *supportedTypes = [NSArray arrayWithObject:NSStringPboardType];
|
|
||||||
|
|
||||||
[pasteBoard declareTypes:supportedTypes owner:nil];
|
|
||||||
|
|
||||||
textToCopy = [NSString stringWithCString:buffer encoding:NSUTF8StringEncoding];
|
|
||||||
|
|
||||||
|
NSPasteboard *pasteBoard = NSPasteboard.generalPasteboard;
|
||||||
|
[pasteBoard declareTypes:@[ NSStringPboardType ] owner:nil];
|
||||||
|
NSString *textToCopy = [NSString stringWithCString:buffer encoding:NSUTF8StringEncoding];
|
||||||
[pasteBoard setString:textToCopy forType:NSStringPboardType];
|
[pasteBoard setString:textToCopy forType:NSStringPboardType];
|
||||||
|
}
|
||||||
[pool drain];
|
|
||||||
}
|
}
|
||||||
|
@@ -34,9 +34,9 @@
|
|||||||
#ifdef WITH_X11_XINPUT
|
#ifdef WITH_X11_XINPUT
|
||||||
# include <X11/extensions/XInput.h>
|
# include <X11/extensions/XInput.h>
|
||||||
|
|
||||||
/* Disable xinput warp, currently not implemented by Xorg for multi-head display.
|
/* Disable XINPUT warp, currently not implemented by Xorg for multi-head display.
|
||||||
* (see comment in xserver "Xi/xiwarppointer.c" -> "FIXME: panoramix stuff is missing" ~ v1.13.4)
|
* (see comment in XSERVER `Xi/xiwarppointer.c` -> `FIXME: panoramix stuff is missing` ~ v1.13.4)
|
||||||
* If this is supported we can add back xinput for warping (fixing T48901).
|
* If this is supported we can add back XINPUT for warping (fixing T48901).
|
||||||
* For now disable (see T50383). */
|
* For now disable (see T50383). */
|
||||||
// # define USE_X11_XINPUT_WARP
|
// # define USE_X11_XINPUT_WARP
|
||||||
#endif
|
#endif
|
||||||
|
@@ -92,16 +92,11 @@ GHOST_WindowWin32::GHOST_WindowWin32(GHOST_SystemWin32 *system,
|
|||||||
{
|
{
|
||||||
wchar_t *title_16 = alloc_utf16_from_8((char *)title, 0);
|
wchar_t *title_16 = alloc_utf16_from_8((char *)title, 0);
|
||||||
RECT win_rect = {left, top, (long)(left + width), (long)(top + height)};
|
RECT win_rect = {left, top, (long)(left + width), (long)(top + height)};
|
||||||
RECT parent_rect = {0, 0, 0, 0};
|
|
||||||
|
|
||||||
// Initialize tablet variables
|
// Initialize tablet variables
|
||||||
memset(&m_wintab, 0, sizeof(m_wintab));
|
memset(&m_wintab, 0, sizeof(m_wintab));
|
||||||
m_tabletData = GHOST_TABLET_DATA_NONE;
|
m_tabletData = GHOST_TABLET_DATA_NONE;
|
||||||
|
|
||||||
if (parentwindow) {
|
|
||||||
GetWindowRect(m_parentWindowHwnd, &parent_rect);
|
|
||||||
}
|
|
||||||
|
|
||||||
DWORD style = parentwindow ?
|
DWORD style = parentwindow ?
|
||||||
WS_POPUPWINDOW | WS_CAPTION | WS_MAXIMIZEBOX | WS_MINIMIZEBOX | WS_SIZEBOX :
|
WS_POPUPWINDOW | WS_CAPTION | WS_MAXIMIZEBOX | WS_MINIMIZEBOX | WS_SIZEBOX :
|
||||||
WS_OVERLAPPEDWINDOW;
|
WS_OVERLAPPEDWINDOW;
|
||||||
@@ -124,9 +119,7 @@ GHOST_WindowWin32::GHOST_WindowWin32(GHOST_SystemWin32 *system,
|
|||||||
MONITORINFOEX monitor;
|
MONITORINFOEX monitor;
|
||||||
monitor.cbSize = sizeof(MONITORINFOEX);
|
monitor.cbSize = sizeof(MONITORINFOEX);
|
||||||
monitor.dwFlags = 0;
|
monitor.dwFlags = 0;
|
||||||
GetMonitorInfo(
|
GetMonitorInfo(MonitorFromRect(&win_rect, MONITOR_DEFAULTTONEAREST), &monitor);
|
||||||
MonitorFromRect(parentwindow ? &parent_rect : &win_rect, MONITOR_DEFAULTTONEAREST),
|
|
||||||
&monitor);
|
|
||||||
|
|
||||||
/* Adjust our requested size to allow for caption and borders and constrain to monitor. */
|
/* Adjust our requested size to allow for caption and borders and constrain to monitor. */
|
||||||
AdjustWindowRectEx(&win_rect, WS_CAPTION, FALSE, 0);
|
AdjustWindowRectEx(&win_rect, WS_CAPTION, FALSE, 0);
|
||||||
@@ -1200,7 +1193,7 @@ GHOST_TSuccess GHOST_WindowWin32::setWindowCustomCursorShape(GHOST_TUns8 *bitmap
|
|||||||
GHOST_TUns32 fullBitRow, fullMaskRow;
|
GHOST_TUns32 fullBitRow, fullMaskRow;
|
||||||
int x, y, cols;
|
int x, y, cols;
|
||||||
|
|
||||||
cols = sizeX / 8; /* Number of whole bytes per row (width of bm/mask). */
|
cols = sizeX / 8; /* Number of whole bytes per row (width of bitmap/mask). */
|
||||||
if (sizeX % 8)
|
if (sizeX % 8)
|
||||||
cols++;
|
cols++;
|
||||||
|
|
||||||
|
@@ -1,2 +1,34 @@
|
|||||||
DisableFormat: true
|
BasedOnStyle: Google
|
||||||
SortIncludes: false
|
|
||||||
|
ColumnLimit: 80
|
||||||
|
|
||||||
|
Standard: Cpp11
|
||||||
|
|
||||||
|
# Indent nested preprocessor.
|
||||||
|
# #ifdef Foo
|
||||||
|
# # include <nested>
|
||||||
|
# #endif
|
||||||
|
IndentPPDirectives: AfterHash
|
||||||
|
|
||||||
|
# For the cases when namespace is closing with a wrong comment
|
||||||
|
FixNamespaceComments: true
|
||||||
|
|
||||||
|
AllowShortFunctionsOnASingleLine: InlineOnly
|
||||||
|
AllowShortBlocksOnASingleLine: false
|
||||||
|
AllowShortIfStatementsOnASingleLine: false
|
||||||
|
AllowShortLoopsOnASingleLine: false
|
||||||
|
AllowShortCaseLabelsOnASingleLine: true
|
||||||
|
|
||||||
|
# No bin packing, every argument is on its own line.
|
||||||
|
BinPackArguments: false
|
||||||
|
BinPackParameters: false
|
||||||
|
|
||||||
|
# Ensure pointer alignment.
|
||||||
|
# ObjectType* object;
|
||||||
|
PointerAlignment: Left
|
||||||
|
DerivePointerAlignment: false
|
||||||
|
|
||||||
|
AlignEscapedNewlines: Right
|
||||||
|
|
||||||
|
IncludeBlocks: Preserve
|
||||||
|
SortIncludes: true
|
||||||
|
@@ -22,11 +22,11 @@
|
|||||||
#include "intern/utildefines.h"
|
#include "intern/utildefines.h"
|
||||||
#include "libmv/autotrack/autotrack.h"
|
#include "libmv/autotrack/autotrack.h"
|
||||||
|
|
||||||
|
using libmv::TrackRegionOptions;
|
||||||
|
using libmv::TrackRegionResult;
|
||||||
using mv::AutoTrack;
|
using mv::AutoTrack;
|
||||||
using mv::FrameAccessor;
|
using mv::FrameAccessor;
|
||||||
using mv::Marker;
|
using mv::Marker;
|
||||||
using libmv::TrackRegionOptions;
|
|
||||||
using libmv::TrackRegionResult;
|
|
||||||
|
|
||||||
libmv_AutoTrack* libmv_autoTrackNew(libmv_FrameAccessor* frame_accessor) {
|
libmv_AutoTrack* libmv_autoTrackNew(libmv_FrameAccessor* frame_accessor) {
|
||||||
return (libmv_AutoTrack*)LIBMV_OBJECT_NEW(AutoTrack,
|
return (libmv_AutoTrack*)LIBMV_OBJECT_NEW(AutoTrack,
|
||||||
@@ -53,16 +53,13 @@ int libmv_autoTrackMarker(libmv_AutoTrack* libmv_autotrack,
|
|||||||
const libmv_TrackRegionOptions* libmv_options,
|
const libmv_TrackRegionOptions* libmv_options,
|
||||||
libmv_Marker* libmv_tracked_marker,
|
libmv_Marker* libmv_tracked_marker,
|
||||||
libmv_TrackRegionResult* libmv_result) {
|
libmv_TrackRegionResult* libmv_result) {
|
||||||
|
|
||||||
Marker tracked_marker;
|
Marker tracked_marker;
|
||||||
TrackRegionOptions options;
|
TrackRegionOptions options;
|
||||||
TrackRegionResult result;
|
TrackRegionResult result;
|
||||||
libmv_apiMarkerToMarker(*libmv_tracked_marker, &tracked_marker);
|
libmv_apiMarkerToMarker(*libmv_tracked_marker, &tracked_marker);
|
||||||
libmv_configureTrackRegionOptions(*libmv_options,
|
libmv_configureTrackRegionOptions(*libmv_options, &options);
|
||||||
&options);
|
bool ok = (((AutoTrack*)libmv_autotrack)
|
||||||
bool ok = (((AutoTrack*) libmv_autotrack)->TrackMarker(&tracked_marker,
|
->TrackMarker(&tracked_marker, &result, &options));
|
||||||
&result,
|
|
||||||
&options));
|
|
||||||
libmv_markerToApiMarker(tracked_marker, libmv_tracked_marker);
|
libmv_markerToApiMarker(tracked_marker, libmv_tracked_marker);
|
||||||
libmv_regionTrackergetResult(result, libmv_result);
|
libmv_regionTrackergetResult(result, libmv_result);
|
||||||
return ok && result.is_usable();
|
return ok && result.is_usable();
|
||||||
@@ -96,10 +93,8 @@ int libmv_autoTrackGetMarker(libmv_AutoTrack* libmv_autotrack,
|
|||||||
int track,
|
int track,
|
||||||
libmv_Marker* libmv_marker) {
|
libmv_Marker* libmv_marker) {
|
||||||
Marker marker;
|
Marker marker;
|
||||||
int ok = ((AutoTrack*) libmv_autotrack)->GetMarker(clip,
|
int ok =
|
||||||
frame,
|
((AutoTrack*)libmv_autotrack)->GetMarker(clip, frame, track, &marker);
|
||||||
track,
|
|
||||||
&marker);
|
|
||||||
if (ok) {
|
if (ok) {
|
||||||
libmv_markerToApiMarker(marker, libmv_marker);
|
libmv_markerToApiMarker(marker, libmv_marker);
|
||||||
}
|
}
|
||||||
|
@@ -21,9 +21,9 @@
|
|||||||
#define LIBMV_C_API_AUTOTRACK_H_
|
#define LIBMV_C_API_AUTOTRACK_H_
|
||||||
|
|
||||||
#include "intern/frame_accessor.h"
|
#include "intern/frame_accessor.h"
|
||||||
#include "intern/tracksN.h"
|
|
||||||
#include "intern/track_region.h"
|
|
||||||
#include "intern/region.h"
|
#include "intern/region.h"
|
||||||
|
#include "intern/track_region.h"
|
||||||
|
#include "intern/tracksN.h"
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
extern "C" {
|
extern "C" {
|
||||||
|
@@ -21,11 +21,11 @@
|
|||||||
#include "intern/utildefines.h"
|
#include "intern/utildefines.h"
|
||||||
#include "libmv/simple_pipeline/camera_intrinsics.h"
|
#include "libmv/simple_pipeline/camera_intrinsics.h"
|
||||||
|
|
||||||
|
using libmv::BrownCameraIntrinsics;
|
||||||
using libmv::CameraIntrinsics;
|
using libmv::CameraIntrinsics;
|
||||||
using libmv::DivisionCameraIntrinsics;
|
using libmv::DivisionCameraIntrinsics;
|
||||||
using libmv::PolynomialCameraIntrinsics;
|
|
||||||
using libmv::NukeCameraIntrinsics;
|
using libmv::NukeCameraIntrinsics;
|
||||||
using libmv::BrownCameraIntrinsics;
|
using libmv::PolynomialCameraIntrinsics;
|
||||||
|
|
||||||
libmv_CameraIntrinsics* libmv_cameraIntrinsicsNew(
|
libmv_CameraIntrinsics* libmv_cameraIntrinsicsNew(
|
||||||
const libmv_CameraIntrinsicsOptions* libmv_camera_intrinsics_options) {
|
const libmv_CameraIntrinsicsOptions* libmv_camera_intrinsics_options) {
|
||||||
@@ -41,40 +41,34 @@ libmv_CameraIntrinsics *libmv_cameraIntrinsicsCopy(
|
|||||||
|
|
||||||
CameraIntrinsics* new_intrinsics = NULL;
|
CameraIntrinsics* new_intrinsics = NULL;
|
||||||
switch (orig_intrinsics->GetDistortionModelType()) {
|
switch (orig_intrinsics->GetDistortionModelType()) {
|
||||||
case libmv::DISTORTION_MODEL_POLYNOMIAL:
|
case libmv::DISTORTION_MODEL_POLYNOMIAL: {
|
||||||
{
|
|
||||||
const PolynomialCameraIntrinsics* polynomial_intrinsics =
|
const PolynomialCameraIntrinsics* polynomial_intrinsics =
|
||||||
static_cast<const PolynomialCameraIntrinsics*>(orig_intrinsics);
|
static_cast<const PolynomialCameraIntrinsics*>(orig_intrinsics);
|
||||||
new_intrinsics = LIBMV_OBJECT_NEW(PolynomialCameraIntrinsics,
|
new_intrinsics =
|
||||||
*polynomial_intrinsics);
|
LIBMV_OBJECT_NEW(PolynomialCameraIntrinsics, *polynomial_intrinsics);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case libmv::DISTORTION_MODEL_DIVISION:
|
case libmv::DISTORTION_MODEL_DIVISION: {
|
||||||
{
|
|
||||||
const DivisionCameraIntrinsics* division_intrinsics =
|
const DivisionCameraIntrinsics* division_intrinsics =
|
||||||
static_cast<const DivisionCameraIntrinsics*>(orig_intrinsics);
|
static_cast<const DivisionCameraIntrinsics*>(orig_intrinsics);
|
||||||
new_intrinsics = LIBMV_OBJECT_NEW(DivisionCameraIntrinsics,
|
new_intrinsics =
|
||||||
*division_intrinsics);
|
LIBMV_OBJECT_NEW(DivisionCameraIntrinsics, *division_intrinsics);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case libmv::DISTORTION_MODEL_NUKE:
|
case libmv::DISTORTION_MODEL_NUKE: {
|
||||||
{
|
|
||||||
const NukeCameraIntrinsics* nuke_intrinsics =
|
const NukeCameraIntrinsics* nuke_intrinsics =
|
||||||
static_cast<const NukeCameraIntrinsics*>(orig_intrinsics);
|
static_cast<const NukeCameraIntrinsics*>(orig_intrinsics);
|
||||||
new_intrinsics = LIBMV_OBJECT_NEW(NukeCameraIntrinsics,
|
new_intrinsics = LIBMV_OBJECT_NEW(NukeCameraIntrinsics, *nuke_intrinsics);
|
||||||
*nuke_intrinsics);
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case libmv::DISTORTION_MODEL_BROWN:
|
case libmv::DISTORTION_MODEL_BROWN: {
|
||||||
{
|
|
||||||
const BrownCameraIntrinsics* brown_intrinsics =
|
const BrownCameraIntrinsics* brown_intrinsics =
|
||||||
static_cast<const BrownCameraIntrinsics*>(orig_intrinsics);
|
static_cast<const BrownCameraIntrinsics*>(orig_intrinsics);
|
||||||
new_intrinsics = LIBMV_OBJECT_NEW(BrownCameraIntrinsics,
|
new_intrinsics =
|
||||||
*brown_intrinsics);
|
LIBMV_OBJECT_NEW(BrownCameraIntrinsics, *brown_intrinsics);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
default:
|
default: assert(!"Unknown distortion model");
|
||||||
assert(!"Unknown distortion model");
|
|
||||||
}
|
}
|
||||||
return (libmv_CameraIntrinsics*)new_intrinsics;
|
return (libmv_CameraIntrinsics*)new_intrinsics;
|
||||||
}
|
}
|
||||||
@@ -115,8 +109,7 @@ void libmv_cameraIntrinsicsUpdate(
|
|||||||
}
|
}
|
||||||
|
|
||||||
switch (libmv_camera_intrinsics_options->distortion_model) {
|
switch (libmv_camera_intrinsics_options->distortion_model) {
|
||||||
case LIBMV_DISTORTION_MODEL_POLYNOMIAL:
|
case LIBMV_DISTORTION_MODEL_POLYNOMIAL: {
|
||||||
{
|
|
||||||
assert(camera_intrinsics->GetDistortionModelType() ==
|
assert(camera_intrinsics->GetDistortionModelType() ==
|
||||||
libmv::DISTORTION_MODEL_POLYNOMIAL);
|
libmv::DISTORTION_MODEL_POLYNOMIAL);
|
||||||
|
|
||||||
@@ -135,8 +128,7 @@ void libmv_cameraIntrinsicsUpdate(
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
case LIBMV_DISTORTION_MODEL_DIVISION:
|
case LIBMV_DISTORTION_MODEL_DIVISION: {
|
||||||
{
|
|
||||||
assert(camera_intrinsics->GetDistortionModelType() ==
|
assert(camera_intrinsics->GetDistortionModelType() ==
|
||||||
libmv::DISTORTION_MODEL_DIVISION);
|
libmv::DISTORTION_MODEL_DIVISION);
|
||||||
|
|
||||||
@@ -146,16 +138,14 @@ void libmv_cameraIntrinsicsUpdate(
|
|||||||
double k1 = libmv_camera_intrinsics_options->division_k1;
|
double k1 = libmv_camera_intrinsics_options->division_k1;
|
||||||
double k2 = libmv_camera_intrinsics_options->division_k2;
|
double k2 = libmv_camera_intrinsics_options->division_k2;
|
||||||
|
|
||||||
if (division_intrinsics->k1() != k1 ||
|
if (division_intrinsics->k1() != k1 || division_intrinsics->k2() != k2) {
|
||||||
division_intrinsics->k2() != k2) {
|
|
||||||
division_intrinsics->SetDistortion(k1, k2);
|
division_intrinsics->SetDistortion(k1, k2);
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
case LIBMV_DISTORTION_MODEL_NUKE:
|
case LIBMV_DISTORTION_MODEL_NUKE: {
|
||||||
{
|
|
||||||
assert(camera_intrinsics->GetDistortionModelType() ==
|
assert(camera_intrinsics->GetDistortionModelType() ==
|
||||||
libmv::DISTORTION_MODEL_NUKE);
|
libmv::DISTORTION_MODEL_NUKE);
|
||||||
|
|
||||||
@@ -165,16 +155,14 @@ void libmv_cameraIntrinsicsUpdate(
|
|||||||
double k1 = libmv_camera_intrinsics_options->nuke_k1;
|
double k1 = libmv_camera_intrinsics_options->nuke_k1;
|
||||||
double k2 = libmv_camera_intrinsics_options->nuke_k2;
|
double k2 = libmv_camera_intrinsics_options->nuke_k2;
|
||||||
|
|
||||||
if (nuke_intrinsics->k1() != k1 ||
|
if (nuke_intrinsics->k1() != k1 || nuke_intrinsics->k2() != k2) {
|
||||||
nuke_intrinsics->k2() != k2) {
|
|
||||||
nuke_intrinsics->SetDistortion(k1, k2);
|
nuke_intrinsics->SetDistortion(k1, k2);
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
case LIBMV_DISTORTION_MODEL_BROWN:
|
case LIBMV_DISTORTION_MODEL_BROWN: {
|
||||||
{
|
|
||||||
assert(camera_intrinsics->GetDistortionModelType() ==
|
assert(camera_intrinsics->GetDistortionModelType() ==
|
||||||
libmv::DISTORTION_MODEL_BROWN);
|
libmv::DISTORTION_MODEL_BROWN);
|
||||||
|
|
||||||
@@ -186,10 +174,8 @@ void libmv_cameraIntrinsicsUpdate(
|
|||||||
double k3 = libmv_camera_intrinsics_options->brown_k3;
|
double k3 = libmv_camera_intrinsics_options->brown_k3;
|
||||||
double k4 = libmv_camera_intrinsics_options->brown_k4;
|
double k4 = libmv_camera_intrinsics_options->brown_k4;
|
||||||
|
|
||||||
if (brown_intrinsics->k1() != k1 ||
|
if (brown_intrinsics->k1() != k1 || brown_intrinsics->k2() != k2 ||
|
||||||
brown_intrinsics->k2() != k2 ||
|
brown_intrinsics->k3() != k3 || brown_intrinsics->k4() != k4) {
|
||||||
brown_intrinsics->k3() != k3 ||
|
|
||||||
brown_intrinsics->k4() != k4) {
|
|
||||||
brown_intrinsics->SetRadialDistortion(k1, k2, k3, k4);
|
brown_intrinsics->SetRadialDistortion(k1, k2, k3, k4);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -202,8 +188,7 @@ void libmv_cameraIntrinsicsUpdate(
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
default:
|
default: assert(!"Unknown distortion model");
|
||||||
assert(!"Unknown distortion model");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -230,8 +215,7 @@ void libmv_cameraIntrinsicsExtractOptions(
|
|||||||
camera_intrinsics_options->image_height = camera_intrinsics->image_height();
|
camera_intrinsics_options->image_height = camera_intrinsics->image_height();
|
||||||
|
|
||||||
switch (camera_intrinsics->GetDistortionModelType()) {
|
switch (camera_intrinsics->GetDistortionModelType()) {
|
||||||
case libmv::DISTORTION_MODEL_POLYNOMIAL:
|
case libmv::DISTORTION_MODEL_POLYNOMIAL: {
|
||||||
{
|
|
||||||
const PolynomialCameraIntrinsics* polynomial_intrinsics =
|
const PolynomialCameraIntrinsics* polynomial_intrinsics =
|
||||||
static_cast<const PolynomialCameraIntrinsics*>(camera_intrinsics);
|
static_cast<const PolynomialCameraIntrinsics*>(camera_intrinsics);
|
||||||
camera_intrinsics_options->distortion_model =
|
camera_intrinsics_options->distortion_model =
|
||||||
@@ -244,8 +228,7 @@ void libmv_cameraIntrinsicsExtractOptions(
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
case libmv::DISTORTION_MODEL_DIVISION:
|
case libmv::DISTORTION_MODEL_DIVISION: {
|
||||||
{
|
|
||||||
const DivisionCameraIntrinsics* division_intrinsics =
|
const DivisionCameraIntrinsics* division_intrinsics =
|
||||||
static_cast<const DivisionCameraIntrinsics*>(camera_intrinsics);
|
static_cast<const DivisionCameraIntrinsics*>(camera_intrinsics);
|
||||||
camera_intrinsics_options->distortion_model =
|
camera_intrinsics_options->distortion_model =
|
||||||
@@ -255,19 +238,16 @@ void libmv_cameraIntrinsicsExtractOptions(
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
case libmv::DISTORTION_MODEL_NUKE:
|
case libmv::DISTORTION_MODEL_NUKE: {
|
||||||
{
|
|
||||||
const NukeCameraIntrinsics* nuke_intrinsics =
|
const NukeCameraIntrinsics* nuke_intrinsics =
|
||||||
static_cast<const NukeCameraIntrinsics*>(camera_intrinsics);
|
static_cast<const NukeCameraIntrinsics*>(camera_intrinsics);
|
||||||
camera_intrinsics_options->distortion_model =
|
camera_intrinsics_options->distortion_model = LIBMV_DISTORTION_MODEL_NUKE;
|
||||||
LIBMV_DISTORTION_MODEL_NUKE;
|
|
||||||
camera_intrinsics_options->nuke_k1 = nuke_intrinsics->k1();
|
camera_intrinsics_options->nuke_k1 = nuke_intrinsics->k1();
|
||||||
camera_intrinsics_options->nuke_k2 = nuke_intrinsics->k2();
|
camera_intrinsics_options->nuke_k2 = nuke_intrinsics->k2();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
case libmv::DISTORTION_MODEL_BROWN:
|
case libmv::DISTORTION_MODEL_BROWN: {
|
||||||
{
|
|
||||||
const BrownCameraIntrinsics* brown_intrinsics =
|
const BrownCameraIntrinsics* brown_intrinsics =
|
||||||
static_cast<const BrownCameraIntrinsics*>(camera_intrinsics);
|
static_cast<const BrownCameraIntrinsics*>(camera_intrinsics);
|
||||||
camera_intrinsics_options->distortion_model =
|
camera_intrinsics_options->distortion_model =
|
||||||
@@ -281,8 +261,7 @@ void libmv_cameraIntrinsicsExtractOptions(
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
default:
|
default: assert(!"Unknown distortion model");
|
||||||
assert(!"Unknown distortion model");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -295,11 +274,8 @@ void libmv_cameraIntrinsicsUndistortByte(
|
|||||||
int channels,
|
int channels,
|
||||||
unsigned char* destination_image) {
|
unsigned char* destination_image) {
|
||||||
CameraIntrinsics* camera_intrinsics = (CameraIntrinsics*)libmv_intrinsics;
|
CameraIntrinsics* camera_intrinsics = (CameraIntrinsics*)libmv_intrinsics;
|
||||||
camera_intrinsics->UndistortBuffer(source_image,
|
camera_intrinsics->UndistortBuffer(
|
||||||
width, height,
|
source_image, width, height, overscan, channels, destination_image);
|
||||||
overscan,
|
|
||||||
channels,
|
|
||||||
destination_image);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void libmv_cameraIntrinsicsUndistortFloat(
|
void libmv_cameraIntrinsicsUndistortFloat(
|
||||||
@@ -311,11 +287,8 @@ void libmv_cameraIntrinsicsUndistortFloat(
|
|||||||
int channels,
|
int channels,
|
||||||
float* destination_image) {
|
float* destination_image) {
|
||||||
CameraIntrinsics* intrinsics = (CameraIntrinsics*)libmv_intrinsics;
|
CameraIntrinsics* intrinsics = (CameraIntrinsics*)libmv_intrinsics;
|
||||||
intrinsics->UndistortBuffer(source_image,
|
intrinsics->UndistortBuffer(
|
||||||
width, height,
|
source_image, width, height, overscan, channels, destination_image);
|
||||||
overscan,
|
|
||||||
channels,
|
|
||||||
destination_image);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void libmv_cameraIntrinsicsDistortByte(
|
void libmv_cameraIntrinsicsDistortByte(
|
||||||
@@ -327,11 +300,8 @@ void libmv_cameraIntrinsicsDistortByte(
|
|||||||
int channels,
|
int channels,
|
||||||
unsigned char* destination_image) {
|
unsigned char* destination_image) {
|
||||||
CameraIntrinsics* intrinsics = (CameraIntrinsics*)libmv_intrinsics;
|
CameraIntrinsics* intrinsics = (CameraIntrinsics*)libmv_intrinsics;
|
||||||
intrinsics->DistortBuffer(source_image,
|
intrinsics->DistortBuffer(
|
||||||
width, height,
|
source_image, width, height, overscan, channels, destination_image);
|
||||||
overscan,
|
|
||||||
channels,
|
|
||||||
destination_image);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void libmv_cameraIntrinsicsDistortFloat(
|
void libmv_cameraIntrinsicsDistortFloat(
|
||||||
@@ -343,11 +313,8 @@ void libmv_cameraIntrinsicsDistortFloat(
|
|||||||
int channels,
|
int channels,
|
||||||
float* destination_image) {
|
float* destination_image) {
|
||||||
CameraIntrinsics* intrinsics = (CameraIntrinsics*)libmv_intrinsics;
|
CameraIntrinsics* intrinsics = (CameraIntrinsics*)libmv_intrinsics;
|
||||||
intrinsics->DistortBuffer(source_image,
|
intrinsics->DistortBuffer(
|
||||||
width, height,
|
source_image, width, height, overscan, channels, destination_image);
|
||||||
overscan,
|
|
||||||
channels,
|
|
||||||
destination_image);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void libmv_cameraIntrinsicsApply(
|
void libmv_cameraIntrinsicsApply(
|
||||||
@@ -384,8 +351,7 @@ static void libmv_cameraIntrinsicsFillFromOptions(
|
|||||||
camera_intrinsics_options->image_height);
|
camera_intrinsics_options->image_height);
|
||||||
|
|
||||||
switch (camera_intrinsics_options->distortion_model) {
|
switch (camera_intrinsics_options->distortion_model) {
|
||||||
case LIBMV_DISTORTION_MODEL_POLYNOMIAL:
|
case LIBMV_DISTORTION_MODEL_POLYNOMIAL: {
|
||||||
{
|
|
||||||
PolynomialCameraIntrinsics* polynomial_intrinsics =
|
PolynomialCameraIntrinsics* polynomial_intrinsics =
|
||||||
static_cast<PolynomialCameraIntrinsics*>(camera_intrinsics);
|
static_cast<PolynomialCameraIntrinsics*>(camera_intrinsics);
|
||||||
|
|
||||||
@@ -397,8 +363,7 @@ static void libmv_cameraIntrinsicsFillFromOptions(
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
case LIBMV_DISTORTION_MODEL_DIVISION:
|
case LIBMV_DISTORTION_MODEL_DIVISION: {
|
||||||
{
|
|
||||||
DivisionCameraIntrinsics* division_intrinsics =
|
DivisionCameraIntrinsics* division_intrinsics =
|
||||||
static_cast<DivisionCameraIntrinsics*>(camera_intrinsics);
|
static_cast<DivisionCameraIntrinsics*>(camera_intrinsics);
|
||||||
|
|
||||||
@@ -408,19 +373,16 @@ static void libmv_cameraIntrinsicsFillFromOptions(
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
case LIBMV_DISTORTION_MODEL_NUKE:
|
case LIBMV_DISTORTION_MODEL_NUKE: {
|
||||||
{
|
|
||||||
NukeCameraIntrinsics* nuke_intrinsics =
|
NukeCameraIntrinsics* nuke_intrinsics =
|
||||||
static_cast<NukeCameraIntrinsics*>(camera_intrinsics);
|
static_cast<NukeCameraIntrinsics*>(camera_intrinsics);
|
||||||
|
|
||||||
nuke_intrinsics->SetDistortion(
|
nuke_intrinsics->SetDistortion(camera_intrinsics_options->nuke_k1,
|
||||||
camera_intrinsics_options->nuke_k1,
|
|
||||||
camera_intrinsics_options->nuke_k2);
|
camera_intrinsics_options->nuke_k2);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
case LIBMV_DISTORTION_MODEL_BROWN:
|
case LIBMV_DISTORTION_MODEL_BROWN: {
|
||||||
{
|
|
||||||
BrownCameraIntrinsics* brown_intrinsics =
|
BrownCameraIntrinsics* brown_intrinsics =
|
||||||
static_cast<BrownCameraIntrinsics*>(camera_intrinsics);
|
static_cast<BrownCameraIntrinsics*>(camera_intrinsics);
|
||||||
|
|
||||||
@@ -436,8 +398,7 @@ static void libmv_cameraIntrinsicsFillFromOptions(
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
default:
|
default: assert(!"Unknown distortion model");
|
||||||
assert(!"Unknown distortion model");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -457,8 +418,7 @@ CameraIntrinsics* libmv_cameraIntrinsicsCreateFromOptions(
|
|||||||
case LIBMV_DISTORTION_MODEL_BROWN:
|
case LIBMV_DISTORTION_MODEL_BROWN:
|
||||||
camera_intrinsics = LIBMV_OBJECT_NEW(BrownCameraIntrinsics);
|
camera_intrinsics = LIBMV_OBJECT_NEW(BrownCameraIntrinsics);
|
||||||
break;
|
break;
|
||||||
default:
|
default: assert(!"Unknown distortion model");
|
||||||
assert(!"Unknown distortion model");
|
|
||||||
}
|
}
|
||||||
libmv_cameraIntrinsicsFillFromOptions(camera_intrinsics_options,
|
libmv_cameraIntrinsicsFillFromOptions(camera_intrinsics_options,
|
||||||
camera_intrinsics);
|
camera_intrinsics);
|
||||||
|
@@ -42,14 +42,12 @@ struct LibmvFrameAccessor : public FrameAccessor {
|
|||||||
get_mask_for_track_callback_(get_mask_for_track_callback),
|
get_mask_for_track_callback_(get_mask_for_track_callback),
|
||||||
release_mask_callback_(release_mask_callback) {}
|
release_mask_callback_(release_mask_callback) {}
|
||||||
|
|
||||||
virtual ~LibmvFrameAccessor() {
|
virtual ~LibmvFrameAccessor() {}
|
||||||
}
|
|
||||||
|
|
||||||
libmv_InputMode get_libmv_input_mode(InputMode input_mode) {
|
libmv_InputMode get_libmv_input_mode(InputMode input_mode) {
|
||||||
switch (input_mode) {
|
switch (input_mode) {
|
||||||
#define CHECK_INPUT_MODE(mode) \
|
#define CHECK_INPUT_MODE(mode) \
|
||||||
case mode: \
|
case mode: return LIBMV_IMAGE_MODE_##mode;
|
||||||
return LIBMV_IMAGE_MODE_ ## mode;
|
|
||||||
CHECK_INPUT_MODE(MONO)
|
CHECK_INPUT_MODE(MONO)
|
||||||
CHECK_INPUT_MODE(RGBA)
|
CHECK_INPUT_MODE(RGBA)
|
||||||
#undef CHECK_INPUT_MODE
|
#undef CHECK_INPUT_MODE
|
||||||
@@ -59,8 +57,7 @@ struct LibmvFrameAccessor : public FrameAccessor {
|
|||||||
return LIBMV_IMAGE_MODE_MONO;
|
return LIBMV_IMAGE_MODE_MONO;
|
||||||
}
|
}
|
||||||
|
|
||||||
void get_libmv_region(const Region& region,
|
void get_libmv_region(const Region& region, libmv_Region* libmv_region) {
|
||||||
libmv_Region* libmv_region) {
|
|
||||||
libmv_region->min[0] = region.min(0);
|
libmv_region->min[0] = region.min(0);
|
||||||
libmv_region->min[1] = region.min(1);
|
libmv_region->min[1] = region.min(1);
|
||||||
libmv_region->max[0] = region.max(0);
|
libmv_region->max[0] = region.max(0);
|
||||||
@@ -93,18 +90,13 @@ struct LibmvFrameAccessor : public FrameAccessor {
|
|||||||
&channels);
|
&channels);
|
||||||
|
|
||||||
// TODO(sergey): Dumb code for until we can set data directly.
|
// TODO(sergey): Dumb code for until we can set data directly.
|
||||||
FloatImage temp_image(float_buffer,
|
FloatImage temp_image(float_buffer, height, width, channels);
|
||||||
height,
|
|
||||||
width,
|
|
||||||
channels);
|
|
||||||
destination->CopyFrom(temp_image);
|
destination->CopyFrom(temp_image);
|
||||||
|
|
||||||
return cache_key;
|
return cache_key;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ReleaseImage(Key cache_key) {
|
void ReleaseImage(Key cache_key) { release_image_callback_(cache_key); }
|
||||||
release_image_callback_(cache_key);
|
|
||||||
}
|
|
||||||
|
|
||||||
Key GetMaskForTrack(int clip,
|
Key GetMaskForTrack(int clip,
|
||||||
int frame,
|
int frame,
|
||||||
@@ -117,8 +109,8 @@ struct LibmvFrameAccessor : public FrameAccessor {
|
|||||||
if (region) {
|
if (region) {
|
||||||
get_libmv_region(*region, &libmv_region);
|
get_libmv_region(*region, &libmv_region);
|
||||||
}
|
}
|
||||||
Key cache_key = get_mask_for_track_callback_(
|
Key cache_key =
|
||||||
user_data_,
|
get_mask_for_track_callback_(user_data_,
|
||||||
clip,
|
clip,
|
||||||
frame,
|
frame,
|
||||||
track,
|
track,
|
||||||
@@ -133,30 +125,21 @@ struct LibmvFrameAccessor : public FrameAccessor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// TODO(sergey): Dumb code for until we can set data directly.
|
// TODO(sergey): Dumb code for until we can set data directly.
|
||||||
FloatImage temp_image(float_buffer,
|
FloatImage temp_image(float_buffer, height, width, 1);
|
||||||
height,
|
|
||||||
width,
|
|
||||||
1);
|
|
||||||
destination->CopyFrom(temp_image);
|
destination->CopyFrom(temp_image);
|
||||||
|
|
||||||
return cache_key;
|
return cache_key;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ReleaseMask(Key key) {
|
void ReleaseMask(Key key) { release_mask_callback_(key); }
|
||||||
release_mask_callback_(key);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool GetClipDimensions(int /*clip*/, int* /*width*/, int* /*height*/) {
|
bool GetClipDimensions(int /*clip*/, int* /*width*/, int* /*height*/) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
int NumClips() {
|
int NumClips() { return 1; }
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
int NumFrames(int /*clip*/) {
|
int NumFrames(int /*clip*/) { return 0; }
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
libmv_FrameAccessorUserData* user_data_;
|
libmv_FrameAccessorUserData* user_data_;
|
||||||
libmv_GetImageCallback get_image_callback_;
|
libmv_GetImageCallback get_image_callback_;
|
||||||
@@ -185,7 +168,8 @@ void libmv_FrameAccessorDestroy(libmv_FrameAccessor* frame_accessor) {
|
|||||||
LIBMV_OBJECT_DELETE(frame_accessor, LibmvFrameAccessor);
|
LIBMV_OBJECT_DELETE(frame_accessor, LibmvFrameAccessor);
|
||||||
}
|
}
|
||||||
|
|
||||||
int64_t libmv_frameAccessorgetTransformKey(const libmv_FrameTransform *transform) {
|
int64_t libmv_frameAccessorgetTransformKey(
|
||||||
|
const libmv_FrameTransform* transform) {
|
||||||
return ((FrameAccessor::Transform*)transform)->key();
|
return ((FrameAccessor::Transform*)transform)->key();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -198,8 +182,7 @@ void libmv_frameAccessorgetTransformRun(const libmv_FrameTransform *transform,
|
|||||||
input_image->channels);
|
input_image->channels);
|
||||||
|
|
||||||
FloatImage output;
|
FloatImage output;
|
||||||
((FrameAccessor::Transform*) transform)->run(input,
|
((FrameAccessor::Transform*)transform)->run(input, &output);
|
||||||
&output);
|
|
||||||
|
|
||||||
int num_pixels = output.Width() * output.Height() * output.Depth();
|
int num_pixels = output.Width() * output.Height() * output.Depth();
|
||||||
output_image->buffer = new float[num_pixels];
|
output_image->buffer = new float[num_pixels];
|
||||||
|
@@ -73,7 +73,8 @@ libmv_FrameAccessor* libmv_FrameAccessorNew(
|
|||||||
libmv_ReleaseMaskCallback release_mask_callback);
|
libmv_ReleaseMaskCallback release_mask_callback);
|
||||||
void libmv_FrameAccessorDestroy(libmv_FrameAccessor* frame_accessor);
|
void libmv_FrameAccessorDestroy(libmv_FrameAccessor* frame_accessor);
|
||||||
|
|
||||||
int64_t libmv_frameAccessorgetTransformKey(const libmv_FrameTransform *transform);
|
int64_t libmv_frameAccessorgetTransformKey(
|
||||||
|
const libmv_FrameTransform* transform);
|
||||||
|
|
||||||
void libmv_frameAccessorgetTransformRun(const libmv_FrameTransform* transform,
|
void libmv_frameAccessorgetTransformRun(const libmv_FrameTransform* transform,
|
||||||
const libmv_FloatImage* input_image,
|
const libmv_FloatImage* input_image,
|
||||||
|
@@ -41,10 +41,8 @@ void libmv_homography2DFromCorrespondencesEuc(/* const */ double (*x1)[2],
|
|||||||
LG << "x2: " << x2_mat;
|
LG << "x2: " << x2_mat;
|
||||||
|
|
||||||
libmv::EstimateHomographyOptions options;
|
libmv::EstimateHomographyOptions options;
|
||||||
libmv::EstimateHomography2DFromCorrespondences(x1_mat,
|
libmv::EstimateHomography2DFromCorrespondences(
|
||||||
x2_mat,
|
x1_mat, x2_mat, options, &H_mat);
|
||||||
options,
|
|
||||||
&H_mat);
|
|
||||||
|
|
||||||
LG << "H: " << H_mat;
|
LG << "H: " << H_mat;
|
||||||
|
|
||||||
|
@@ -21,8 +21,8 @@
|
|||||||
#include "intern/utildefines.h"
|
#include "intern/utildefines.h"
|
||||||
#include "libmv/tracking/track_region.h"
|
#include "libmv/tracking/track_region.h"
|
||||||
|
|
||||||
#include <cassert>
|
|
||||||
#include <png.h>
|
#include <png.h>
|
||||||
|
#include <cassert>
|
||||||
|
|
||||||
using libmv::FloatImage;
|
using libmv::FloatImage;
|
||||||
using libmv::SamplePlanarPatch;
|
using libmv::SamplePlanarPatch;
|
||||||
@@ -63,8 +63,7 @@ void libmv_floatBufferToFloatImage(const float* buffer,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void libmv_floatImageToFloatBuffer(const FloatImage &image,
|
void libmv_floatImageToFloatBuffer(const FloatImage& image, float* buffer) {
|
||||||
float* buffer) {
|
|
||||||
for (int y = 0, a = 0; y < image.Height(); y++) {
|
for (int y = 0, a = 0; y < image.Height(); y++) {
|
||||||
for (int x = 0; x < image.Width(); x++) {
|
for (int x = 0; x < image.Width(); x++) {
|
||||||
for (int k = 0; k < image.Depth(); k++) {
|
for (int k = 0; k < image.Depth(); k++) {
|
||||||
@@ -180,9 +179,8 @@ bool libmv_saveImage(const FloatImage& image,
|
|||||||
|
|
||||||
static int image_counter = 0;
|
static int image_counter = 0;
|
||||||
char file_name[128];
|
char file_name[128];
|
||||||
snprintf(file_name, sizeof(file_name),
|
snprintf(
|
||||||
"%s_%02d.png",
|
file_name, sizeof(file_name), "%s_%02d.png", prefix, ++image_counter);
|
||||||
prefix, ++image_counter);
|
|
||||||
bool result = savePNGImage(row_pointers,
|
bool result = savePNGImage(row_pointers,
|
||||||
image.Width(),
|
image.Width(),
|
||||||
image.Height(),
|
image.Height(),
|
||||||
@@ -221,8 +219,10 @@ void libmv_samplePlanarPatchFloat(const float* image,
|
|||||||
}
|
}
|
||||||
|
|
||||||
SamplePlanarPatch(libmv_image,
|
SamplePlanarPatch(libmv_image,
|
||||||
xs, ys,
|
xs,
|
||||||
num_samples_x, num_samples_y,
|
ys,
|
||||||
|
num_samples_x,
|
||||||
|
num_samples_y,
|
||||||
libmv_mask_for_sample,
|
libmv_mask_for_sample,
|
||||||
&libmv_patch,
|
&libmv_patch,
|
||||||
warped_position_x,
|
warped_position_x,
|
||||||
@@ -254,8 +254,10 @@ void libmv_samplePlanarPatchByte(const unsigned char* image,
|
|||||||
}
|
}
|
||||||
|
|
||||||
libmv::SamplePlanarPatch(libmv_image,
|
libmv::SamplePlanarPatch(libmv_image,
|
||||||
xs, ys,
|
xs,
|
||||||
num_samples_x, num_samples_y,
|
ys,
|
||||||
|
num_samples_x,
|
||||||
|
num_samples_y,
|
||||||
libmv_mask_for_sample,
|
libmv_mask_for_sample,
|
||||||
&libmv_patch,
|
&libmv_patch,
|
||||||
warped_position_x,
|
warped_position_x,
|
||||||
|
@@ -24,8 +24,8 @@
|
|||||||
|
|
||||||
#include "libmv/logging/logging.h"
|
#include "libmv/logging/logging.h"
|
||||||
#include "libmv/simple_pipeline/bundle.h"
|
#include "libmv/simple_pipeline/bundle.h"
|
||||||
#include "libmv/simple_pipeline/keyframe_selection.h"
|
|
||||||
#include "libmv/simple_pipeline/initialize_reconstruction.h"
|
#include "libmv/simple_pipeline/initialize_reconstruction.h"
|
||||||
|
#include "libmv/simple_pipeline/keyframe_selection.h"
|
||||||
#include "libmv/simple_pipeline/modal_solver.h"
|
#include "libmv/simple_pipeline/modal_solver.h"
|
||||||
#include "libmv/simple_pipeline/pipeline.h"
|
#include "libmv/simple_pipeline/pipeline.h"
|
||||||
#include "libmv/simple_pipeline/reconstruction_scale.h"
|
#include "libmv/simple_pipeline/reconstruction_scale.h"
|
||||||
@@ -39,12 +39,12 @@ using libmv::EuclideanScaleToUnity;
|
|||||||
using libmv::Marker;
|
using libmv::Marker;
|
||||||
using libmv::ProgressUpdateCallback;
|
using libmv::ProgressUpdateCallback;
|
||||||
|
|
||||||
using libmv::PolynomialCameraIntrinsics;
|
|
||||||
using libmv::Tracks;
|
|
||||||
using libmv::EuclideanBundle;
|
using libmv::EuclideanBundle;
|
||||||
using libmv::EuclideanCompleteReconstruction;
|
using libmv::EuclideanCompleteReconstruction;
|
||||||
using libmv::EuclideanReconstructTwoFrames;
|
using libmv::EuclideanReconstructTwoFrames;
|
||||||
using libmv::EuclideanReprojectionError;
|
using libmv::EuclideanReprojectionError;
|
||||||
|
using libmv::PolynomialCameraIntrinsics;
|
||||||
|
using libmv::Tracks;
|
||||||
|
|
||||||
struct libmv_Reconstruction {
|
struct libmv_Reconstruction {
|
||||||
EuclideanReconstruction reconstruction;
|
EuclideanReconstruction reconstruction;
|
||||||
@@ -73,6 +73,7 @@ class ReconstructUpdateCallback : public ProgressUpdateCallback {
|
|||||||
progress_update_callback_(callback_customdata_, progress, message);
|
progress_update_callback_(callback_customdata_, progress, message);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
reconstruct_progress_update_cb progress_update_callback_;
|
reconstruct_progress_update_cb progress_update_callback_;
|
||||||
void* callback_customdata_;
|
void* callback_customdata_;
|
||||||
@@ -134,9 +135,8 @@ void finishReconstruction(
|
|||||||
/* Reprojection error calculation. */
|
/* Reprojection error calculation. */
|
||||||
progress_update_callback(callback_customdata, 1.0, "Finishing solution");
|
progress_update_callback(callback_customdata, 1.0, "Finishing solution");
|
||||||
libmv_reconstruction->tracks = tracks;
|
libmv_reconstruction->tracks = tracks;
|
||||||
libmv_reconstruction->error = EuclideanReprojectionError(tracks,
|
libmv_reconstruction->error =
|
||||||
reconstruction,
|
EuclideanReprojectionError(tracks, reconstruction, camera_intrinsics);
|
||||||
camera_intrinsics);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool selectTwoKeyframesBasedOnGRICAndVariance(
|
bool selectTwoKeyframesBasedOnGRICAndVariance(
|
||||||
@@ -148,9 +148,8 @@ bool selectTwoKeyframesBasedOnGRICAndVariance(
|
|||||||
libmv::vector<int> keyframes;
|
libmv::vector<int> keyframes;
|
||||||
|
|
||||||
/* Get list of all keyframe candidates first. */
|
/* Get list of all keyframe candidates first. */
|
||||||
SelectKeyframesBasedOnGRICAndVariance(normalized_tracks,
|
SelectKeyframesBasedOnGRICAndVariance(
|
||||||
camera_intrinsics,
|
normalized_tracks, camera_intrinsics, keyframes);
|
||||||
keyframes);
|
|
||||||
|
|
||||||
if (keyframes.size() < 2) {
|
if (keyframes.size() < 2) {
|
||||||
LG << "Not enough keyframes detected by GRIC";
|
LG << "Not enough keyframes detected by GRIC";
|
||||||
@@ -183,16 +182,12 @@ bool selectTwoKeyframesBasedOnGRICAndVariance(
|
|||||||
/* get a solution from two keyframes only */
|
/* get a solution from two keyframes only */
|
||||||
EuclideanReconstructTwoFrames(keyframe_markers, &reconstruction);
|
EuclideanReconstructTwoFrames(keyframe_markers, &reconstruction);
|
||||||
EuclideanBundle(keyframe_tracks, &reconstruction);
|
EuclideanBundle(keyframe_tracks, &reconstruction);
|
||||||
EuclideanCompleteReconstruction(keyframe_tracks,
|
EuclideanCompleteReconstruction(keyframe_tracks, &reconstruction, NULL);
|
||||||
&reconstruction,
|
|
||||||
NULL);
|
|
||||||
|
|
||||||
double current_error = EuclideanReprojectionError(tracks,
|
double current_error =
|
||||||
reconstruction,
|
EuclideanReprojectionError(tracks, reconstruction, camera_intrinsics);
|
||||||
camera_intrinsics);
|
|
||||||
|
|
||||||
LG << "Error between " << previous_keyframe
|
LG << "Error between " << previous_keyframe << " and " << current_keyframe
|
||||||
<< " and " << current_keyframe
|
|
||||||
<< ": " << current_error;
|
<< ": " << current_error;
|
||||||
|
|
||||||
if (current_error < best_error) {
|
if (current_error < best_error) {
|
||||||
@@ -214,9 +209,8 @@ Marker libmv_projectMarker(const EuclideanPoint& point,
|
|||||||
projected /= projected(2);
|
projected /= projected(2);
|
||||||
|
|
||||||
libmv::Marker reprojected_marker;
|
libmv::Marker reprojected_marker;
|
||||||
intrinsics.ApplyIntrinsics(projected(0), projected(1),
|
intrinsics.ApplyIntrinsics(
|
||||||
&reprojected_marker.x,
|
projected(0), projected(1), &reprojected_marker.x, &reprojected_marker.y);
|
||||||
&reprojected_marker.y);
|
|
||||||
|
|
||||||
reprojected_marker.image = camera.image;
|
reprojected_marker.image = camera.image;
|
||||||
reprojected_marker.track = point.track;
|
reprojected_marker.track = point.track;
|
||||||
@@ -229,12 +223,10 @@ void libmv_getNormalizedTracks(const Tracks &tracks,
|
|||||||
libmv::vector<Marker> markers = tracks.AllMarkers();
|
libmv::vector<Marker> markers = tracks.AllMarkers();
|
||||||
for (int i = 0; i < markers.size(); ++i) {
|
for (int i = 0; i < markers.size(); ++i) {
|
||||||
Marker& marker = markers[i];
|
Marker& marker = markers[i];
|
||||||
camera_intrinsics.InvertIntrinsics(marker.x, marker.y,
|
camera_intrinsics.InvertIntrinsics(
|
||||||
&marker.x, &marker.y);
|
marker.x, marker.y, &marker.x, &marker.y);
|
||||||
normalized_tracks->Insert(marker.image,
|
normalized_tracks->Insert(
|
||||||
marker.track,
|
marker.image, marker.track, marker.x, marker.y, marker.weight);
|
||||||
marker.x, marker.y,
|
|
||||||
marker.weight);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -254,8 +246,7 @@ libmv_Reconstruction *libmv_solveReconstruction(
|
|||||||
libmv_reconstruction->reconstruction;
|
libmv_reconstruction->reconstruction;
|
||||||
|
|
||||||
ReconstructUpdateCallback update_callback =
|
ReconstructUpdateCallback update_callback =
|
||||||
ReconstructUpdateCallback(progress_update_callback,
|
ReconstructUpdateCallback(progress_update_callback, callback_customdata);
|
||||||
callback_customdata);
|
|
||||||
|
|
||||||
/* Retrieve reconstruction options from C-API to libmv API. */
|
/* Retrieve reconstruction options from C-API to libmv API. */
|
||||||
CameraIntrinsics* camera_intrinsics;
|
CameraIntrinsics* camera_intrinsics;
|
||||||
@@ -309,14 +300,12 @@ libmv_Reconstruction *libmv_solveReconstruction(
|
|||||||
}
|
}
|
||||||
|
|
||||||
EuclideanBundle(normalized_tracks, &reconstruction);
|
EuclideanBundle(normalized_tracks, &reconstruction);
|
||||||
EuclideanCompleteReconstruction(normalized_tracks,
|
EuclideanCompleteReconstruction(
|
||||||
&reconstruction,
|
normalized_tracks, &reconstruction, &update_callback);
|
||||||
&update_callback);
|
|
||||||
|
|
||||||
/* Refinement. */
|
/* Refinement. */
|
||||||
if (libmv_reconstruction_options->refine_intrinsics) {
|
if (libmv_reconstruction_options->refine_intrinsics) {
|
||||||
libmv_solveRefineIntrinsics(
|
libmv_solveRefineIntrinsics(tracks,
|
||||||
tracks,
|
|
||||||
libmv_reconstruction_options->refine_intrinsics,
|
libmv_reconstruction_options->refine_intrinsics,
|
||||||
libmv::BUNDLE_NO_CONSTRAINTS,
|
libmv::BUNDLE_NO_CONSTRAINTS,
|
||||||
progress_update_callback,
|
progress_update_callback,
|
||||||
@@ -353,14 +342,12 @@ libmv_Reconstruction *libmv_solveModal(
|
|||||||
libmv_reconstruction->reconstruction;
|
libmv_reconstruction->reconstruction;
|
||||||
|
|
||||||
ReconstructUpdateCallback update_callback =
|
ReconstructUpdateCallback update_callback =
|
||||||
ReconstructUpdateCallback(progress_update_callback,
|
ReconstructUpdateCallback(progress_update_callback, callback_customdata);
|
||||||
callback_customdata);
|
|
||||||
|
|
||||||
/* Retrieve reconstruction options from C-API to libmv API. */
|
/* Retrieve reconstruction options from C-API to libmv API. */
|
||||||
CameraIntrinsics* camera_intrinsics;
|
CameraIntrinsics* camera_intrinsics;
|
||||||
camera_intrinsics = libmv_reconstruction->intrinsics =
|
camera_intrinsics = libmv_reconstruction->intrinsics =
|
||||||
libmv_cameraIntrinsicsCreateFromOptions(
|
libmv_cameraIntrinsicsCreateFromOptions(libmv_camera_intrinsics_options);
|
||||||
libmv_camera_intrinsics_options);
|
|
||||||
|
|
||||||
/* Invert the camera intrinsics. */
|
/* Invert the camera intrinsics. */
|
||||||
Tracks normalized_tracks;
|
Tracks normalized_tracks;
|
||||||
@@ -378,11 +365,11 @@ libmv_Reconstruction *libmv_solveModal(
|
|||||||
|
|
||||||
/* Refinement. */
|
/* Refinement. */
|
||||||
if (libmv_reconstruction_options->refine_intrinsics) {
|
if (libmv_reconstruction_options->refine_intrinsics) {
|
||||||
libmv_solveRefineIntrinsics(
|
libmv_solveRefineIntrinsics(tracks,
|
||||||
tracks,
|
|
||||||
libmv_reconstruction_options->refine_intrinsics,
|
libmv_reconstruction_options->refine_intrinsics,
|
||||||
libmv::BUNDLE_NO_TRANSLATION,
|
libmv::BUNDLE_NO_TRANSLATION,
|
||||||
progress_update_callback, callback_customdata,
|
progress_update_callback,
|
||||||
|
callback_customdata,
|
||||||
&reconstruction,
|
&reconstruction,
|
||||||
camera_intrinsics);
|
camera_intrinsics);
|
||||||
}
|
}
|
||||||
@@ -413,8 +400,7 @@ int libmv_reprojectionPointForTrack(
|
|||||||
double pos[3]) {
|
double pos[3]) {
|
||||||
const EuclideanReconstruction* reconstruction =
|
const EuclideanReconstruction* reconstruction =
|
||||||
&libmv_reconstruction->reconstruction;
|
&libmv_reconstruction->reconstruction;
|
||||||
const EuclideanPoint *point =
|
const EuclideanPoint* point = reconstruction->PointForTrack(track);
|
||||||
reconstruction->PointForTrack(track);
|
|
||||||
if (point) {
|
if (point) {
|
||||||
pos[0] = point->X[0];
|
pos[0] = point->X[0];
|
||||||
pos[1] = point->X[2];
|
pos[1] = point->X[2];
|
||||||
@@ -425,8 +411,7 @@ int libmv_reprojectionPointForTrack(
|
|||||||
}
|
}
|
||||||
|
|
||||||
double libmv_reprojectionErrorForTrack(
|
double libmv_reprojectionErrorForTrack(
|
||||||
const libmv_Reconstruction *libmv_reconstruction,
|
const libmv_Reconstruction* libmv_reconstruction, int track) {
|
||||||
int track) {
|
|
||||||
const EuclideanReconstruction* reconstruction =
|
const EuclideanReconstruction* reconstruction =
|
||||||
&libmv_reconstruction->reconstruction;
|
&libmv_reconstruction->reconstruction;
|
||||||
const CameraIntrinsics* intrinsics = libmv_reconstruction->intrinsics;
|
const CameraIntrinsics* intrinsics = libmv_reconstruction->intrinsics;
|
||||||
@@ -461,8 +446,7 @@ double libmv_reprojectionErrorForTrack(
|
|||||||
}
|
}
|
||||||
|
|
||||||
double libmv_reprojectionErrorForImage(
|
double libmv_reprojectionErrorForImage(
|
||||||
const libmv_Reconstruction *libmv_reconstruction,
|
const libmv_Reconstruction* libmv_reconstruction, int image) {
|
||||||
int image) {
|
|
||||||
const EuclideanReconstruction* reconstruction =
|
const EuclideanReconstruction* reconstruction =
|
||||||
&libmv_reconstruction->reconstruction;
|
&libmv_reconstruction->reconstruction;
|
||||||
const CameraIntrinsics* intrinsics = libmv_reconstruction->intrinsics;
|
const CameraIntrinsics* intrinsics = libmv_reconstruction->intrinsics;
|
||||||
@@ -503,8 +487,7 @@ int libmv_reprojectionCameraForImage(
|
|||||||
double mat[4][4]) {
|
double mat[4][4]) {
|
||||||
const EuclideanReconstruction* reconstruction =
|
const EuclideanReconstruction* reconstruction =
|
||||||
&libmv_reconstruction->reconstruction;
|
&libmv_reconstruction->reconstruction;
|
||||||
const EuclideanCamera *camera =
|
const EuclideanCamera* camera = reconstruction->CameraForImage(image);
|
||||||
reconstruction->CameraForImage(image);
|
|
||||||
|
|
||||||
if (camera) {
|
if (camera) {
|
||||||
for (int j = 0; j < 3; ++j) {
|
for (int j = 0; j < 3; ++j) {
|
||||||
|
@@ -38,10 +38,9 @@ enum {
|
|||||||
LIBMV_REFINE_RADIAL_DISTORTION_K2 = (1 << 3),
|
LIBMV_REFINE_RADIAL_DISTORTION_K2 = (1 << 3),
|
||||||
LIBMV_REFINE_RADIAL_DISTORTION_K3 = (1 << 4),
|
LIBMV_REFINE_RADIAL_DISTORTION_K3 = (1 << 4),
|
||||||
LIBMV_REFINE_RADIAL_DISTORTION_K4 = (1 << 5),
|
LIBMV_REFINE_RADIAL_DISTORTION_K4 = (1 << 5),
|
||||||
LIBMV_REFINE_RADIAL_DISTORTION = (LIBMV_REFINE_RADIAL_DISTORTION_K1 |
|
LIBMV_REFINE_RADIAL_DISTORTION =
|
||||||
LIBMV_REFINE_RADIAL_DISTORTION_K2 |
|
(LIBMV_REFINE_RADIAL_DISTORTION_K1 | LIBMV_REFINE_RADIAL_DISTORTION_K2 |
|
||||||
LIBMV_REFINE_RADIAL_DISTORTION_K3 |
|
LIBMV_REFINE_RADIAL_DISTORTION_K3 | LIBMV_REFINE_RADIAL_DISTORTION_K4),
|
||||||
LIBMV_REFINE_RADIAL_DISTORTION_K4),
|
|
||||||
|
|
||||||
LIBMV_REFINE_TANGENTIAL_DISTORTION_P1 = (1 << 6),
|
LIBMV_REFINE_TANGENTIAL_DISTORTION_P1 = (1 << 6),
|
||||||
LIBMV_REFINE_TANGENTIAL_DISTORTION_P2 = (1 << 7),
|
LIBMV_REFINE_TANGENTIAL_DISTORTION_P2 = (1 << 7),
|
||||||
@@ -78,24 +77,21 @@ int libmv_reconstructionIsValid(libmv_Reconstruction *libmv_reconstruction);
|
|||||||
void libmv_reconstructionDestroy(libmv_Reconstruction* libmv_reconstruction);
|
void libmv_reconstructionDestroy(libmv_Reconstruction* libmv_reconstruction);
|
||||||
|
|
||||||
int libmv_reprojectionPointForTrack(
|
int libmv_reprojectionPointForTrack(
|
||||||
const libmv_Reconstruction* libmv_reconstruction,
|
const libmv_Reconstruction* libmv_reconstruction, int track, double pos[3]);
|
||||||
int track,
|
|
||||||
double pos[3]);
|
|
||||||
|
|
||||||
double libmv_reprojectionErrorForTrack(
|
double libmv_reprojectionErrorForTrack(
|
||||||
const libmv_Reconstruction* libmv_reconstruction,
|
const libmv_Reconstruction* libmv_reconstruction, int track);
|
||||||
int track);
|
|
||||||
|
|
||||||
double libmv_reprojectionErrorForImage(
|
double libmv_reprojectionErrorForImage(
|
||||||
const libmv_Reconstruction* libmv_reconstruction,
|
const libmv_Reconstruction* libmv_reconstruction, int image);
|
||||||
int image);
|
|
||||||
|
|
||||||
int libmv_reprojectionCameraForImage(
|
int libmv_reprojectionCameraForImage(
|
||||||
const libmv_Reconstruction* libmv_reconstruction,
|
const libmv_Reconstruction* libmv_reconstruction,
|
||||||
int image,
|
int image,
|
||||||
double mat[4][4]);
|
double mat[4][4]);
|
||||||
|
|
||||||
double libmv_reprojectionError(const libmv_Reconstruction* libmv_reconstruction);
|
double libmv_reprojectionError(
|
||||||
|
const libmv_Reconstruction* libmv_reconstruction);
|
||||||
|
|
||||||
struct libmv_CameraIntrinsics* libmv_reconstructionExtractIntrinsics(
|
struct libmv_CameraIntrinsics* libmv_reconstructionExtractIntrinsics(
|
||||||
libmv_Reconstruction* libmv_Reconstruction);
|
libmv_Reconstruction* libmv_Reconstruction);
|
||||||
|
@@ -82,7 +82,8 @@ void libmv_samplePlanarPatchByte(const unsigned char * /*image*/,
|
|||||||
int /*channels*/,
|
int /*channels*/,
|
||||||
const double* /*xs*/,
|
const double* /*xs*/,
|
||||||
const double* /*ys*/,
|
const double* /*ys*/,
|
||||||
int /*num_samples_x*/, int /*num_samples_y*/,
|
int /*num_samples_x*/,
|
||||||
|
int /*num_samples_y*/,
|
||||||
const float* /*mask*/,
|
const float* /*mask*/,
|
||||||
unsigned char* /*patch*/,
|
unsigned char* /*patch*/,
|
||||||
double* /*warped_position_x*/,
|
double* /*warped_position_x*/,
|
||||||
@@ -90,8 +91,7 @@ void libmv_samplePlanarPatchByte(const unsigned char * /*image*/,
|
|||||||
/* TODO(sergey): implement */
|
/* TODO(sergey): implement */
|
||||||
}
|
}
|
||||||
|
|
||||||
void libmv_floatImageDestroy(libmv_FloatImage* /*image*/)
|
void libmv_floatImageDestroy(libmv_FloatImage* /*image*/) {
|
||||||
{
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ************ Tracks ************ */
|
/* ************ Tracks ************ */
|
||||||
@@ -131,7 +131,8 @@ libmv_Reconstruction *libmv_solveModal(
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
int libmv_reconstructionIsValid(libmv_Reconstruction * /*libmv_reconstruction*/) {
|
int libmv_reconstructionIsValid(
|
||||||
|
libmv_Reconstruction* /*libmv_reconstruction*/) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -143,14 +144,12 @@ int libmv_reprojectionPointForTrack(
|
|||||||
}
|
}
|
||||||
|
|
||||||
double libmv_reprojectionErrorForTrack(
|
double libmv_reprojectionErrorForTrack(
|
||||||
const libmv_Reconstruction * /*libmv_reconstruction*/,
|
const libmv_Reconstruction* /*libmv_reconstruction*/, int /*track*/) {
|
||||||
int /*track*/) {
|
|
||||||
return 0.0;
|
return 0.0;
|
||||||
}
|
}
|
||||||
|
|
||||||
double libmv_reprojectionErrorForImage(
|
double libmv_reprojectionErrorForImage(
|
||||||
const libmv_Reconstruction * /*libmv_reconstruction*/,
|
const libmv_Reconstruction* /*libmv_reconstruction*/, int /*image*/) {
|
||||||
int /*image*/) {
|
|
||||||
return 0.0;
|
return 0.0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -235,8 +234,7 @@ void libmv_cameraIntrinsicsUpdate(
|
|||||||
}
|
}
|
||||||
|
|
||||||
void libmv_cameraIntrinsicsSetThreads(
|
void libmv_cameraIntrinsicsSetThreads(
|
||||||
libmv_CameraIntrinsics * /*libmv_intrinsics*/,
|
libmv_CameraIntrinsics* /*libmv_intrinsics*/, int /*threads*/) {
|
||||||
int /*threads*/) {
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void libmv_cameraIntrinsicsExtractOptions(
|
void libmv_cameraIntrinsicsExtractOptions(
|
||||||
@@ -249,11 +247,13 @@ void libmv_cameraIntrinsicsExtractOptions(
|
|||||||
void libmv_cameraIntrinsicsUndistortByte(
|
void libmv_cameraIntrinsicsUndistortByte(
|
||||||
const libmv_CameraIntrinsics* /*libmv_intrinsics*/,
|
const libmv_CameraIntrinsics* /*libmv_intrinsics*/,
|
||||||
const unsigned char* source_image,
|
const unsigned char* source_image,
|
||||||
int width, int height,
|
int width,
|
||||||
|
int height,
|
||||||
float /*overscan*/,
|
float /*overscan*/,
|
||||||
int channels,
|
int channels,
|
||||||
unsigned char* destination_image) {
|
unsigned char* destination_image) {
|
||||||
memcpy(destination_image, source_image,
|
memcpy(destination_image,
|
||||||
|
source_image,
|
||||||
channels * width * height * sizeof(unsigned char));
|
channels * width * height * sizeof(unsigned char));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -265,7 +265,8 @@ void libmv_cameraIntrinsicsUndistortFloat(
|
|||||||
float /*overscan*/,
|
float /*overscan*/,
|
||||||
int channels,
|
int channels,
|
||||||
float* destination_image) {
|
float* destination_image) {
|
||||||
memcpy(destination_image, source_image,
|
memcpy(destination_image,
|
||||||
|
source_image,
|
||||||
channels * width * height * sizeof(float));
|
channels * width * height * sizeof(float));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -277,7 +278,8 @@ void libmv_cameraIntrinsicsDistortByte(
|
|||||||
float /*overscan*/,
|
float /*overscan*/,
|
||||||
int channels,
|
int channels,
|
||||||
unsigned char* destination_image) {
|
unsigned char* destination_image) {
|
||||||
memcpy(destination_image, source_image,
|
memcpy(destination_image,
|
||||||
|
source_image,
|
||||||
channels * width * height * sizeof(unsigned char));
|
channels * width * height * sizeof(unsigned char));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -289,7 +291,8 @@ void libmv_cameraIntrinsicsDistortFloat(
|
|||||||
float /*overscan*/,
|
float /*overscan*/,
|
||||||
int channels,
|
int channels,
|
||||||
float* destination_image) {
|
float* destination_image) {
|
||||||
memcpy(destination_image, source_image,
|
memcpy(destination_image,
|
||||||
|
source_image,
|
||||||
channels * width * height * sizeof(float));
|
channels * width * height * sizeof(float));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -327,45 +330,38 @@ void libmv_homography2DFromCorrespondencesEuc(/* const */ double (* /*x1*/)[2],
|
|||||||
|
|
||||||
/* ************ autotrack ************ */
|
/* ************ autotrack ************ */
|
||||||
|
|
||||||
libmv_AutoTrack* libmv_autoTrackNew(libmv_FrameAccessor* /*frame_accessor*/)
|
libmv_AutoTrack* libmv_autoTrackNew(libmv_FrameAccessor* /*frame_accessor*/) {
|
||||||
{
|
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
void libmv_autoTrackDestroy(libmv_AutoTrack* /*libmv_autotrack*/)
|
void libmv_autoTrackDestroy(libmv_AutoTrack* /*libmv_autotrack*/) {
|
||||||
{
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void libmv_autoTrackSetOptions(libmv_AutoTrack* /*libmv_autotrack*/,
|
void libmv_autoTrackSetOptions(libmv_AutoTrack* /*libmv_autotrack*/,
|
||||||
const libmv_AutoTrackOptions* /*options*/)
|
const libmv_AutoTrackOptions* /*options*/) {
|
||||||
{
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int libmv_autoTrackMarker(libmv_AutoTrack* /*libmv_autotrack*/,
|
int libmv_autoTrackMarker(libmv_AutoTrack* /*libmv_autotrack*/,
|
||||||
const libmv_TrackRegionOptions* /*libmv_options*/,
|
const libmv_TrackRegionOptions* /*libmv_options*/,
|
||||||
libmv_Marker* /*libmv_tracker_marker*/,
|
libmv_Marker* /*libmv_tracker_marker*/,
|
||||||
libmv_TrackRegionResult* /*libmv_result*/)
|
libmv_TrackRegionResult* /*libmv_result*/) {
|
||||||
{
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void libmv_autoTrackAddMarker(libmv_AutoTrack* /*libmv_autotrack*/,
|
void libmv_autoTrackAddMarker(libmv_AutoTrack* /*libmv_autotrack*/,
|
||||||
const libmv_Marker* /*libmv_marker*/)
|
const libmv_Marker* /*libmv_marker*/) {
|
||||||
{
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void libmv_autoTrackSetMarkers(libmv_AutoTrack* /*libmv_autotrack*/,
|
void libmv_autoTrackSetMarkers(libmv_AutoTrack* /*libmv_autotrack*/,
|
||||||
const libmv_Marker* /*libmv_marker-*/,
|
const libmv_Marker* /*libmv_marker-*/,
|
||||||
size_t /*num_markers*/)
|
size_t /*num_markers*/) {
|
||||||
{
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int libmv_autoTrackGetMarker(libmv_AutoTrack* /*libmv_autotrack*/,
|
int libmv_autoTrackGetMarker(libmv_AutoTrack* /*libmv_autotrack*/,
|
||||||
int /*clip*/,
|
int /*clip*/,
|
||||||
int /*frame*/,
|
int /*frame*/,
|
||||||
int /*track*/,
|
int /*track*/,
|
||||||
libmv_Marker* /*libmv_marker*/)
|
libmv_Marker* /*libmv_marker*/) {
|
||||||
{
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -376,24 +372,20 @@ libmv_FrameAccessor* libmv_FrameAccessorNew(
|
|||||||
libmv_GetImageCallback /*get_image_callback*/,
|
libmv_GetImageCallback /*get_image_callback*/,
|
||||||
libmv_ReleaseImageCallback /*release_image_callback*/,
|
libmv_ReleaseImageCallback /*release_image_callback*/,
|
||||||
libmv_GetMaskForTrackCallback /*get_mask_for_track_callback*/,
|
libmv_GetMaskForTrackCallback /*get_mask_for_track_callback*/,
|
||||||
libmv_ReleaseMaskCallback /*release_mask_callback*/)
|
libmv_ReleaseMaskCallback /*release_mask_callback*/) {
|
||||||
{
|
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
void libmv_FrameAccessorDestroy(libmv_FrameAccessor* /*frame_accessor*/)
|
void libmv_FrameAccessorDestroy(libmv_FrameAccessor* /*frame_accessor*/) {
|
||||||
{
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int64_t libmv_frameAccessorgetTransformKey(
|
int64_t libmv_frameAccessorgetTransformKey(
|
||||||
const libmv_FrameTransform * /*transform*/)
|
const libmv_FrameTransform* /*transform*/) {
|
||||||
{
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void libmv_frameAccessorgetTransformRun(const libmv_FrameTransform* /*transform*/,
|
void libmv_frameAccessorgetTransformRun(
|
||||||
|
const libmv_FrameTransform* /*transform*/,
|
||||||
const libmv_FloatImage* /*input_image*/,
|
const libmv_FloatImage* /*input_image*/,
|
||||||
libmv_FloatImage* /*output_image*/)
|
libmv_FloatImage* /*output_image*/) {
|
||||||
{
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -32,9 +32,9 @@
|
|||||||
#undef DUMP_ALWAYS
|
#undef DUMP_ALWAYS
|
||||||
|
|
||||||
using libmv::FloatImage;
|
using libmv::FloatImage;
|
||||||
|
using libmv::TrackRegion;
|
||||||
using libmv::TrackRegionOptions;
|
using libmv::TrackRegionOptions;
|
||||||
using libmv::TrackRegionResult;
|
using libmv::TrackRegionResult;
|
||||||
using libmv::TrackRegion;
|
|
||||||
|
|
||||||
void libmv_configureTrackRegionOptions(
|
void libmv_configureTrackRegionOptions(
|
||||||
const libmv_TrackRegionOptions& options,
|
const libmv_TrackRegionOptions& options,
|
||||||
@@ -66,7 +66,8 @@ void libmv_configureTrackRegionOptions(
|
|||||||
* so disabling for now for until proper prediction model is landed.
|
* so disabling for now for until proper prediction model is landed.
|
||||||
*
|
*
|
||||||
* The thing is, currently blender sends input coordinates as the guess to
|
* The thing is, currently blender sends input coordinates as the guess to
|
||||||
* region tracker and in case of fast motion such an early out ruins the track.
|
* region tracker and in case of fast motion such an early out ruins the
|
||||||
|
* track.
|
||||||
*/
|
*/
|
||||||
track_region_options->attempt_refine_before_brute = false;
|
track_region_options->attempt_refine_before_brute = false;
|
||||||
track_region_options->use_normalized_intensities = options.use_normalization;
|
track_region_options->use_normalized_intensities = options.use_normalization;
|
||||||
@@ -108,33 +109,27 @@ int libmv_trackRegion(const libmv_TrackRegionOptions* options,
|
|||||||
|
|
||||||
libmv_configureTrackRegionOptions(*options, &track_region_options);
|
libmv_configureTrackRegionOptions(*options, &track_region_options);
|
||||||
if (options->image1_mask) {
|
if (options->image1_mask) {
|
||||||
libmv_floatBufferToFloatImage(options->image1_mask,
|
libmv_floatBufferToFloatImage(
|
||||||
image1_width,
|
options->image1_mask, image1_width, image1_height, 1, &image1_mask);
|
||||||
image1_height,
|
|
||||||
1,
|
|
||||||
&image1_mask);
|
|
||||||
|
|
||||||
track_region_options.image1_mask = &image1_mask;
|
track_region_options.image1_mask = &image1_mask;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Convert from raw float buffers to libmv's FloatImage.
|
// Convert from raw float buffers to libmv's FloatImage.
|
||||||
FloatImage old_patch, new_patch;
|
FloatImage old_patch, new_patch;
|
||||||
libmv_floatBufferToFloatImage(image1,
|
libmv_floatBufferToFloatImage(
|
||||||
image1_width,
|
image1, image1_width, image1_height, 1, &old_patch);
|
||||||
image1_height,
|
libmv_floatBufferToFloatImage(
|
||||||
1,
|
image2, image2_width, image2_height, 1, &new_patch);
|
||||||
&old_patch);
|
|
||||||
libmv_floatBufferToFloatImage(image2,
|
|
||||||
image2_width,
|
|
||||||
image2_height,
|
|
||||||
1,
|
|
||||||
&new_patch);
|
|
||||||
|
|
||||||
TrackRegionResult track_region_result;
|
TrackRegionResult track_region_result;
|
||||||
TrackRegion(old_patch, new_patch,
|
TrackRegion(old_patch,
|
||||||
xx1, yy1,
|
new_patch,
|
||||||
|
xx1,
|
||||||
|
yy1,
|
||||||
track_region_options,
|
track_region_options,
|
||||||
xx2, yy2,
|
xx2,
|
||||||
|
yy2,
|
||||||
&track_region_result);
|
&track_region_result);
|
||||||
|
|
||||||
// Convert to floats for the blender api.
|
// Convert to floats for the blender api.
|
||||||
|
@@ -44,7 +44,7 @@ typedef struct libmv_TrackRegionResult {
|
|||||||
namespace libmv {
|
namespace libmv {
|
||||||
struct TrackRegionOptions;
|
struct TrackRegionOptions;
|
||||||
struct TrackRegionResult;
|
struct TrackRegionResult;
|
||||||
}
|
} // namespace libmv
|
||||||
void libmv_configureTrackRegionOptions(
|
void libmv_configureTrackRegionOptions(
|
||||||
const libmv_TrackRegionOptions& options,
|
const libmv_TrackRegionOptions& options,
|
||||||
libmv::TrackRegionOptions* track_region_options);
|
libmv::TrackRegionOptions* track_region_options);
|
||||||
|
@@ -25,8 +25,7 @@
|
|||||||
using mv::Marker;
|
using mv::Marker;
|
||||||
using mv::Tracks;
|
using mv::Tracks;
|
||||||
|
|
||||||
void libmv_apiMarkerToMarker(const libmv_Marker& libmv_marker,
|
void libmv_apiMarkerToMarker(const libmv_Marker& libmv_marker, Marker* marker) {
|
||||||
Marker *marker) {
|
|
||||||
marker->clip = libmv_marker.clip;
|
marker->clip = libmv_marker.clip;
|
||||||
marker->frame = libmv_marker.frame;
|
marker->frame = libmv_marker.frame;
|
||||||
marker->track = libmv_marker.track;
|
marker->track = libmv_marker.track;
|
||||||
@@ -50,8 +49,7 @@ void libmv_apiMarkerToMarker(const libmv_Marker& libmv_marker,
|
|||||||
marker->disabled_channels = libmv_marker.disabled_channels;
|
marker->disabled_channels = libmv_marker.disabled_channels;
|
||||||
}
|
}
|
||||||
|
|
||||||
void libmv_markerToApiMarker(const Marker& marker,
|
void libmv_markerToApiMarker(const Marker& marker, libmv_Marker* libmv_marker) {
|
||||||
libmv_Marker *libmv_marker) {
|
|
||||||
libmv_marker->clip = marker.clip;
|
libmv_marker->clip = marker.clip;
|
||||||
libmv_marker->frame = marker.frame;
|
libmv_marker->frame = marker.frame;
|
||||||
libmv_marker->track = marker.track;
|
libmv_marker->track = marker.track;
|
||||||
@@ -109,8 +107,7 @@ void libmv_tracksRemoveMarkerN(libmv_TracksN* libmv_tracks,
|
|||||||
((Tracks*)libmv_tracks)->RemoveMarker(clip, frame, track);
|
((Tracks*)libmv_tracks)->RemoveMarker(clip, frame, track);
|
||||||
}
|
}
|
||||||
|
|
||||||
void libmv_tracksRemoveMarkersForTrack(libmv_TracksN* libmv_tracks,
|
void libmv_tracksRemoveMarkersForTrack(libmv_TracksN* libmv_tracks, int track) {
|
||||||
int track) {
|
|
||||||
((Tracks*)libmv_tracks)->RemoveMarkersForTrack(track);
|
((Tracks*)libmv_tracks)->RemoveMarkersForTrack(track);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -92,7 +92,6 @@ libmv_TracksN* libmv_tracksNewN(void);
|
|||||||
|
|
||||||
void libmv_tracksDestroyN(libmv_TracksN* libmv_tracks);
|
void libmv_tracksDestroyN(libmv_TracksN* libmv_tracks);
|
||||||
|
|
||||||
|
|
||||||
void libmv_tracksAddMarkerN(libmv_TracksN* libmv_tracks,
|
void libmv_tracksAddMarkerN(libmv_TracksN* libmv_tracks,
|
||||||
const libmv_Marker* libmv_marker);
|
const libmv_Marker* libmv_marker);
|
||||||
|
|
||||||
@@ -107,8 +106,7 @@ void libmv_tracksRemoveMarkerN(libmv_TracksN* libmv_tracks,
|
|||||||
int frame,
|
int frame,
|
||||||
int track);
|
int track);
|
||||||
|
|
||||||
void libmv_tracksRemoveMarkersForTrack(libmv_TracksN* libmv_tracks,
|
void libmv_tracksRemoveMarkersForTrack(libmv_TracksN* libmv_tracks, int track);
|
||||||
int track);
|
|
||||||
|
|
||||||
int libmv_tracksMaxClipN(libmv_TracksN* libmv_tracks);
|
int libmv_tracksMaxClipN(libmv_TracksN* libmv_tracks);
|
||||||
int libmv_tracksMaxFrameN(libmv_TracksN* libmv_tracks, int clip);
|
int libmv_tracksMaxFrameN(libmv_TracksN* libmv_tracks, int clip);
|
||||||
|
@@ -48,9 +48,15 @@
|
|||||||
((type*)(what))->~type(); \
|
((type*)(what))->~type(); \
|
||||||
free(what); \
|
free(what); \
|
||||||
} \
|
} \
|
||||||
} (void)0
|
} \
|
||||||
|
(void)0
|
||||||
# define LIBMV_STRUCT_NEW(type, count) (type*)malloc(sizeof(type) * count)
|
# define LIBMV_STRUCT_NEW(type, count) (type*)malloc(sizeof(type) * count)
|
||||||
# define LIBMV_STRUCT_DELETE(what) { if (what) free(what); } (void)0
|
# define LIBMV_STRUCT_DELETE(what) \
|
||||||
|
{ \
|
||||||
|
if (what) \
|
||||||
|
free(what); \
|
||||||
|
} \
|
||||||
|
(void)0
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#endif // LIBMV_C_API_UTILDEFINES_H_
|
#endif // LIBMV_C_API_UTILDEFINES_H_
|
||||||
|
@@ -21,9 +21,9 @@
|
|||||||
// Author: mierle@gmail.com (Keir Mierle)
|
// Author: mierle@gmail.com (Keir Mierle)
|
||||||
|
|
||||||
#include "libmv/autotrack/autotrack.h"
|
#include "libmv/autotrack/autotrack.h"
|
||||||
#include "libmv/autotrack/quad.h"
|
|
||||||
#include "libmv/autotrack/frame_accessor.h"
|
#include "libmv/autotrack/frame_accessor.h"
|
||||||
#include "libmv/autotrack/predict_tracks.h"
|
#include "libmv/autotrack/predict_tracks.h"
|
||||||
|
#include "libmv/autotrack/quad.h"
|
||||||
#include "libmv/base/scoped_ptr.h"
|
#include "libmv/base/scoped_ptr.h"
|
||||||
#include "libmv/logging/logging.h"
|
#include "libmv/logging/logging.h"
|
||||||
#include "libmv/numeric/numeric.h"
|
#include "libmv/numeric/numeric.h"
|
||||||
@@ -37,19 +37,15 @@ class DisableChannelsTransform : public FrameAccessor::Transform {
|
|||||||
DisableChannelsTransform(int disabled_channels)
|
DisableChannelsTransform(int disabled_channels)
|
||||||
: disabled_channels_(disabled_channels) {}
|
: disabled_channels_(disabled_channels) {}
|
||||||
|
|
||||||
int64_t key() const {
|
int64_t key() const { return disabled_channels_; }
|
||||||
return disabled_channels_;
|
|
||||||
}
|
|
||||||
|
|
||||||
void run(const FloatImage& input, FloatImage* output) const {
|
void run(const FloatImage& input, FloatImage* output) const {
|
||||||
bool disable_red = (disabled_channels_ & Marker::CHANNEL_R) != 0,
|
bool disable_red = (disabled_channels_ & Marker::CHANNEL_R) != 0,
|
||||||
disable_green = (disabled_channels_ & Marker::CHANNEL_G) != 0,
|
disable_green = (disabled_channels_ & Marker::CHANNEL_G) != 0,
|
||||||
disable_blue = (disabled_channels_ & Marker::CHANNEL_B) != 0;
|
disable_blue = (disabled_channels_ & Marker::CHANNEL_B) != 0;
|
||||||
|
|
||||||
LG << "Disabling channels: "
|
LG << "Disabling channels: " << (disable_red ? "R " : "")
|
||||||
<< (disable_red ? "R " : "")
|
<< (disable_green ? "G " : "") << (disable_blue ? "B" : "");
|
||||||
<< (disable_green ? "G " : "")
|
|
||||||
<< (disable_blue ? "B" : "");
|
|
||||||
|
|
||||||
// It's important to rescale the resultappropriately so that e.g. if only
|
// It's important to rescale the resultappropriately so that e.g. if only
|
||||||
// blue is selected, it's not zeroed out.
|
// blue is selected, it's not zeroed out.
|
||||||
@@ -115,11 +111,8 @@ FrameAccessor::Key GetMaskForMarker(const Marker& marker,
|
|||||||
FrameAccessor* frame_accessor,
|
FrameAccessor* frame_accessor,
|
||||||
FloatImage* mask) {
|
FloatImage* mask) {
|
||||||
Region region = marker.search_region.Rounded();
|
Region region = marker.search_region.Rounded();
|
||||||
return frame_accessor->GetMaskForTrack(marker.clip,
|
return frame_accessor->GetMaskForTrack(
|
||||||
marker.frame,
|
marker.clip, marker.frame, marker.track, ®ion, mask);
|
||||||
marker.track,
|
|
||||||
®ion,
|
|
||||||
mask);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
@@ -152,23 +145,20 @@ bool AutoTrack::TrackMarker(Marker* tracked_marker,
|
|||||||
// TODO(keir): Technically this could take a smaller slice from the source
|
// TODO(keir): Technically this could take a smaller slice from the source
|
||||||
// image instead of taking one the size of the search window.
|
// image instead of taking one the size of the search window.
|
||||||
FloatImage reference_image;
|
FloatImage reference_image;
|
||||||
FrameAccessor::Key reference_key = GetImageForMarker(reference_marker,
|
FrameAccessor::Key reference_key =
|
||||||
frame_accessor_,
|
GetImageForMarker(reference_marker, frame_accessor_, &reference_image);
|
||||||
&reference_image);
|
|
||||||
if (!reference_key) {
|
if (!reference_key) {
|
||||||
LG << "Couldn't get frame for reference marker: " << reference_marker;
|
LG << "Couldn't get frame for reference marker: " << reference_marker;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
FloatImage reference_mask;
|
FloatImage reference_mask;
|
||||||
FrameAccessor::Key reference_mask_key = GetMaskForMarker(reference_marker,
|
FrameAccessor::Key reference_mask_key =
|
||||||
frame_accessor_,
|
GetMaskForMarker(reference_marker, frame_accessor_, &reference_mask);
|
||||||
&reference_mask);
|
|
||||||
|
|
||||||
FloatImage tracked_image;
|
FloatImage tracked_image;
|
||||||
FrameAccessor::Key tracked_key = GetImageForMarker(*tracked_marker,
|
FrameAccessor::Key tracked_key =
|
||||||
frame_accessor_,
|
GetImageForMarker(*tracked_marker, frame_accessor_, &tracked_image);
|
||||||
&tracked_image);
|
|
||||||
if (!tracked_key) {
|
if (!tracked_key) {
|
||||||
frame_accessor_->ReleaseImage(reference_key);
|
frame_accessor_->ReleaseImage(reference_key);
|
||||||
LG << "Couldn't get frame for tracked marker: " << tracked_marker;
|
LG << "Couldn't get frame for tracked marker: " << tracked_marker;
|
||||||
@@ -191,9 +181,11 @@ bool AutoTrack::TrackMarker(Marker* tracked_marker,
|
|||||||
local_track_region_options.attempt_refine_before_brute = predicted_position;
|
local_track_region_options.attempt_refine_before_brute = predicted_position;
|
||||||
TrackRegion(reference_image,
|
TrackRegion(reference_image,
|
||||||
tracked_image,
|
tracked_image,
|
||||||
x1, y1,
|
x1,
|
||||||
|
y1,
|
||||||
local_track_region_options,
|
local_track_region_options,
|
||||||
x2, y2,
|
x2,
|
||||||
|
y2,
|
||||||
result);
|
result);
|
||||||
|
|
||||||
// Copy results over the tracked marker.
|
// Copy results over the tracked marker.
|
||||||
@@ -230,7 +222,9 @@ void AutoTrack::SetMarkers(vector<Marker>* markers) {
|
|||||||
tracks_.SetMarkers(markers);
|
tracks_.SetMarkers(markers);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool AutoTrack::GetMarker(int clip, int frame, int track,
|
bool AutoTrack::GetMarker(int clip,
|
||||||
|
int frame,
|
||||||
|
int track,
|
||||||
Marker* markers) const {
|
Marker* markers) const {
|
||||||
return tracks_.GetMarker(clip, frame, track, markers);
|
return tracks_.GetMarker(clip, frame, track, markers);
|
||||||
}
|
}
|
||||||
@@ -242,7 +236,8 @@ void AutoTrack::DetectAndTrack(const DetectAndTrackOptions& options) {
|
|||||||
vector<Marker> previous_frame_markers;
|
vector<Marker> previous_frame_markers;
|
||||||
// Q: How to decide track #s when detecting?
|
// Q: How to decide track #s when detecting?
|
||||||
// Q: How to match markers from previous frame? set of prev frame tracks?
|
// Q: How to match markers from previous frame? set of prev frame tracks?
|
||||||
// Q: How to decide what markers should get tracked and which ones should not?
|
// Q: How to decide what markers should get tracked and which ones should
|
||||||
|
// not?
|
||||||
for (int frame = 0; frame < num_frames; ++frame) {
|
for (int frame = 0; frame < num_frames; ++frame) {
|
||||||
if (Cancelled()) {
|
if (Cancelled()) {
|
||||||
LG << "Got cancel message while detecting and tracking...";
|
LG << "Got cancel message while detecting and tracking...";
|
||||||
@@ -271,8 +266,7 @@ void AutoTrack::DetectAndTrack(const DetectAndTrackOptions& options) {
|
|||||||
for (int i = 0; i < this_frame_markers.size(); ++i) {
|
for (int i = 0; i < this_frame_markers.size(); ++i) {
|
||||||
tracks_in_this_frame.push_back(this_frame_markers[i].track);
|
tracks_in_this_frame.push_back(this_frame_markers[i].track);
|
||||||
}
|
}
|
||||||
std::sort(tracks_in_this_frame.begin(),
|
std::sort(tracks_in_this_frame.begin(), tracks_in_this_frame.end());
|
||||||
tracks_in_this_frame.end());
|
|
||||||
|
|
||||||
// Find tracks in the previous frame that are not in this one.
|
// Find tracks in the previous frame that are not in this one.
|
||||||
vector<Marker*> previous_frame_markers_to_track;
|
vector<Marker*> previous_frame_markers_to_track;
|
||||||
|
@@ -23,8 +23,8 @@
|
|||||||
#ifndef LIBMV_AUTOTRACK_AUTOTRACK_H_
|
#ifndef LIBMV_AUTOTRACK_AUTOTRACK_H_
|
||||||
#define LIBMV_AUTOTRACK_AUTOTRACK_H_
|
#define LIBMV_AUTOTRACK_AUTOTRACK_H_
|
||||||
|
|
||||||
#include "libmv/autotrack/tracks.h"
|
|
||||||
#include "libmv/autotrack/region.h"
|
#include "libmv/autotrack/region.h"
|
||||||
|
#include "libmv/autotrack/tracks.h"
|
||||||
#include "libmv/tracking/track_region.h"
|
#include "libmv/tracking/track_region.h"
|
||||||
|
|
||||||
namespace libmv {
|
namespace libmv {
|
||||||
@@ -74,8 +74,7 @@ class AutoTrack {
|
|||||||
Region search_region;
|
Region search_region;
|
||||||
};
|
};
|
||||||
|
|
||||||
AutoTrack(FrameAccessor* frame_accessor)
|
AutoTrack(FrameAccessor* frame_accessor) : frame_accessor_(frame_accessor) {}
|
||||||
: frame_accessor_(frame_accessor) {}
|
|
||||||
|
|
||||||
// Marker manipulation.
|
// Marker manipulation.
|
||||||
// Clip manipulation.
|
// Clip manipulation.
|
||||||
@@ -150,10 +149,9 @@ class AutoTrack {
|
|||||||
};
|
};
|
||||||
void DetectAndTrack(const DetectAndTrackOptions& options);
|
void DetectAndTrack(const DetectAndTrackOptions& options);
|
||||||
|
|
||||||
struct DetectFeaturesInFrameOptions {
|
struct DetectFeaturesInFrameOptions {};
|
||||||
};
|
void DetectFeaturesInFrame(
|
||||||
void DetectFeaturesInFrame(int clip, int frame,
|
int clip, int frame, const DetectFeaturesInFrameOptions* options = NULL) {
|
||||||
const DetectFeaturesInFrameOptions* options=NULL) {
|
|
||||||
(void)clip;
|
(void)clip;
|
||||||
(void)frame;
|
(void)frame;
|
||||||
(void)options;
|
(void)options;
|
||||||
|
@@ -50,10 +50,7 @@ struct FrameAccessor {
|
|||||||
virtual void run(const FloatImage& input, FloatImage* output) const = 0;
|
virtual void run(const FloatImage& input, FloatImage* output) const = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
enum InputMode {
|
enum InputMode { MONO, RGBA };
|
||||||
MONO,
|
|
||||||
RGBA
|
|
||||||
};
|
|
||||||
|
|
||||||
typedef void* Key;
|
typedef void* Key;
|
||||||
|
|
||||||
@@ -100,6 +97,6 @@ struct FrameAccessor {
|
|||||||
virtual int NumFrames(int clip) = 0;
|
virtual int NumFrames(int clip) = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace libmv
|
} // namespace mv
|
||||||
|
|
||||||
#endif // LIBMV_AUTOTRACK_FRAME_ACCESSOR_H_
|
#endif // LIBMV_AUTOTRACK_FRAME_ACCESSOR_H_
|
||||||
|
@@ -69,11 +69,7 @@ struct Marker {
|
|||||||
|
|
||||||
// Markers may be inliers or outliers if the tracking fails; this allows
|
// Markers may be inliers or outliers if the tracking fails; this allows
|
||||||
// visualizing the markers in the image.
|
// visualizing the markers in the image.
|
||||||
enum Status {
|
enum Status { UNKNOWN, INLIER, OUTLIER };
|
||||||
UNKNOWN,
|
|
||||||
INLIER,
|
|
||||||
OUTLIER
|
|
||||||
};
|
|
||||||
Status status;
|
Status status;
|
||||||
|
|
||||||
// When doing correlation tracking, where to search in the current frame for
|
// When doing correlation tracking, where to search in the current frame for
|
||||||
@@ -90,12 +86,7 @@ struct Marker {
|
|||||||
// another primitive (a rectangular prisim). This captures the information
|
// another primitive (a rectangular prisim). This captures the information
|
||||||
// needed to say that for example a collection of markers belongs to model #2
|
// needed to say that for example a collection of markers belongs to model #2
|
||||||
// (and model #2 is a plane).
|
// (and model #2 is a plane).
|
||||||
enum ModelType {
|
enum ModelType { POINT, PLANE, LINE, CUBE };
|
||||||
POINT,
|
|
||||||
PLANE,
|
|
||||||
LINE,
|
|
||||||
CUBE
|
|
||||||
};
|
|
||||||
ModelType model_type;
|
ModelType model_type;
|
||||||
|
|
||||||
// The model ID this track (e.g. the second model, which is a plane).
|
// The model ID this track (e.g. the second model, which is a plane).
|
||||||
@@ -129,12 +120,8 @@ struct Marker {
|
|||||||
};
|
};
|
||||||
|
|
||||||
inline std::ostream& operator<<(std::ostream& out, const Marker& marker) {
|
inline std::ostream& operator<<(std::ostream& out, const Marker& marker) {
|
||||||
out << "{"
|
out << "{" << marker.clip << ", " << marker.frame << ", " << marker.track
|
||||||
<< marker.clip << ", "
|
<< ", (" << marker.center.x() << ", " << marker.center.y() << ")"
|
||||||
<< marker.frame << ", "
|
|
||||||
<< marker.track << ", ("
|
|
||||||
<< marker.center.x() << ", "
|
|
||||||
<< marker.center.y() << ")"
|
|
||||||
<< "}";
|
<< "}";
|
||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
@@ -23,18 +23,13 @@
|
|||||||
#ifndef LIBMV_AUTOTRACK_MODEL_H_
|
#ifndef LIBMV_AUTOTRACK_MODEL_H_
|
||||||
#define LIBMV_AUTOTRACK_MODEL_H_
|
#define LIBMV_AUTOTRACK_MODEL_H_
|
||||||
|
|
||||||
#include "libmv/numeric/numeric.h"
|
|
||||||
#include "libmv/autotrack/quad.h"
|
#include "libmv/autotrack/quad.h"
|
||||||
|
#include "libmv/numeric/numeric.h"
|
||||||
|
|
||||||
namespace mv {
|
namespace mv {
|
||||||
|
|
||||||
struct Model {
|
struct Model {
|
||||||
enum ModelType {
|
enum ModelType { POINT, PLANE, LINE, CUBE };
|
||||||
POINT,
|
|
||||||
PLANE,
|
|
||||||
LINE,
|
|
||||||
CUBE
|
|
||||||
};
|
|
||||||
|
|
||||||
// ???
|
// ???
|
||||||
};
|
};
|
||||||
|
@@ -20,8 +20,8 @@
|
|||||||
//
|
//
|
||||||
// Author: mierle@gmail.com (Keir Mierle)
|
// Author: mierle@gmail.com (Keir Mierle)
|
||||||
|
|
||||||
#include "libmv/autotrack/marker.h"
|
|
||||||
#include "libmv/autotrack/predict_tracks.h"
|
#include "libmv/autotrack/predict_tracks.h"
|
||||||
|
#include "libmv/autotrack/marker.h"
|
||||||
#include "libmv/autotrack/tracks.h"
|
#include "libmv/autotrack/tracks.h"
|
||||||
#include "libmv/base/vector.h"
|
#include "libmv/base/vector.h"
|
||||||
#include "libmv/logging/logging.h"
|
#include "libmv/logging/logging.h"
|
||||||
@@ -31,8 +31,8 @@ namespace mv {
|
|||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
using libmv::vector;
|
|
||||||
using libmv::Vec2;
|
using libmv::Vec2;
|
||||||
|
using libmv::vector;
|
||||||
|
|
||||||
// Implied time delta between steps. Set empirically by tweaking and seeing
|
// Implied time delta between steps. Set empirically by tweaking and seeing
|
||||||
// what numbers did best at prediction.
|
// what numbers did best at prediction.
|
||||||
@@ -57,6 +57,8 @@ const double dt = 3.8;
|
|||||||
|
|
||||||
// For a typical system having constant velocity. This gives smooth-appearing
|
// For a typical system having constant velocity. This gives smooth-appearing
|
||||||
// predictions, but they are not always as accurate.
|
// predictions, but they are not always as accurate.
|
||||||
|
//
|
||||||
|
// clang-format off
|
||||||
const double velocity_state_transition_data[] = {
|
const double velocity_state_transition_data[] = {
|
||||||
1, dt, 0, 0, 0, 0,
|
1, dt, 0, 0, 0, 0,
|
||||||
0, 1, 0, 0, 0, 0,
|
0, 1, 0, 0, 0, 0,
|
||||||
@@ -65,10 +67,13 @@ const double velocity_state_transition_data[] = {
|
|||||||
0, 0, 0, 0, 1, 0,
|
0, 0, 0, 0, 1, 0,
|
||||||
0, 0, 0, 0, 0, 1
|
0, 0, 0, 0, 0, 1
|
||||||
};
|
};
|
||||||
|
// clang-format on
|
||||||
|
|
||||||
#if 0
|
#if 0
|
||||||
// This 3rd-order system also models acceleration. This makes for "jerky"
|
// This 3rd-order system also models acceleration. This makes for "jerky"
|
||||||
// predictions, but that tend to be more accurate.
|
// predictions, but that tend to be more accurate.
|
||||||
|
//
|
||||||
|
// clang-format off
|
||||||
const double acceleration_state_transition_data[] = {
|
const double acceleration_state_transition_data[] = {
|
||||||
1, dt, dt*dt/2, 0, 0, 0,
|
1, dt, dt*dt/2, 0, 0, 0,
|
||||||
0, 1, dt, 0, 0, 0,
|
0, 1, dt, 0, 0, 0,
|
||||||
@@ -77,9 +82,12 @@ const double acceleration_state_transition_data[] = {
|
|||||||
0, 0, 0, 0, 1, dt,
|
0, 0, 0, 0, 1, dt,
|
||||||
0, 0, 0, 0, 0, 1
|
0, 0, 0, 0, 0, 1
|
||||||
};
|
};
|
||||||
|
// clang-format on
|
||||||
|
|
||||||
// This system (attempts) to add an angular velocity component. However, it's
|
// This system (attempts) to add an angular velocity component. However, it's
|
||||||
// total junk.
|
// total junk.
|
||||||
|
//
|
||||||
|
// clang-format off
|
||||||
const double angular_state_transition_data[] = {
|
const double angular_state_transition_data[] = {
|
||||||
1, dt, -dt, 0, 0, 0, // Position x
|
1, dt, -dt, 0, 0, 0, // Position x
|
||||||
0, 1, 0, 0, 0, 0, // Velocity x
|
0, 1, 0, 0, 0, 0, // Velocity x
|
||||||
@@ -88,17 +96,22 @@ const double angular_state_transition_data[] = {
|
|||||||
0, 0, 0, 0, 1, 0, // Velocity y
|
0, 0, 0, 0, 1, 0, // Velocity y
|
||||||
0, 0, 0, 0, 0, 1 // Ignored
|
0, 0, 0, 0, 0, 1 // Ignored
|
||||||
};
|
};
|
||||||
|
// clang-format on
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
const double* state_transition_data = velocity_state_transition_data;
|
const double* state_transition_data = velocity_state_transition_data;
|
||||||
|
|
||||||
// Observation matrix.
|
// Observation matrix.
|
||||||
|
// clang-format off
|
||||||
const double observation_data[] = {
|
const double observation_data[] = {
|
||||||
1., 0., 0., 0., 0., 0.,
|
1., 0., 0., 0., 0., 0.,
|
||||||
0., 0., 0., 1., 0., 0.
|
0., 0., 0., 1., 0., 0.
|
||||||
};
|
};
|
||||||
|
// clang-format on
|
||||||
|
|
||||||
// Process covariance.
|
// Process covariance.
|
||||||
|
//
|
||||||
|
// clang-format off
|
||||||
const double process_covariance_data[] = {
|
const double process_covariance_data[] = {
|
||||||
35, 0, 0, 0, 0, 0,
|
35, 0, 0, 0, 0, 0,
|
||||||
0, 5, 0, 0, 0, 0,
|
0, 5, 0, 0, 0, 0,
|
||||||
@@ -107,14 +120,19 @@ const double process_covariance_data[] = {
|
|||||||
0, 0, 0, 0, 5, 0,
|
0, 0, 0, 0, 5, 0,
|
||||||
0, 0, 0, 0, 0, 5
|
0, 0, 0, 0, 0, 5
|
||||||
};
|
};
|
||||||
|
// clang-format on
|
||||||
|
|
||||||
// Process covariance.
|
// Process covariance.
|
||||||
const double measurement_covariance_data[] = {
|
const double measurement_covariance_data[] = {
|
||||||
0.01, 0.00,
|
0.01,
|
||||||
0.00, 0.01,
|
0.00,
|
||||||
|
0.00,
|
||||||
|
0.01,
|
||||||
};
|
};
|
||||||
|
|
||||||
// Initial covariance.
|
// Initial covariance.
|
||||||
|
//
|
||||||
|
// clang-format off
|
||||||
const double initial_covariance_data[] = {
|
const double initial_covariance_data[] = {
|
||||||
10, 0, 0, 0, 0, 0,
|
10, 0, 0, 0, 0, 0,
|
||||||
0, 1, 0, 0, 0, 0,
|
0, 1, 0, 0, 0, 0,
|
||||||
@@ -123,6 +141,7 @@ const double initial_covariance_data[] = {
|
|||||||
0, 0, 0, 0, 1, 0,
|
0, 0, 0, 0, 1, 0,
|
||||||
0, 0, 0, 0, 0, 1
|
0, 0, 0, 0, 0, 1
|
||||||
};
|
};
|
||||||
|
// clang-format on
|
||||||
|
|
||||||
typedef mv::KalmanFilter<double, 6, 2> TrackerKalman;
|
typedef mv::KalmanFilter<double, 6, 2> TrackerKalman;
|
||||||
|
|
||||||
@@ -147,8 +166,8 @@ void RunPrediction(const vector<Marker*> previous_markers,
|
|||||||
TrackerKalman::State state;
|
TrackerKalman::State state;
|
||||||
state.mean << previous_markers[0]->center.x(), 0, 0,
|
state.mean << previous_markers[0]->center.x(), 0, 0,
|
||||||
previous_markers[0]->center.y(), 0, 0;
|
previous_markers[0]->center.y(), 0, 0;
|
||||||
state.covariance = Eigen::Matrix<double, 6, 6, Eigen::RowMajor>(
|
state.covariance =
|
||||||
initial_covariance_data);
|
Eigen::Matrix<double, 6, 6, Eigen::RowMajor>(initial_covariance_data);
|
||||||
|
|
||||||
int current_frame = previous_markers[0]->frame;
|
int current_frame = previous_markers[0]->frame;
|
||||||
int target_frame = predicted_marker->frame;
|
int target_frame = predicted_marker->frame;
|
||||||
@@ -159,19 +178,18 @@ void RunPrediction(const vector<Marker*> previous_markers,
|
|||||||
for (int i = 1; i < previous_markers.size(); ++i) {
|
for (int i = 1; i < previous_markers.size(); ++i) {
|
||||||
// Step forward predicting the state until it is on the current marker.
|
// Step forward predicting the state until it is on the current marker.
|
||||||
int predictions = 0;
|
int predictions = 0;
|
||||||
for (;
|
for (; current_frame != previous_markers[i]->frame;
|
||||||
current_frame != previous_markers[i]->frame;
|
|
||||||
current_frame += frame_delta) {
|
current_frame += frame_delta) {
|
||||||
filter.Step(&state);
|
filter.Step(&state);
|
||||||
predictions++;
|
predictions++;
|
||||||
LG << "Predicted point (frame " << current_frame << "): "
|
LG << "Predicted point (frame " << current_frame << "): " << state.mean(0)
|
||||||
<< state.mean(0) << ", " << state.mean(3);
|
<< ", " << state.mean(3);
|
||||||
}
|
}
|
||||||
// Log the error -- not actually used, but interesting.
|
// Log the error -- not actually used, but interesting.
|
||||||
Vec2 error = previous_markers[i]->center.cast<double>() -
|
Vec2 error = previous_markers[i]->center.cast<double>() -
|
||||||
Vec2(state.mean(0), state.mean(3));
|
Vec2(state.mean(0), state.mean(3));
|
||||||
LG << "Prediction error for " << predictions << " steps: ("
|
LG << "Prediction error for " << predictions << " steps: (" << error.x()
|
||||||
<< error.x() << ", " << error.y() << "); norm: " << error.norm();
|
<< ", " << error.y() << "); norm: " << error.norm();
|
||||||
// Now that the state is predicted in the current frame, update the state
|
// Now that the state is predicted in the current frame, update the state
|
||||||
// based on the measurement from the current frame.
|
// based on the measurement from the current frame.
|
||||||
filter.Update(previous_markers[i]->center.cast<double>(),
|
filter.Update(previous_markers[i]->center.cast<double>(),
|
||||||
@@ -184,8 +202,8 @@ void RunPrediction(const vector<Marker*> previous_markers,
|
|||||||
// predict until the target frame.
|
// predict until the target frame.
|
||||||
for (; current_frame != target_frame; current_frame += frame_delta) {
|
for (; current_frame != target_frame; current_frame += frame_delta) {
|
||||||
filter.Step(&state);
|
filter.Step(&state);
|
||||||
LG << "Final predicted point (frame " << current_frame << "): "
|
LG << "Final predicted point (frame " << current_frame
|
||||||
<< state.mean(0) << ", " << state.mean(3);
|
<< "): " << state.mean(0) << ", " << state.mean(3);
|
||||||
}
|
}
|
||||||
|
|
||||||
// The x and y positions are at 0 and 3; ignore acceleration and velocity.
|
// The x and y positions are at 0 and 3; ignore acceleration and velocity.
|
||||||
@@ -253,13 +271,13 @@ bool PredictMarkerPosition(const Tracks& tracks, Marker* marker) {
|
|||||||
} else if (insert_at != -1) {
|
} else if (insert_at != -1) {
|
||||||
// Found existing marker; scan before and after it.
|
// Found existing marker; scan before and after it.
|
||||||
forward_scan_begin = insert_at + 1;
|
forward_scan_begin = insert_at + 1;
|
||||||
forward_scan_end = markers.size() - 1;;
|
forward_scan_end = markers.size() - 1;
|
||||||
backward_scan_begin = insert_at - 1;
|
backward_scan_begin = insert_at - 1;
|
||||||
backward_scan_end = 0;
|
backward_scan_end = 0;
|
||||||
} else {
|
} else {
|
||||||
// Didn't find existing marker but found an insertion point.
|
// Didn't find existing marker but found an insertion point.
|
||||||
forward_scan_begin = insert_before;
|
forward_scan_begin = insert_before;
|
||||||
forward_scan_end = markers.size() - 1;;
|
forward_scan_end = markers.size() - 1;
|
||||||
backward_scan_begin = insert_before - 1;
|
backward_scan_begin = insert_before - 1;
|
||||||
backward_scan_end = 0;
|
backward_scan_end = 0;
|
||||||
}
|
}
|
||||||
@@ -301,9 +319,8 @@ bool PredictMarkerPosition(const Tracks& tracks, Marker* marker) {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
LG << "Predicting backward";
|
LG << "Predicting backward";
|
||||||
int predict_begin =
|
int predict_begin = std::min(
|
||||||
std::min(forward_scan_begin + max_frames_to_predict_from,
|
forward_scan_begin + max_frames_to_predict_from, forward_scan_end);
|
||||||
forward_scan_end);
|
|
||||||
int predict_end = forward_scan_begin;
|
int predict_end = forward_scan_begin;
|
||||||
vector<Marker*> previous_markers;
|
vector<Marker*> previous_markers;
|
||||||
for (int i = predict_begin; i >= predict_end; --i) {
|
for (int i = predict_begin; i >= predict_end; --i) {
|
||||||
@@ -312,7 +329,6 @@ bool PredictMarkerPosition(const Tracks& tracks, Marker* marker) {
|
|||||||
RunPrediction(previous_markers, marker);
|
RunPrediction(previous_markers, marker);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace mv
|
} // namespace mv
|
||||||
|
@@ -35,10 +35,8 @@ static void AddMarker(int frame, float x, float y, Tracks* tracks) {
|
|||||||
marker.frame = frame;
|
marker.frame = frame;
|
||||||
marker.center.x() = x;
|
marker.center.x() = x;
|
||||||
marker.center.y() = y;
|
marker.center.y() = y;
|
||||||
marker.patch.coordinates << x - 1, y - 1,
|
marker.patch.coordinates << x - 1, y - 1, x + 1, y - 1, x + 1, y + 1, x - 1,
|
||||||
x + 1, y - 1,
|
y + 1;
|
||||||
x + 1, y + 1,
|
|
||||||
x - 1, y + 1;
|
|
||||||
tracks->AddMarker(marker);
|
tracks->AddMarker(marker);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -66,10 +64,8 @@ TEST(PredictMarkerPosition, EasyLinearMotion) {
|
|||||||
// Check the patch coordinates as well.
|
// Check the patch coordinates as well.
|
||||||
double x = 9, y = 40.0;
|
double x = 9, y = 40.0;
|
||||||
Quad2Df expected_patch;
|
Quad2Df expected_patch;
|
||||||
expected_patch.coordinates << x - 1, y - 1,
|
expected_patch.coordinates << x - 1, y - 1, x + 1, y - 1, x + 1, y + 1, x - 1,
|
||||||
x + 1, y - 1,
|
y + 1;
|
||||||
x + 1, y + 1,
|
|
||||||
x - 1, y + 1;
|
|
||||||
|
|
||||||
error = (expected_patch.coordinates - predicted.patch.coordinates).norm();
|
error = (expected_patch.coordinates - predicted.patch.coordinates).norm();
|
||||||
LG << "Patch error: " << error;
|
LG << "Patch error: " << error;
|
||||||
@@ -101,10 +97,8 @@ TEST(PredictMarkerPosition, EasyBackwardLinearMotion) {
|
|||||||
// Check the patch coordinates as well.
|
// Check the patch coordinates as well.
|
||||||
double x = 9.0, y = 40.0;
|
double x = 9.0, y = 40.0;
|
||||||
Quad2Df expected_patch;
|
Quad2Df expected_patch;
|
||||||
expected_patch.coordinates << x - 1, y - 1,
|
expected_patch.coordinates << x - 1, y - 1, x + 1, y - 1, x + 1, y + 1, x - 1,
|
||||||
x + 1, y - 1,
|
y + 1;
|
||||||
x + 1, y + 1,
|
|
||||||
x - 1, y + 1;
|
|
||||||
|
|
||||||
error = (expected_patch.coordinates - predicted.patch.coordinates).norm();
|
error = (expected_patch.coordinates - predicted.patch.coordinates).norm();
|
||||||
LG << "Patch error: " << error;
|
LG << "Patch error: " << error;
|
||||||
|
@@ -23,8 +23,8 @@
|
|||||||
#include "libmv/autotrack/tracks.h"
|
#include "libmv/autotrack/tracks.h"
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <vector>
|
|
||||||
#include <iterator>
|
#include <iterator>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
#include "libmv/numeric/numeric.h"
|
#include "libmv/numeric/numeric.h"
|
||||||
|
|
||||||
@@ -34,12 +34,12 @@ Tracks::Tracks(const Tracks& other) {
|
|||||||
markers_ = other.markers_;
|
markers_ = other.markers_;
|
||||||
}
|
}
|
||||||
|
|
||||||
Tracks::Tracks(const vector<Marker>& markers) : markers_(markers) {}
|
Tracks::Tracks(const vector<Marker>& markers) : markers_(markers) {
|
||||||
|
}
|
||||||
|
|
||||||
bool Tracks::GetMarker(int clip, int frame, int track, Marker* marker) const {
|
bool Tracks::GetMarker(int clip, int frame, int track, Marker* marker) const {
|
||||||
for (int i = 0; i < markers_.size(); ++i) {
|
for (int i = 0; i < markers_.size(); ++i) {
|
||||||
if (markers_[i].clip == clip &&
|
if (markers_[i].clip == clip && markers_[i].frame == frame &&
|
||||||
markers_[i].frame == frame &&
|
|
||||||
markers_[i].track == track) {
|
markers_[i].track == track) {
|
||||||
*marker = markers_[i];
|
*marker = markers_[i];
|
||||||
return true;
|
return true;
|
||||||
@@ -60,8 +60,7 @@ void Tracks::GetMarkersForTrackInClip(int clip,
|
|||||||
int track,
|
int track,
|
||||||
vector<Marker>* markers) const {
|
vector<Marker>* markers) const {
|
||||||
for (int i = 0; i < markers_.size(); ++i) {
|
for (int i = 0; i < markers_.size(); ++i) {
|
||||||
if (clip == markers_[i].clip &&
|
if (clip == markers_[i].clip && track == markers_[i].track) {
|
||||||
track == markers_[i].track) {
|
|
||||||
markers->push_back(markers_[i]);
|
markers->push_back(markers_[i]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -71,15 +70,16 @@ void Tracks::GetMarkersInFrame(int clip,
|
|||||||
int frame,
|
int frame,
|
||||||
vector<Marker>* markers) const {
|
vector<Marker>* markers) const {
|
||||||
for (int i = 0; i < markers_.size(); ++i) {
|
for (int i = 0; i < markers_.size(); ++i) {
|
||||||
if (markers_[i].clip == clip &&
|
if (markers_[i].clip == clip && markers_[i].frame == frame) {
|
||||||
markers_[i].frame == frame) {
|
|
||||||
markers->push_back(markers_[i]);
|
markers->push_back(markers_[i]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Tracks::GetMarkersForTracksInBothImages(int clip1, int frame1,
|
void Tracks::GetMarkersForTracksInBothImages(int clip1,
|
||||||
int clip2, int frame2,
|
int frame1,
|
||||||
|
int clip2,
|
||||||
|
int frame2,
|
||||||
vector<Marker>* markers) const {
|
vector<Marker>* markers) const {
|
||||||
std::vector<int> image1_tracks;
|
std::vector<int> image1_tracks;
|
||||||
std::vector<int> image2_tracks;
|
std::vector<int> image2_tracks;
|
||||||
@@ -99,20 +99,19 @@ void Tracks::GetMarkersForTracksInBothImages(int clip1, int frame1,
|
|||||||
std::sort(image1_tracks.begin(), image1_tracks.end());
|
std::sort(image1_tracks.begin(), image1_tracks.end());
|
||||||
std::sort(image2_tracks.begin(), image2_tracks.end());
|
std::sort(image2_tracks.begin(), image2_tracks.end());
|
||||||
std::vector<int> intersection;
|
std::vector<int> intersection;
|
||||||
std::set_intersection(image1_tracks.begin(), image1_tracks.end(),
|
std::set_intersection(image1_tracks.begin(),
|
||||||
image2_tracks.begin(), image2_tracks.end(),
|
image1_tracks.end(),
|
||||||
|
image2_tracks.begin(),
|
||||||
|
image2_tracks.end(),
|
||||||
std::back_inserter(intersection));
|
std::back_inserter(intersection));
|
||||||
|
|
||||||
// Scan through and get the relevant tracks from the two images.
|
// Scan through and get the relevant tracks from the two images.
|
||||||
for (int i = 0; i < markers_.size(); ++i) {
|
for (int i = 0; i < markers_.size(); ++i) {
|
||||||
// Save markers that are in either frame and are in our candidate set.
|
// Save markers that are in either frame and are in our candidate set.
|
||||||
if (((markers_[i].clip == clip1 &&
|
if (((markers_[i].clip == clip1 && markers_[i].frame == frame1) ||
|
||||||
markers_[i].frame == frame1) ||
|
(markers_[i].clip == clip2 && markers_[i].frame == frame2)) &&
|
||||||
(markers_[i].clip == clip2 &&
|
std::binary_search(
|
||||||
markers_[i].frame == frame2)) &&
|
intersection.begin(), intersection.end(), markers_[i].track)) {
|
||||||
std::binary_search(intersection.begin(),
|
|
||||||
intersection.end(),
|
|
||||||
markers_[i].track)) {
|
|
||||||
markers->push_back(markers_[i]);
|
markers->push_back(markers_[i]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -122,8 +121,7 @@ void Tracks::AddMarker(const Marker& marker) {
|
|||||||
// TODO(keir): This is quadratic for repeated insertions. Fix this by adding
|
// TODO(keir): This is quadratic for repeated insertions. Fix this by adding
|
||||||
// a smarter data structure like a set<>.
|
// a smarter data structure like a set<>.
|
||||||
for (int i = 0; i < markers_.size(); ++i) {
|
for (int i = 0; i < markers_.size(); ++i) {
|
||||||
if (markers_[i].clip == marker.clip &&
|
if (markers_[i].clip == marker.clip && markers_[i].frame == marker.frame &&
|
||||||
markers_[i].frame == marker.frame &&
|
|
||||||
markers_[i].track == marker.track) {
|
markers_[i].track == marker.track) {
|
||||||
markers_[i] = marker;
|
markers_[i] = marker;
|
||||||
return;
|
return;
|
||||||
@@ -139,8 +137,7 @@ void Tracks::SetMarkers(vector<Marker>* markers) {
|
|||||||
bool Tracks::RemoveMarker(int clip, int frame, int track) {
|
bool Tracks::RemoveMarker(int clip, int frame, int track) {
|
||||||
int size = markers_.size();
|
int size = markers_.size();
|
||||||
for (int i = 0; i < markers_.size(); ++i) {
|
for (int i = 0; i < markers_.size(); ++i) {
|
||||||
if (markers_[i].clip == clip &&
|
if (markers_[i].clip == clip && markers_[i].frame == frame &&
|
||||||
markers_[i].frame == frame &&
|
|
||||||
markers_[i].track == track) {
|
markers_[i].track == track) {
|
||||||
markers_[i] = markers_[size - 1];
|
markers_[i] = markers_[size - 1];
|
||||||
markers_.resize(size - 1);
|
markers_.resize(size - 1);
|
||||||
|
@@ -23,8 +23,8 @@
|
|||||||
#ifndef LIBMV_AUTOTRACK_TRACKS_H_
|
#ifndef LIBMV_AUTOTRACK_TRACKS_H_
|
||||||
#define LIBMV_AUTOTRACK_TRACKS_H_
|
#define LIBMV_AUTOTRACK_TRACKS_H_
|
||||||
|
|
||||||
#include "libmv/base/vector.h"
|
|
||||||
#include "libmv/autotrack/marker.h"
|
#include "libmv/autotrack/marker.h"
|
||||||
|
#include "libmv/base/vector.h"
|
||||||
|
|
||||||
namespace mv {
|
namespace mv {
|
||||||
|
|
||||||
@@ -51,8 +51,10 @@ class Tracks {
|
|||||||
//
|
//
|
||||||
// This is not the same as the union of the markers in frame1 and
|
// This is not the same as the union of the markers in frame1 and
|
||||||
// frame2; each marker is for a track that appears in both images.
|
// frame2; each marker is for a track that appears in both images.
|
||||||
void GetMarkersForTracksInBothImages(int clip1, int frame1,
|
void GetMarkersForTracksInBothImages(int clip1,
|
||||||
int clip2, int frame2,
|
int frame1,
|
||||||
|
int clip2,
|
||||||
|
int frame2,
|
||||||
vector<Marker>* markers) const;
|
vector<Marker>* markers) const;
|
||||||
|
|
||||||
void AddMarker(const Marker& marker);
|
void AddMarker(const Marker& marker);
|
||||||
|
@@ -22,8 +22,8 @@
|
|||||||
|
|
||||||
#include "libmv/autotrack/tracks.h"
|
#include "libmv/autotrack/tracks.h"
|
||||||
|
|
||||||
#include "testing/testing.h"
|
|
||||||
#include "libmv/logging/logging.h"
|
#include "libmv/logging/logging.h"
|
||||||
|
#include "testing/testing.h"
|
||||||
|
|
||||||
namespace mv {
|
namespace mv {
|
||||||
|
|
||||||
|
@@ -28,6 +28,7 @@ class IdGenerator {
|
|||||||
public:
|
public:
|
||||||
IdGenerator() : next_(0) {}
|
IdGenerator() : next_(0) {}
|
||||||
ID Generate() { return next_++; }
|
ID Generate() { return next_++; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
ID next_;
|
ID next_;
|
||||||
};
|
};
|
||||||
|
@@ -26,8 +26,8 @@
|
|||||||
|
|
||||||
namespace libmv {
|
namespace libmv {
|
||||||
|
|
||||||
using std::map;
|
|
||||||
using std::make_pair;
|
using std::make_pair;
|
||||||
|
using std::map;
|
||||||
|
|
||||||
} // namespace libmv
|
} // namespace libmv
|
||||||
|
|
||||||
|
@@ -92,8 +92,10 @@ class scoped_array {
|
|||||||
T* array_;
|
T* array_;
|
||||||
|
|
||||||
// Forbid comparison of different scoped_array types.
|
// Forbid comparison of different scoped_array types.
|
||||||
template <typename T2> bool operator==(scoped_array<T2> const& p2) const;
|
template <typename T2>
|
||||||
template <typename T2> bool operator!=(scoped_array<T2> const& p2) const;
|
bool operator==(scoped_array<T2> const& p2) const;
|
||||||
|
template <typename T2>
|
||||||
|
bool operator!=(scoped_array<T2> const& p2) const;
|
||||||
|
|
||||||
// Disallow evil constructors
|
// Disallow evil constructors
|
||||||
scoped_array(const scoped_array&);
|
scoped_array(const scoped_array&);
|
||||||
|
@@ -19,9 +19,9 @@
|
|||||||
// IN THE SOFTWARE.
|
// IN THE SOFTWARE.
|
||||||
|
|
||||||
#include "libmv/base/vector.h"
|
#include "libmv/base/vector.h"
|
||||||
|
#include <algorithm>
|
||||||
#include "libmv/numeric/numeric.h"
|
#include "libmv/numeric/numeric.h"
|
||||||
#include "testing/testing.h"
|
#include "testing/testing.h"
|
||||||
#include <algorithm>
|
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
using namespace libmv;
|
using namespace libmv;
|
||||||
|
@@ -18,7 +18,6 @@
|
|||||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||||
// IN THE SOFTWARE.
|
// IN THE SOFTWARE.
|
||||||
|
|
||||||
|
|
||||||
#ifndef LIBMV_BASE_VECTOR_UTILS_H_
|
#ifndef LIBMV_BASE_VECTOR_UTILS_H_
|
||||||
#define LIBMV_BASE_VECTOR_UTILS_H_
|
#define LIBMV_BASE_VECTOR_UTILS_H_
|
||||||
|
|
||||||
|
@@ -18,16 +18,15 @@
|
|||||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||||
// IN THE SOFTWARE.
|
// IN THE SOFTWARE.
|
||||||
|
|
||||||
#include "libmv/image/image.h"
|
|
||||||
#include <iostream>
|
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
|
#include <iostream>
|
||||||
|
#include "libmv/image/image.h"
|
||||||
|
|
||||||
namespace libmv {
|
namespace libmv {
|
||||||
|
|
||||||
void FloatArrayToScaledByteArray(const Array3Df& float_array,
|
void FloatArrayToScaledByteArray(const Array3Df& float_array,
|
||||||
Array3Du* byte_array,
|
Array3Du* byte_array,
|
||||||
bool automatic_range_detection
|
bool automatic_range_detection) {
|
||||||
) {
|
|
||||||
byte_array->ResizeLike(float_array);
|
byte_array->ResizeLike(float_array);
|
||||||
float minval = HUGE_VAL;
|
float minval = HUGE_VAL;
|
||||||
float maxval = -HUGE_VAL;
|
float maxval = -HUGE_VAL;
|
||||||
|
@@ -81,13 +81,9 @@ class ArrayND : public BaseArray {
|
|||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
const Index &Shapes() const {
|
const Index& Shapes() const { return shape_; }
|
||||||
return shape_;
|
|
||||||
}
|
|
||||||
|
|
||||||
const Index &Strides() const {
|
const Index& Strides() const { return strides_; }
|
||||||
return strides_;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Create an array of shape s.
|
/// Create an array of shape s.
|
||||||
void Resize(const Index& new_shape) {
|
void Resize(const Index& new_shape) {
|
||||||
@@ -115,9 +111,7 @@ class ArrayND : public BaseArray {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Resizes the array to shape s. All data is lost.
|
/// Resizes the array to shape s. All data is lost.
|
||||||
void Resize(const int *new_shape_array) {
|
void Resize(const int* new_shape_array) { Resize(Index(new_shape_array)); }
|
||||||
Resize(Index(new_shape_array));
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Resize a 1D array to length s0.
|
/// Resize a 1D array to length s0.
|
||||||
void Resize(int s0) {
|
void Resize(int s0) {
|
||||||
@@ -136,9 +130,7 @@ class ArrayND : public BaseArray {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Match Eigen2's API.
|
// Match Eigen2's API.
|
||||||
void resize(int rows, int cols) {
|
void resize(int rows, int cols) { Resize(rows, cols); }
|
||||||
Resize(rows, cols);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Resize a 3D array to shape (s0,s1,s2).
|
/// Resize a 3D array to shape (s0,s1,s2).
|
||||||
void Resize(int s0, int s1, int s2) {
|
void Resize(int s0, int s1, int s2) {
|
||||||
@@ -171,19 +163,13 @@ class ArrayND : public BaseArray {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Return a tuple containing the length of each axis.
|
/// Return a tuple containing the length of each axis.
|
||||||
const Index &Shape() const {
|
const Index& Shape() const { return shape_; }
|
||||||
return shape_;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Return the length of an axis.
|
/// Return the length of an axis.
|
||||||
int Shape(int axis) const {
|
int Shape(int axis) const { return shape_(axis); }
|
||||||
return shape_(axis);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Return the distance between neighboring elements along axis.
|
/// Return the distance between neighboring elements along axis.
|
||||||
int Stride(int axis) const {
|
int Stride(int axis) const { return strides_(axis); }
|
||||||
return strides_(axis);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Return the number of elements of the array.
|
/// Return the number of elements of the array.
|
||||||
int Size() const {
|
int Size() const {
|
||||||
@@ -194,9 +180,7 @@ class ArrayND : public BaseArray {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Return the total amount of memory used by the array.
|
/// Return the total amount of memory used by the array.
|
||||||
int MemorySizeInBytes() const {
|
int MemorySizeInBytes() const { return sizeof(*this) + Size() * sizeof(T); }
|
||||||
return sizeof(*this) + Size() * sizeof(T);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Pointer to the first element of the array.
|
/// Pointer to the first element of the array.
|
||||||
T* Data() { return data_; }
|
T* Data() { return data_; }
|
||||||
@@ -237,9 +221,7 @@ class ArrayND : public BaseArray {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// 1D specialization.
|
/// 1D specialization.
|
||||||
T &operator()(int i0) {
|
T& operator()(int i0) { return *(Data() + Offset(i0)); }
|
||||||
return *( Data() + Offset(i0) );
|
|
||||||
}
|
|
||||||
|
|
||||||
/// 2D specialization.
|
/// 2D specialization.
|
||||||
T& operator()(int i0, int i1) {
|
T& operator()(int i0, int i1) {
|
||||||
@@ -262,9 +244,7 @@ class ArrayND : public BaseArray {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// 1D specialization.
|
/// 1D specialization.
|
||||||
const T &operator()(int i0) const {
|
const T& operator()(int i0) const { return *(Data() + Offset(i0)); }
|
||||||
return *(Data() + Offset(i0));
|
|
||||||
}
|
|
||||||
|
|
||||||
/// 2D specialization.
|
/// 2D specialization.
|
||||||
const T& operator()(int i0, int i1) const {
|
const T& operator()(int i0, int i1) const {
|
||||||
@@ -287,26 +267,24 @@ class ArrayND : public BaseArray {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// 1D specialization.
|
/// 1D specialization.
|
||||||
bool Contains(int i0) const {
|
bool Contains(int i0) const { return 0 <= i0 && i0 < Shape(0); }
|
||||||
return 0 <= i0 && i0 < Shape(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// 2D specialization.
|
/// 2D specialization.
|
||||||
bool Contains(int i0, int i1) const {
|
bool Contains(int i0, int i1) const {
|
||||||
return 0 <= i0 && i0 < Shape(0)
|
return 0 <= i0 && i0 < Shape(0) && 0 <= i1 && i1 < Shape(1);
|
||||||
&& 0 <= i1 && i1 < Shape(1);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 3D specialization.
|
/// 3D specialization.
|
||||||
bool Contains(int i0, int i1, int i2) const {
|
bool Contains(int i0, int i1, int i2) const {
|
||||||
return 0 <= i0 && i0 < Shape(0)
|
return 0 <= i0 && i0 < Shape(0) && 0 <= i1 && i1 < Shape(1) && 0 <= i2 &&
|
||||||
&& 0 <= i1 && i1 < Shape(1)
|
i2 < Shape(2);
|
||||||
&& 0 <= i2 && i2 < Shape(2);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool operator==(const ArrayND<T, N>& other) const {
|
bool operator==(const ArrayND<T, N>& other) const {
|
||||||
if (shape_ != other.shape_) return false;
|
if (shape_ != other.shape_)
|
||||||
if (strides_ != other.strides_) return false;
|
return false;
|
||||||
|
if (strides_ != other.strides_)
|
||||||
|
return false;
|
||||||
for (int i = 0; i < Size(); ++i) {
|
for (int i = 0; i < Size(); ++i) {
|
||||||
if (this->Data()[i] != other.Data()[i])
|
if (this->Data()[i] != other.Data()[i])
|
||||||
return false;
|
return false;
|
||||||
@@ -346,30 +324,20 @@ class ArrayND : public BaseArray {
|
|||||||
template <typename T>
|
template <typename T>
|
||||||
class Array3D : public ArrayND<T, 3> {
|
class Array3D : public ArrayND<T, 3> {
|
||||||
typedef ArrayND<T, 3> Base;
|
typedef ArrayND<T, 3> Base;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
Array3D()
|
Array3D() : Base() {}
|
||||||
: Base() {
|
Array3D(int height, int width, int depth = 1) : Base(height, width, depth) {}
|
||||||
}
|
|
||||||
Array3D(int height, int width, int depth = 1)
|
|
||||||
: Base(height, width, depth) {
|
|
||||||
}
|
|
||||||
Array3D(T* data, int height, int width, int depth = 1)
|
Array3D(T* data, int height, int width, int depth = 1)
|
||||||
: Base(data, height, width, depth) {
|
: Base(data, height, width, depth) {}
|
||||||
}
|
|
||||||
|
|
||||||
void Resize(int height, int width, int depth = 1) {
|
void Resize(int height, int width, int depth = 1) {
|
||||||
Base::Resize(height, width, depth);
|
Base::Resize(height, width, depth);
|
||||||
}
|
}
|
||||||
|
|
||||||
int Height() const {
|
int Height() const { return Base::Shape(0); }
|
||||||
return Base::Shape(0);
|
int Width() const { return Base::Shape(1); }
|
||||||
}
|
int Depth() const { return Base::Shape(2); }
|
||||||
int Width() const {
|
|
||||||
return Base::Shape(1);
|
|
||||||
}
|
|
||||||
int Depth() const {
|
|
||||||
return Base::Shape(2);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Match Eigen2's API so that Array3D's and Mat*'s can work together via
|
// Match Eigen2's API so that Array3D's and Mat*'s can work together via
|
||||||
// template magic.
|
// template magic.
|
||||||
@@ -420,9 +388,7 @@ void ByteArrayToScaledFloatArray(const Array3Du &byte_array,
|
|||||||
Array3Df* float_array);
|
Array3Df* float_array);
|
||||||
|
|
||||||
template <typename AArrayType, typename BArrayType, typename CArrayType>
|
template <typename AArrayType, typename BArrayType, typename CArrayType>
|
||||||
void MultiplyElements(const AArrayType &a,
|
void MultiplyElements(const AArrayType& a, const BArrayType& b, CArrayType* c) {
|
||||||
const BArrayType &b,
|
|
||||||
CArrayType *c) {
|
|
||||||
// This function does an element-wise multiply between
|
// This function does an element-wise multiply between
|
||||||
// the two Arrays A and B, and stores the result in C.
|
// the two Arrays A and B, and stores the result in C.
|
||||||
// A and B must have the same dimensions.
|
// A and B must have the same dimensions.
|
||||||
|
@@ -21,9 +21,9 @@
|
|||||||
#include "libmv/image/array_nd.h"
|
#include "libmv/image/array_nd.h"
|
||||||
#include "testing/testing.h"
|
#include "testing/testing.h"
|
||||||
|
|
||||||
using libmv::ArrayND;
|
|
||||||
using libmv::Array3D;
|
using libmv::Array3D;
|
||||||
using libmv::Array3Df;
|
using libmv::Array3Df;
|
||||||
|
using libmv::ArrayND;
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
|
@@ -64,9 +64,14 @@ void ComputeGaussianKernel(double sigma, Vec *kernel, Vec *derivative) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
template <int size, bool vertical>
|
template <int size, bool vertical>
|
||||||
void FastConvolve(const Vec &kernel, int width, int height,
|
void FastConvolve(const Vec& kernel,
|
||||||
const float* src, int src_stride, int src_line_stride,
|
int width,
|
||||||
float* dst, int dst_stride) {
|
int height,
|
||||||
|
const float* src,
|
||||||
|
int src_stride,
|
||||||
|
int src_line_stride,
|
||||||
|
float* dst,
|
||||||
|
int dst_stride) {
|
||||||
double coefficients[2 * size + 1];
|
double coefficients[2 * size + 1];
|
||||||
for (int k = 0; k < 2 * size + 1; ++k) {
|
for (int k = 0; k < 2 * size + 1; ++k) {
|
||||||
coefficients[k] = kernel(2 * size - k);
|
coefficients[k] = kernel(2 * size - k);
|
||||||
@@ -119,19 +124,22 @@ void Convolve(const Array3Df &in,
|
|||||||
// fast path.
|
// fast path.
|
||||||
int half_width = kernel.size() / 2;
|
int half_width = kernel.size() / 2;
|
||||||
switch (half_width) {
|
switch (half_width) {
|
||||||
#define static_convolution(size) case size: \
|
#define static_convolution(size) \
|
||||||
FastConvolve<size, vertical>(kernel, width, height, src, src_stride, \
|
case size: \
|
||||||
src_line_stride, dst, dst_stride); break;
|
FastConvolve<size, vertical>(kernel, \
|
||||||
static_convolution(1)
|
width, \
|
||||||
static_convolution(2)
|
height, \
|
||||||
static_convolution(3)
|
src, \
|
||||||
static_convolution(4)
|
src_stride, \
|
||||||
static_convolution(5)
|
src_line_stride, \
|
||||||
static_convolution(6)
|
dst, \
|
||||||
|
dst_stride); \
|
||||||
|
break;
|
||||||
|
static_convolution(1) static_convolution(2) static_convolution(3)
|
||||||
|
static_convolution(4) static_convolution(5) static_convolution(6)
|
||||||
static_convolution(7)
|
static_convolution(7)
|
||||||
#undef static_convolution
|
#undef static_convolution
|
||||||
default:
|
default : int dynamic_size = kernel.size() / 2;
|
||||||
int dynamic_size = kernel.size() / 2;
|
|
||||||
for (int y = 0; y < height; ++y) {
|
for (int y = 0; y < height; ++y) {
|
||||||
for (int x = 0; x < width; ++x) {
|
for (int x = 0; x < width; ++x) {
|
||||||
double sum = 0;
|
double sum = 0;
|
||||||
@@ -171,9 +179,7 @@ void ConvolveVertical(const Array3Df &in,
|
|||||||
Convolve<true>(in, kernel, out_pointer, plane);
|
Convolve<true>(in, kernel, out_pointer, plane);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ConvolveGaussian(const Array3Df &in,
|
void ConvolveGaussian(const Array3Df& in, double sigma, Array3Df* out_pointer) {
|
||||||
double sigma,
|
|
||||||
Array3Df *out_pointer) {
|
|
||||||
Vec kernel, derivative;
|
Vec kernel, derivative;
|
||||||
ComputeGaussianKernel(sigma, &kernel, &derivative);
|
ComputeGaussianKernel(sigma, &kernel, &derivative);
|
||||||
|
|
||||||
@@ -314,9 +320,7 @@ void BoxFilterVertical(const Array3Df &in,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void BoxFilter(const Array3Df &in,
|
void BoxFilter(const Array3Df& in, int box_width, Array3Df* out) {
|
||||||
int box_width,
|
|
||||||
Array3Df *out) {
|
|
||||||
Array3Df tmp;
|
Array3Df tmp;
|
||||||
BoxFilterHorizontal(in, box_width, &tmp);
|
BoxFilterHorizontal(in, box_width, &tmp);
|
||||||
BoxFilterVertical(tmp, box_width, out);
|
BoxFilterVertical(tmp, box_width, out);
|
||||||
@@ -330,13 +334,13 @@ void LaplaceFilter(unsigned char* src,
|
|||||||
for (int y = 1; y < height - 1; y++)
|
for (int y = 1; y < height - 1; y++)
|
||||||
for (int x = 1; x < width - 1; x++) {
|
for (int x = 1; x < width - 1; x++) {
|
||||||
const unsigned char* s = &src[y * width + x];
|
const unsigned char* s = &src[y * width + x];
|
||||||
int l = 128 +
|
int l = 128 + s[-width - 1] + s[-width] + s[-width + 1] + s[1] -
|
||||||
s[-width-1] + s[-width] + s[-width+1] +
|
8 * s[0] + s[1] + s[width - 1] + s[width] + s[width + 1];
|
||||||
s[1] - 8*s[0] + s[1] +
|
|
||||||
s[ width-1] + s[ width] + s[ width+1];
|
|
||||||
int d = ((256 - strength) * s[0] + strength * l) / 256;
|
int d = ((256 - strength) * s[0] + strength * l) / 256;
|
||||||
if (d < 0) d=0;
|
if (d < 0)
|
||||||
if (d > 255) d=255;
|
d = 0;
|
||||||
|
if (d > 255)
|
||||||
|
d = 255;
|
||||||
dst[y * width + x] = d;
|
dst[y * width + x] = d;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -35,7 +35,8 @@ inline double Gaussian(double x, double sigma) {
|
|||||||
// 2D gaussian (zero mean)
|
// 2D gaussian (zero mean)
|
||||||
// (9) in http://mathworld.wolfram.com/GaussianFunction.html
|
// (9) in http://mathworld.wolfram.com/GaussianFunction.html
|
||||||
inline double Gaussian2D(double x, double y, double sigma) {
|
inline double Gaussian2D(double x, double y, double sigma) {
|
||||||
return 1.0/(2.0*M_PI*sigma*sigma) * exp( -(x*x+y*y)/(2.0*sigma*sigma));
|
return 1.0 / (2.0 * M_PI * sigma * sigma) *
|
||||||
|
exp(-(x * x + y * y) / (2.0 * sigma * sigma));
|
||||||
}
|
}
|
||||||
inline double GaussianDerivative(double x, double sigma) {
|
inline double GaussianDerivative(double x, double sigma) {
|
||||||
return -x / sigma / sigma * Gaussian(x, sigma);
|
return -x / sigma / sigma * Gaussian(x, sigma);
|
||||||
@@ -83,17 +84,17 @@ void BoxFilterVertical(const FloatImage &in,
|
|||||||
int window_size,
|
int window_size,
|
||||||
FloatImage* out_pointer);
|
FloatImage* out_pointer);
|
||||||
|
|
||||||
void BoxFilter(const FloatImage &in,
|
void BoxFilter(const FloatImage& in, int box_width, FloatImage* out);
|
||||||
int box_width,
|
|
||||||
FloatImage *out);
|
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
Convolve \a src into \a dst with the discrete laplacian operator.
|
Convolve \a src into \a dst with the discrete laplacian operator.
|
||||||
|
|
||||||
\a src and \a dst should be \a width x \a height images.
|
\a src and \a dst should be \a width x \a height images.
|
||||||
\a strength is an interpolation coefficient (0-256) between original image and the laplacian.
|
\a strength is an interpolation coefficient (0-256) between original image
|
||||||
|
and the laplacian.
|
||||||
|
|
||||||
\note Make sure the search region is filtered with the same strength as the pattern.
|
\note Make sure the search region is filtered with the same strength as the
|
||||||
|
pattern.
|
||||||
*/
|
*/
|
||||||
void LaplaceFilter(unsigned char* src,
|
void LaplaceFilter(unsigned char* src,
|
||||||
unsigned char* dst,
|
unsigned char* dst,
|
||||||
@@ -104,4 +105,3 @@ void LaplaceFilter(unsigned char* src,
|
|||||||
} // namespace libmv
|
} // namespace libmv
|
||||||
|
|
||||||
#endif // LIBMV_IMAGE_CONVOLVE_H_
|
#endif // LIBMV_IMAGE_CONVOLVE_H_
|
||||||
|
|
||||||
|
@@ -21,8 +21,8 @@
|
|||||||
#ifndef LIBMV_IMAGE_CORRELATION_H
|
#ifndef LIBMV_IMAGE_CORRELATION_H
|
||||||
#define LIBMV_IMAGE_CORRELATION_H
|
#define LIBMV_IMAGE_CORRELATION_H
|
||||||
|
|
||||||
#include "libmv/logging/logging.h"
|
|
||||||
#include "libmv/image/image.h"
|
#include "libmv/image/image.h"
|
||||||
|
#include "libmv/logging/logging.h"
|
||||||
|
|
||||||
namespace libmv {
|
namespace libmv {
|
||||||
|
|
||||||
@@ -63,9 +63,8 @@ inline double PearsonProductMomentCorrelation(
|
|||||||
double covariance_xy = sXY - sX * sY;
|
double covariance_xy = sXY - sX * sY;
|
||||||
|
|
||||||
double correlation = covariance_xy / sqrt(var_x * var_y);
|
double correlation = covariance_xy / sqrt(var_x * var_y);
|
||||||
LG << "Covariance xy: " << covariance_xy
|
LG << "Covariance xy: " << covariance_xy << ", var 1: " << var_x
|
||||||
<< ", var 1: " << var_x << ", var 2: " << var_y
|
<< ", var 2: " << var_y << ", correlation: " << correlation;
|
||||||
<< ", correlation: " << correlation;
|
|
||||||
return correlation;
|
return correlation;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -39,14 +39,11 @@ typedef Array3Ds ShortImage;
|
|||||||
// is the best solution after all.
|
// is the best solution after all.
|
||||||
class Image {
|
class Image {
|
||||||
public:
|
public:
|
||||||
|
|
||||||
// Create an image from an array. The image takes ownership of the array.
|
// Create an image from an array. The image takes ownership of the array.
|
||||||
Image(Array3Du* array) : array_type_(BYTE), array_(array) {}
|
Image(Array3Du* array) : array_type_(BYTE), array_(array) {}
|
||||||
Image(Array3Df* array) : array_type_(FLOAT), array_(array) {}
|
Image(Array3Df* array) : array_type_(FLOAT), array_(array) {}
|
||||||
|
|
||||||
Image(const Image &img): array_type_(NONE), array_(NULL) {
|
Image(const Image& img) : array_type_(NONE), array_(NULL) { *this = img; }
|
||||||
*this = img;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Underlying data type.
|
// Underlying data type.
|
||||||
enum DataType {
|
enum DataType {
|
||||||
@@ -73,9 +70,7 @@ class Image {
|
|||||||
case SHORT:
|
case SHORT:
|
||||||
size = reinterpret_cast<Array3Ds*>(array_)->MemorySizeInBytes();
|
size = reinterpret_cast<Array3Ds*>(array_)->MemorySizeInBytes();
|
||||||
break;
|
break;
|
||||||
default :
|
default: size = 0; assert(0);
|
||||||
size = 0;
|
|
||||||
assert(0);
|
|
||||||
}
|
}
|
||||||
size += sizeof(*this);
|
size += sizeof(*this);
|
||||||
return size;
|
return size;
|
||||||
@@ -83,24 +78,11 @@ class Image {
|
|||||||
|
|
||||||
~Image() {
|
~Image() {
|
||||||
switch (array_type_) {
|
switch (array_type_) {
|
||||||
case BYTE:
|
case BYTE: delete reinterpret_cast<Array3Du*>(array_); break;
|
||||||
delete reinterpret_cast<Array3Du *>(array_);
|
case FLOAT: delete reinterpret_cast<Array3Df*>(array_); break;
|
||||||
|
case INT: delete reinterpret_cast<Array3Di*>(array_); break;
|
||||||
break;
|
case SHORT: delete reinterpret_cast<Array3Ds*>(array_); break;
|
||||||
case FLOAT:
|
default: assert(0);
|
||||||
delete reinterpret_cast<Array3Df *>(array_);
|
|
||||||
|
|
||||||
break;
|
|
||||||
case INT:
|
|
||||||
delete reinterpret_cast<Array3Di *>(array_);
|
|
||||||
|
|
||||||
break;
|
|
||||||
case SHORT:
|
|
||||||
delete reinterpret_cast<Array3Ds *>(array_);
|
|
||||||
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
assert(0);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -124,8 +106,7 @@ class Image {
|
|||||||
delete reinterpret_cast<Array3Ds*>(array_);
|
delete reinterpret_cast<Array3Ds*>(array_);
|
||||||
array_ = new Array3Ds(*(Array3Ds*)f.array_);
|
array_ = new Array3Ds(*(Array3Ds*)f.array_);
|
||||||
break;
|
break;
|
||||||
default:
|
default: assert(0);
|
||||||
assert(0);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return *this;
|
return *this;
|
||||||
|
@@ -53,7 +53,8 @@ void Rgb2Gray(const ImageIn &imaIn, ImageOut *imaOut) {
|
|||||||
|
|
||||||
for (int j = 0; j < imaIn.Height(); ++j) {
|
for (int j = 0; j < imaIn.Height(); ++j) {
|
||||||
for (int i = 0; i < imaIn.Width(); ++i) {
|
for (int i = 0; i < imaIn.Width(); ++i) {
|
||||||
(*imaOut)(j, i) = RGB2GRAY(imaIn(j, i, 0) , imaIn(j, i, 1), imaIn(j, i, 2));
|
(*imaOut)(j, i) =
|
||||||
|
RGB2GRAY(imaIn(j, i, 0), imaIn(j, i, 1), imaIn(j, i, 2));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -59,14 +59,18 @@ inline void safePutPixel(int yc, int xc, const Color *col, Image *pim) {
|
|||||||
// Add the rotation of the ellipse.
|
// Add the rotation of the ellipse.
|
||||||
// As the algo. use symmetry we must use 4 rotations.
|
// As the algo. use symmetry we must use 4 rotations.
|
||||||
template <class Image, class Color>
|
template <class Image, class Color>
|
||||||
void DrawEllipse(int xc, int yc, int radiusA, int radiusB,
|
void DrawEllipse(int xc,
|
||||||
const Color &col, Image *pim, double angle = 0.0) {
|
int yc,
|
||||||
|
int radiusA,
|
||||||
|
int radiusB,
|
||||||
|
const Color& col,
|
||||||
|
Image* pim,
|
||||||
|
double angle = 0.0) {
|
||||||
int a = radiusA;
|
int a = radiusA;
|
||||||
int b = radiusB;
|
int b = radiusB;
|
||||||
|
|
||||||
// Counter Clockwise rotation matrix.
|
// Counter Clockwise rotation matrix.
|
||||||
double matXY[4] = { cos(angle), sin(angle),
|
double matXY[4] = {cos(angle), sin(angle), -sin(angle), cos(angle)};
|
||||||
-sin(angle), cos(angle)};
|
|
||||||
int x, y;
|
int x, y;
|
||||||
double d1, d2;
|
double d1, d2;
|
||||||
x = 0;
|
x = 0;
|
||||||
@@ -139,10 +143,10 @@ void DrawEllipse(int xc, int yc, int radiusA, int radiusB,
|
|||||||
template <class Image, class Color>
|
template <class Image, class Color>
|
||||||
void DrawCircle(int x, int y, int radius, const Color& col, Image* pim) {
|
void DrawCircle(int x, int y, int radius, const Color& col, Image* pim) {
|
||||||
Image& im = *pim;
|
Image& im = *pim;
|
||||||
if ( im.Contains(y + radius, x + radius)
|
if (im.Contains(y + radius, x + radius) ||
|
||||||
|| im.Contains(y + radius, x - radius)
|
im.Contains(y + radius, x - radius) ||
|
||||||
|| im.Contains(y - radius, x + radius)
|
im.Contains(y - radius, x + radius) ||
|
||||||
|| im.Contains(y - radius, x - radius)) {
|
im.Contains(y - radius, x - radius)) {
|
||||||
int x1 = 0;
|
int x1 = 0;
|
||||||
int y1 = radius;
|
int y1 = radius;
|
||||||
int d = radius - 1;
|
int d = radius - 1;
|
||||||
@@ -185,13 +189,14 @@ void DrawLine(int xa, int ya, int xb, int yb, const Color &col, Image *pim) {
|
|||||||
int width = pim->Width();
|
int width = pim->Width();
|
||||||
int height = pim->Height();
|
int height = pim->Height();
|
||||||
const bool xdir = xa < xb, ydir = ya < yb;
|
const bool xdir = xa < xb, ydir = ya < yb;
|
||||||
float nx0 = xa, nx1 = xb, ny0 = ya, ny1 = yb,
|
float nx0 = xa, nx1 = xb, ny0 = ya, ny1 = yb, &xleft = xdir ? nx0 : nx1,
|
||||||
&xleft = xdir?nx0:nx1, &yleft = xdir?ny0:ny1,
|
&yleft = xdir ? ny0 : ny1, &xright = xdir ? nx1 : nx0,
|
||||||
&xright = xdir?nx1:nx0, &yright = xdir?ny1:ny0,
|
&yright = xdir ? ny1 : ny0, &xup = ydir ? nx0 : nx1,
|
||||||
&xup = ydir?nx0:nx1, &yup = ydir?ny0:ny1,
|
&yup = ydir ? ny0 : ny1, &xdown = ydir ? nx1 : nx0,
|
||||||
&xdown = ydir?nx1:nx0, &ydown = ydir?ny1:ny0;
|
&ydown = ydir ? ny1 : ny0;
|
||||||
|
|
||||||
if (xright < 0 || xleft >= width) return;
|
if (xright < 0 || xleft >= width)
|
||||||
|
return;
|
||||||
if (xleft < 0) {
|
if (xleft < 0) {
|
||||||
yleft -= xleft * (yright - yleft) / (xright - xleft);
|
yleft -= xleft * (yright - yleft) / (xright - xleft);
|
||||||
xleft = 0;
|
xleft = 0;
|
||||||
@@ -200,7 +205,8 @@ void DrawLine(int xa, int ya, int xb, int yb, const Color &col, Image *pim) {
|
|||||||
yright -= (xright - width) * (yright - yleft) / (xright - xleft);
|
yright -= (xright - width) * (yright - yleft) / (xright - xleft);
|
||||||
xright = width - 1;
|
xright = width - 1;
|
||||||
}
|
}
|
||||||
if (ydown < 0 || yup >= height) return;
|
if (ydown < 0 || yup >= height)
|
||||||
|
return;
|
||||||
if (yup < 0) {
|
if (yup < 0) {
|
||||||
xup -= yup * (xdown - xup) / (ydown - yup);
|
xup -= yup * (xdown - xup) / (ydown - yup);
|
||||||
yup = 0;
|
yup = 0;
|
||||||
|
@@ -23,8 +23,8 @@
|
|||||||
#include "libmv/image/image.h"
|
#include "libmv/image/image.h"
|
||||||
#include "testing/testing.h"
|
#include "testing/testing.h"
|
||||||
|
|
||||||
using libmv::Image;
|
|
||||||
using libmv::Array3Df;
|
using libmv::Array3Df;
|
||||||
|
using libmv::Image;
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
|
@@ -27,16 +27,13 @@ namespace libmv {
|
|||||||
|
|
||||||
/// Nearest neighbor interpolation.
|
/// Nearest neighbor interpolation.
|
||||||
template <typename T>
|
template <typename T>
|
||||||
inline T SampleNearest(const Array3D<T> &image,
|
inline T SampleNearest(const Array3D<T>& image, float y, float x, int v = 0) {
|
||||||
float y, float x, int v = 0) {
|
|
||||||
const int i = int(round(y));
|
const int i = int(round(y));
|
||||||
const int j = int(round(x));
|
const int j = int(round(x));
|
||||||
return image(i, j, v);
|
return image(i, j, v);
|
||||||
}
|
}
|
||||||
|
|
||||||
inline void LinearInitAxis(float x, int size,
|
inline void LinearInitAxis(float x, int size, int* x1, int* x2, float* dx) {
|
||||||
int *x1, int *x2,
|
|
||||||
float *dx) {
|
|
||||||
const int ix = static_cast<int>(x);
|
const int ix = static_cast<int>(x);
|
||||||
if (ix < 0) {
|
if (ix < 0) {
|
||||||
*x1 = 0;
|
*x1 = 0;
|
||||||
@@ -106,10 +103,12 @@ inline void DownsampleChannelsBy2(const Array3Df &in, Array3Df *out) {
|
|||||||
for (int r = 0; r < height; ++r) {
|
for (int r = 0; r < height; ++r) {
|
||||||
for (int c = 0; c < width; ++c) {
|
for (int c = 0; c < width; ++c) {
|
||||||
for (int k = 0; k < depth; ++k) {
|
for (int k = 0; k < depth; ++k) {
|
||||||
|
// clang-format off
|
||||||
(*out)(r, c, k) = (in(2 * r, 2 * c, k) +
|
(*out)(r, c, k) = (in(2 * r, 2 * c, k) +
|
||||||
in(2 * r + 1, 2 * c, k) +
|
in(2 * r + 1, 2 * c, k) +
|
||||||
in(2 * r, 2 * c + 1, k) +
|
in(2 * r, 2 * c + 1, k) +
|
||||||
in(2 * r + 1, 2 * c + 1, k)) / 4.0f;
|
in(2 * r + 1, 2 * c + 1, k)) / 4.0f;
|
||||||
|
// clang-format on
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -118,7 +117,8 @@ inline void DownsampleChannelsBy2(const Array3Df &in, Array3Df *out) {
|
|||||||
// Sample a region centered at x,y in image with size extending by half_width
|
// Sample a region centered at x,y in image with size extending by half_width
|
||||||
// from x,y. Channels specifies the number of channels to sample from.
|
// from x,y. Channels specifies the number of channels to sample from.
|
||||||
inline void SamplePattern(const FloatImage& image,
|
inline void SamplePattern(const FloatImage& image,
|
||||||
double x, double y,
|
double x,
|
||||||
|
double y,
|
||||||
int half_width,
|
int half_width,
|
||||||
int channels,
|
int channels,
|
||||||
FloatImage* sampled) {
|
FloatImage* sampled) {
|
||||||
|
@@ -34,10 +34,14 @@ class Tuple {
|
|||||||
Tuple(T initial_value) { Reset(initial_value); }
|
Tuple(T initial_value) { Reset(initial_value); }
|
||||||
|
|
||||||
template <typename D>
|
template <typename D>
|
||||||
Tuple(D *values) { Reset(values); }
|
Tuple(D* values) {
|
||||||
|
Reset(values);
|
||||||
|
}
|
||||||
|
|
||||||
template <typename D>
|
template <typename D>
|
||||||
Tuple(const Tuple<D, N> &b) { Reset(b); }
|
Tuple(const Tuple<D, N>& b) {
|
||||||
|
Reset(b);
|
||||||
|
}
|
||||||
|
|
||||||
template <typename D>
|
template <typename D>
|
||||||
Tuple& operator=(const Tuple<D, N>& b) {
|
Tuple& operator=(const Tuple<D, N>& b) {
|
||||||
@@ -46,7 +50,9 @@ class Tuple {
|
|||||||
}
|
}
|
||||||
|
|
||||||
template <typename D>
|
template <typename D>
|
||||||
void Reset(const Tuple<D, N>& b) { Reset(b.Data()); }
|
void Reset(const Tuple<D, N>& b) {
|
||||||
|
Reset(b.Data());
|
||||||
|
}
|
||||||
|
|
||||||
template <typename D>
|
template <typename D>
|
||||||
void Reset(D* values) {
|
void Reset(D* values) {
|
||||||
@@ -77,9 +83,7 @@ class Tuple {
|
|||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
bool operator!=(const Tuple<T, N> &other) const {
|
bool operator!=(const Tuple<T, N>& other) const { return !(*this == other); }
|
||||||
return !(*this == other);
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
T data_[N];
|
T data_[N];
|
||||||
|
@@ -38,9 +38,11 @@ void PreconditionerFromPoints(const Mat &points, Mat3 *T) {
|
|||||||
if (variance(1) < 1e-8)
|
if (variance(1) < 1e-8)
|
||||||
yfactor = mean(1) = 1.0;
|
yfactor = mean(1) = 1.0;
|
||||||
|
|
||||||
|
// clang-format off
|
||||||
*T << xfactor, 0, -xfactor * mean(0),
|
*T << xfactor, 0, -xfactor * mean(0),
|
||||||
0, yfactor, -yfactor * mean(1),
|
0, yfactor, -yfactor * mean(1),
|
||||||
0, 0, 1;
|
0, 0, 1;
|
||||||
|
// clang-format on
|
||||||
}
|
}
|
||||||
// HZ 4.4.4 pag.107: Point conditioning (isotropic)
|
// HZ 4.4.4 pag.107: Point conditioning (isotropic)
|
||||||
void IsotropicPreconditionerFromPoints(const Mat& points, Mat3* T) {
|
void IsotropicPreconditionerFromPoints(const Mat& points, Mat3* T) {
|
||||||
@@ -57,9 +59,11 @@ void IsotropicPreconditionerFromPoints(const Mat &points, Mat3 *T) {
|
|||||||
mean.setOnes();
|
mean.setOnes();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// clang-format off
|
||||||
*T << factor, 0, -factor * mean(0),
|
*T << factor, 0, -factor * mean(0),
|
||||||
0, factor, -factor * mean(1),
|
0, factor, -factor * mean(1),
|
||||||
0, 0, 1;
|
0, 0, 1;
|
||||||
|
// clang-format on
|
||||||
}
|
}
|
||||||
|
|
||||||
void ApplyTransformationToPoints(const Mat& points,
|
void ApplyTransformationToPoints(const Mat& points,
|
||||||
@@ -73,9 +77,7 @@ void ApplyTransformationToPoints(const Mat &points,
|
|||||||
HomogeneousToEuclidean(p, transformed_points);
|
HomogeneousToEuclidean(p, transformed_points);
|
||||||
}
|
}
|
||||||
|
|
||||||
void NormalizePoints(const Mat &points,
|
void NormalizePoints(const Mat& points, Mat* normalized_points, Mat3* T) {
|
||||||
Mat *normalized_points,
|
|
||||||
Mat3 *T) {
|
|
||||||
PreconditionerFromPoints(points, T);
|
PreconditionerFromPoints(points, T);
|
||||||
ApplyTransformationToPoints(points, *T, normalized_points);
|
ApplyTransformationToPoints(points, *T, normalized_points);
|
||||||
}
|
}
|
||||||
|
@@ -34,9 +34,7 @@ void ApplyTransformationToPoints(const Mat &points,
|
|||||||
const Mat3& T,
|
const Mat3& T,
|
||||||
Mat* transformed_points);
|
Mat* transformed_points);
|
||||||
|
|
||||||
void NormalizePoints(const Mat &points,
|
void NormalizePoints(const Mat& points, Mat* normalized_points, Mat3* T);
|
||||||
Mat *normalized_points,
|
|
||||||
Mat3 *T);
|
|
||||||
|
|
||||||
void NormalizeIsotropicPoints(const Mat& points,
|
void NormalizeIsotropicPoints(const Mat& points,
|
||||||
Mat* normalized_points,
|
Mat* normalized_points,
|
||||||
|
@@ -23,8 +23,8 @@
|
|||||||
#include <cmath>
|
#include <cmath>
|
||||||
#include <limits>
|
#include <limits>
|
||||||
|
|
||||||
#include <Eigen/SVD>
|
|
||||||
#include <Eigen/Geometry>
|
#include <Eigen/Geometry>
|
||||||
|
#include <Eigen/SVD>
|
||||||
|
|
||||||
#include "libmv/base/vector.h"
|
#include "libmv/base/vector.h"
|
||||||
#include "libmv/logging/logging.h"
|
#include "libmv/logging/logging.h"
|
||||||
@@ -37,7 +37,8 @@ typedef unsigned int uint;
|
|||||||
|
|
||||||
bool EuclideanResection(const Mat2X& x_camera,
|
bool EuclideanResection(const Mat2X& x_camera,
|
||||||
const Mat3X& X_world,
|
const Mat3X& X_world,
|
||||||
Mat3 *R, Vec3 *t,
|
Mat3* R,
|
||||||
|
Vec3* t,
|
||||||
ResectionMethod method) {
|
ResectionMethod method) {
|
||||||
switch (method) {
|
switch (method) {
|
||||||
case RESECTION_ANSAR_DANIILIDIS:
|
case RESECTION_ANSAR_DANIILIDIS:
|
||||||
@@ -49,8 +50,7 @@ bool EuclideanResection(const Mat2X &x_camera,
|
|||||||
case RESECTION_PPNP:
|
case RESECTION_PPNP:
|
||||||
return EuclideanResectionPPnP(x_camera, X_world, R, t);
|
return EuclideanResectionPPnP(x_camera, X_world, R, t);
|
||||||
break;
|
break;
|
||||||
default:
|
default: LOG(FATAL) << "Unknown resection method.";
|
||||||
LOG(FATAL) << "Unknown resection method.";
|
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -58,11 +58,12 @@ bool EuclideanResection(const Mat2X &x_camera,
|
|||||||
bool EuclideanResection(const Mat& x_image,
|
bool EuclideanResection(const Mat& x_image,
|
||||||
const Mat3X& X_world,
|
const Mat3X& X_world,
|
||||||
const Mat3& K,
|
const Mat3& K,
|
||||||
Mat3 *R, Vec3 *t,
|
Mat3* R,
|
||||||
|
Vec3* t,
|
||||||
ResectionMethod method) {
|
ResectionMethod method) {
|
||||||
CHECK(x_image.rows() == 2 || x_image.rows() == 3)
|
CHECK(x_image.rows() == 2 || x_image.rows() == 3)
|
||||||
<< "Invalid size for x_image: "
|
<< "Invalid size for x_image: " << x_image.rows() << "x"
|
||||||
<< x_image.rows() << "x" << x_image.cols();
|
<< x_image.cols();
|
||||||
|
|
||||||
Mat2X x_camera;
|
Mat2X x_camera;
|
||||||
if (x_image.rows() == 2) {
|
if (x_image.rows() == 2) {
|
||||||
@@ -73,10 +74,7 @@ bool EuclideanResection(const Mat &x_image,
|
|||||||
return EuclideanResection(x_camera, X_world, R, t, method);
|
return EuclideanResection(x_camera, X_world, R, t, method);
|
||||||
}
|
}
|
||||||
|
|
||||||
void AbsoluteOrientation(const Mat3X &X,
|
void AbsoluteOrientation(const Mat3X& X, const Mat3X& Xp, Mat3* R, Vec3* t) {
|
||||||
const Mat3X &Xp,
|
|
||||||
Mat3 *R,
|
|
||||||
Vec3 *t) {
|
|
||||||
int num_points = X.cols();
|
int num_points = X.cols();
|
||||||
Vec3 C = X.rowwise().sum() / num_points; // Centroid of X.
|
Vec3 C = X.rowwise().sum() / num_points; // Centroid of X.
|
||||||
Vec3 Cp = Xp.rowwise().sum() / num_points; // Centroid of Xp.
|
Vec3 Cp = Xp.rowwise().sum() / num_points; // Centroid of Xp.
|
||||||
@@ -100,10 +98,12 @@ void AbsoluteOrientation(const Mat3X &X,
|
|||||||
double Szy = Xn.row(2).dot(Xpn.row(1));
|
double Szy = Xn.row(2).dot(Xpn.row(1));
|
||||||
|
|
||||||
Mat4 N;
|
Mat4 N;
|
||||||
|
// clang-format off
|
||||||
N << Sxx + Syy + Szz, Syz - Szy, Szx - Sxz, Sxy - Syx,
|
N << Sxx + Syy + Szz, Syz - Szy, Szx - Sxz, Sxy - Syx,
|
||||||
Syz - Szy, Sxx - Syy - Szz, Sxy + Syx, Szx + Sxz,
|
Syz - Szy, Sxx - Syy - Szz, Sxy + Syx, Szx + Sxz,
|
||||||
Szx - Sxz, Sxy + Syx, -Sxx + Syy - Szz, Syz + Szy,
|
Szx - Sxz, Sxy + Syx, -Sxx + Syy - Szz, Syz + Szy,
|
||||||
Sxy - Syx, Szx + Sxz, Syz + Szy, -Sxx - Syy + Szz;
|
Sxy - Syx, Szx + Sxz, Syz + Szy, -Sxx - Syy + Szz;
|
||||||
|
// clang-format on
|
||||||
|
|
||||||
// Find the unit quaternion q that maximizes qNq. It is the eigenvector
|
// Find the unit quaternion q that maximizes qNq. It is the eigenvector
|
||||||
// corresponding to the lagest eigenvalue.
|
// corresponding to the lagest eigenvalue.
|
||||||
@@ -118,6 +118,7 @@ void AbsoluteOrientation(const Mat3X &X,
|
|||||||
double q1q3 = q(1) * q(3);
|
double q1q3 = q(1) * q(3);
|
||||||
double q2q3 = q(2) * q(3);
|
double q2q3 = q(2) * q(3);
|
||||||
|
|
||||||
|
// clang-format off
|
||||||
(*R) << qq(0) + qq(1) - qq(2) - qq(3),
|
(*R) << qq(0) + qq(1) - qq(2) - qq(3),
|
||||||
2 * (q1q2 - q0q3),
|
2 * (q1q2 - q0q3),
|
||||||
2 * (q1q3 + q0q2),
|
2 * (q1q3 + q0q2),
|
||||||
@@ -127,6 +128,7 @@ void AbsoluteOrientation(const Mat3X &X,
|
|||||||
2 * (q1q3 - q0q2),
|
2 * (q1q3 - q0q2),
|
||||||
2 * (q2q3 + q0q1),
|
2 * (q2q3 + q0q1),
|
||||||
qq(0) - qq(1) - qq(2) + qq(3);
|
qq(0) - qq(1) - qq(2) + qq(3);
|
||||||
|
// clang-format on
|
||||||
|
|
||||||
// Fix the handedness of the R matrix.
|
// Fix the handedness of the R matrix.
|
||||||
if (R->determinant() < 0) {
|
if (R->determinant() < 0) {
|
||||||
@@ -176,9 +178,7 @@ static int Sign(double value) {
|
|||||||
// Lambda to create the constraints in equation (5) in "Linear Pose Estimation
|
// Lambda to create the constraints in equation (5) in "Linear Pose Estimation
|
||||||
// from Points or Lines", by Ansar, A. and Daniilidis, PAMI 2003. vol. 25, no.
|
// from Points or Lines", by Ansar, A. and Daniilidis, PAMI 2003. vol. 25, no.
|
||||||
// 5.
|
// 5.
|
||||||
static Vec MatrixToConstraint(const Mat &A,
|
static Vec MatrixToConstraint(const Mat& A, int num_k_columns, int num_lambda) {
|
||||||
int num_k_columns,
|
|
||||||
int num_lambda) {
|
|
||||||
Vec C(num_k_columns);
|
Vec C(num_k_columns);
|
||||||
C.setZero();
|
C.setZero();
|
||||||
int idx = 0;
|
int idx = 0;
|
||||||
@@ -246,17 +246,17 @@ void EuclideanResectionAnsarDaniilidis(const Mat2X &x_camera,
|
|||||||
}
|
}
|
||||||
|
|
||||||
int num_lambda = num_points + 1; // Dimension of the null space of M.
|
int num_lambda = num_points + 1; // Dimension of the null space of M.
|
||||||
Mat V = M.jacobiSvd(Eigen::ComputeFullV).matrixV().block(0,
|
Mat V = M.jacobiSvd(Eigen::ComputeFullV)
|
||||||
num_m_rows,
|
.matrixV()
|
||||||
num_m_columns,
|
.block(0, num_m_rows, num_m_columns, num_lambda);
|
||||||
num_lambda);
|
|
||||||
|
|
||||||
// TODO(vess): The number of constraint equations in K (num_k_rows) must be
|
// TODO(vess): The number of constraint equations in K (num_k_rows) must be
|
||||||
// (num_points + 1) * (num_points + 2)/2. This creates a performance issue
|
// (num_points + 1) * (num_points + 2)/2. This creates a performance issue
|
||||||
// for more than 4 points. It is fine for 4 points at the moment with 18
|
// for more than 4 points. It is fine for 4 points at the moment with 18
|
||||||
// instead of 15 equations.
|
// instead of 15 equations.
|
||||||
int num_k_rows = num_m_rows + num_points *
|
int num_k_rows =
|
||||||
(num_points*(num_points-1)/2 - num_points+1);
|
num_m_rows +
|
||||||
|
num_points * (num_points * (num_points - 1) / 2 - num_points + 1);
|
||||||
int num_k_columns = num_lambda * (num_lambda + 1) / 2;
|
int num_k_columns = num_lambda * (num_lambda + 1) / 2;
|
||||||
Mat K(num_k_rows, num_k_columns);
|
Mat K(num_k_rows, num_k_columns);
|
||||||
K.setZero();
|
K.setZero();
|
||||||
@@ -317,14 +317,12 @@ void EuclideanResectionAnsarDaniilidis(const Mat2X &x_camera,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Ensure positiveness of the largest value corresponding to lambda_ii.
|
// Ensure positiveness of the largest value corresponding to lambda_ii.
|
||||||
L_sq = L_sq * Sign(L_sq(IJToIndex(max_L_sq_index,
|
L_sq =
|
||||||
max_L_sq_index,
|
L_sq * Sign(L_sq(IJToIndex(max_L_sq_index, max_L_sq_index, num_lambda)));
|
||||||
num_lambda)));
|
|
||||||
|
|
||||||
Vec L(num_lambda);
|
Vec L(num_lambda);
|
||||||
L(max_L_sq_index) = sqrt(L_sq(IJToIndex(max_L_sq_index,
|
L(max_L_sq_index) =
|
||||||
max_L_sq_index,
|
sqrt(L_sq(IJToIndex(max_L_sq_index, max_L_sq_index, num_lambda)));
|
||||||
num_lambda)));
|
|
||||||
|
|
||||||
for (int i = 0; i < num_lambda; ++i) {
|
for (int i = 0; i < num_lambda; ++i) {
|
||||||
if (i != max_L_sq_index) {
|
if (i != max_L_sq_index) {
|
||||||
@@ -408,7 +406,8 @@ static void ComputePointsCoordinatesInCameraFrame(
|
|||||||
size_t num_points = alphas.cols();
|
size_t num_points = alphas.cols();
|
||||||
|
|
||||||
// Estimates the control points in the camera reference frame.
|
// Estimates the control points in the camera reference frame.
|
||||||
Mat34 C2b; C2b.setZero();
|
Mat34 C2b;
|
||||||
|
C2b.setZero();
|
||||||
for (size_t cu = 0; cu < 4; cu++) {
|
for (size_t cu = 0; cu < 4; cu++) {
|
||||||
for (size_t c = 0; c < 4; c++) {
|
for (size_t c = 0; c < 4; c++) {
|
||||||
C2b.col(c) += betas(cu) * U.block(11 - cu, c * 3, 1, 3).transpose();
|
C2b.col(c) += betas(cu) * U.block(11 - cu, c * 3, 1, 3).transpose();
|
||||||
@@ -438,7 +437,8 @@ static void ComputePointsCoordinatesInCameraFrame(
|
|||||||
|
|
||||||
bool EuclideanResectionEPnP(const Mat2X& x_camera,
|
bool EuclideanResectionEPnP(const Mat2X& x_camera,
|
||||||
const Mat3X& X_world,
|
const Mat3X& X_world,
|
||||||
Mat3 *R, Vec3 *t) {
|
Mat3* R,
|
||||||
|
Vec3* t) {
|
||||||
CHECK(x_camera.cols() == X_world.cols());
|
CHECK(x_camera.cols() == X_world.cols());
|
||||||
CHECK(x_camera.cols() > 3);
|
CHECK(x_camera.cols() > 3);
|
||||||
size_t num_points = X_world.cols();
|
size_t num_points = X_world.cols();
|
||||||
@@ -462,6 +462,7 @@ bool EuclideanResectionEPnP(const Mat2X &x_camera,
|
|||||||
double a3 = alphas(3, c);
|
double a3 = alphas(3, c);
|
||||||
double ui = x_camera(0, c);
|
double ui = x_camera(0, c);
|
||||||
double vi = x_camera(1, c);
|
double vi = x_camera(1, c);
|
||||||
|
// clang-format off
|
||||||
M.block(2*c, 0, 2, 12) << a0, 0,
|
M.block(2*c, 0, 2, 12) << a0, 0,
|
||||||
a0*(-ui), a1, 0,
|
a0*(-ui), a1, 0,
|
||||||
a1*(-ui), a2, 0,
|
a1*(-ui), a2, 0,
|
||||||
@@ -471,6 +472,7 @@ bool EuclideanResectionEPnP(const Mat2X &x_camera,
|
|||||||
a1, a1*(-vi), 0,
|
a1, a1*(-vi), 0,
|
||||||
a2, a2*(-vi), 0,
|
a2, a2*(-vi), 0,
|
||||||
a3, a3*(-vi);
|
a3, a3*(-vi);
|
||||||
|
// clang-format on
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO(julien): Avoid the transpose by rewriting the u2.block() calls.
|
// TODO(julien): Avoid the transpose by rewriting the u2.block() calls.
|
||||||
@@ -510,6 +512,7 @@ bool EuclideanResectionEPnP(const Mat2X &x_camera,
|
|||||||
|
|
||||||
Eigen::Matrix<double, 6, 10> L;
|
Eigen::Matrix<double, 6, 10> L;
|
||||||
for (size_t r = 0; r < 6; r++) {
|
for (size_t r = 0; r < 6; r++) {
|
||||||
|
// clang-format off
|
||||||
L.row(r) << dv1.row(r).dot(dv1.row(r)),
|
L.row(r) << dv1.row(r).dot(dv1.row(r)),
|
||||||
2.0 * dv1.row(r).dot(dv2.row(r)),
|
2.0 * dv1.row(r).dot(dv2.row(r)),
|
||||||
dv2.row(r).dot(dv2.row(r)),
|
dv2.row(r).dot(dv2.row(r)),
|
||||||
@@ -520,19 +523,23 @@ bool EuclideanResectionEPnP(const Mat2X &x_camera,
|
|||||||
2.0 * dv2.row(r).dot(dv4.row(r)),
|
2.0 * dv2.row(r).dot(dv4.row(r)),
|
||||||
2.0 * dv3.row(r).dot(dv4.row(r)),
|
2.0 * dv3.row(r).dot(dv4.row(r)),
|
||||||
dv4.row(r).dot(dv4.row(r));
|
dv4.row(r).dot(dv4.row(r));
|
||||||
|
// clang-format on
|
||||||
}
|
}
|
||||||
Vec6 rho;
|
Vec6 rho;
|
||||||
|
// clang-format off
|
||||||
rho << (X_control_points.col(0) - X_control_points.col(1)).squaredNorm(),
|
rho << (X_control_points.col(0) - X_control_points.col(1)).squaredNorm(),
|
||||||
(X_control_points.col(0) - X_control_points.col(2)).squaredNorm(),
|
(X_control_points.col(0) - X_control_points.col(2)).squaredNorm(),
|
||||||
(X_control_points.col(0) - X_control_points.col(3)).squaredNorm(),
|
(X_control_points.col(0) - X_control_points.col(3)).squaredNorm(),
|
||||||
(X_control_points.col(1) - X_control_points.col(2)).squaredNorm(),
|
(X_control_points.col(1) - X_control_points.col(2)).squaredNorm(),
|
||||||
(X_control_points.col(1) - X_control_points.col(3)).squaredNorm(),
|
(X_control_points.col(1) - X_control_points.col(3)).squaredNorm(),
|
||||||
(X_control_points.col(2) - X_control_points.col(3)).squaredNorm();
|
(X_control_points.col(2) - X_control_points.col(3)).squaredNorm();
|
||||||
|
// clang-format on
|
||||||
|
|
||||||
// There are three possible solutions based on the three approximations of L
|
// There are three possible solutions based on the three approximations of L
|
||||||
// (betas). Below, each one is solved for then the best one is chosen.
|
// (betas). Below, each one is solved for then the best one is chosen.
|
||||||
Mat3X X_camera;
|
Mat3X X_camera;
|
||||||
Mat3 K; K.setIdentity();
|
Mat3 K;
|
||||||
|
K.setIdentity();
|
||||||
vector<Mat3> Rs(3);
|
vector<Mat3> Rs(3);
|
||||||
vector<Vec3> ts(3);
|
vector<Vec3> ts(3);
|
||||||
Vec rmse(3);
|
Vec rmse(3);
|
||||||
@@ -715,7 +722,8 @@ bool EuclideanResectionEPnP(const Mat2X &x_camera,
|
|||||||
// other hand, it did work on the first try.
|
// other hand, it did work on the first try.
|
||||||
bool EuclideanResectionPPnP(const Mat2X& x_camera,
|
bool EuclideanResectionPPnP(const Mat2X& x_camera,
|
||||||
const Mat3X& X_world,
|
const Mat3X& X_world,
|
||||||
Mat3 *R, Vec3 *t) {
|
Mat3* R,
|
||||||
|
Vec3* t) {
|
||||||
int n = x_camera.cols();
|
int n = x_camera.cols();
|
||||||
Mat Z = Mat::Zero(n, n);
|
Mat Z = Mat::Zero(n, n);
|
||||||
Vec e = Vec::Ones(n);
|
Vec e = Vec::Ones(n);
|
||||||
@@ -750,7 +758,8 @@ bool EuclideanResectionPPnP(const Mat2X &x_camera,
|
|||||||
Mat PR = P * *R; // n x 3
|
Mat PR = P * *R; // n x 3
|
||||||
c = (S - Z * PR).transpose() * II;
|
c = (S - Z * PR).transpose() * II;
|
||||||
Mat Y = S - e * c.transpose(); // n x 3
|
Mat Y = S - e * c.transpose(); // n x 3
|
||||||
Vec Zmindiag = (PR * Y.transpose()).diagonal()
|
Vec Zmindiag = (PR * Y.transpose())
|
||||||
|
.diagonal()
|
||||||
.cwiseQuotient(P.rowwise().squaredNorm());
|
.cwiseQuotient(P.rowwise().squaredNorm());
|
||||||
for (int i = 0; i < n; ++i) {
|
for (int i = 0; i < n; ++i) {
|
||||||
Zmindiag[i] = std::max(Zmindiag[i], 0.0);
|
Zmindiag[i] = std::max(Zmindiag[i], 0.0);
|
||||||
@@ -769,6 +778,5 @@ bool EuclideanResectionPPnP(const Mat2X &x_camera,
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
} // namespace euclidean_resection
|
||||||
} // namespace resection
|
|
||||||
} // namespace libmv
|
} // namespace libmv
|
||||||
|
@@ -21,8 +21,8 @@
|
|||||||
#ifndef LIBMV_MULTIVIEW_EUCLIDEAN_RESECTION_H_
|
#ifndef LIBMV_MULTIVIEW_EUCLIDEAN_RESECTION_H_
|
||||||
#define LIBMV_MULTIVIEW_EUCLIDEAN_RESECTION_H_
|
#define LIBMV_MULTIVIEW_EUCLIDEAN_RESECTION_H_
|
||||||
|
|
||||||
#include "libmv/numeric/numeric.h"
|
|
||||||
#include "libmv/multiview/projection.h"
|
#include "libmv/multiview/projection.h"
|
||||||
|
#include "libmv/numeric/numeric.h"
|
||||||
|
|
||||||
namespace libmv {
|
namespace libmv {
|
||||||
namespace euclidean_resection {
|
namespace euclidean_resection {
|
||||||
@@ -52,7 +52,8 @@ enum ResectionMethod {
|
|||||||
*/
|
*/
|
||||||
bool EuclideanResection(const Mat2X& x_camera,
|
bool EuclideanResection(const Mat2X& x_camera,
|
||||||
const Mat3X& X_world,
|
const Mat3X& X_world,
|
||||||
Mat3 *R, Vec3 *t,
|
Mat3* R,
|
||||||
|
Vec3* t,
|
||||||
ResectionMethod method = RESECTION_EPNP);
|
ResectionMethod method = RESECTION_EPNP);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -71,7 +72,8 @@ bool EuclideanResection(const Mat2X &x_camera,
|
|||||||
bool EuclideanResection(const Mat& x_image,
|
bool EuclideanResection(const Mat& x_image,
|
||||||
const Mat3X& X_world,
|
const Mat3X& X_world,
|
||||||
const Mat3& K,
|
const Mat3& K,
|
||||||
Mat3 *R, Vec3 *t,
|
Mat3* R,
|
||||||
|
Vec3* t,
|
||||||
ResectionMethod method = RESECTION_EPNP);
|
ResectionMethod method = RESECTION_EPNP);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -84,10 +86,7 @@ bool EuclideanResection(const Mat &x_image,
|
|||||||
* Horn, Hilden, "Closed-form solution of absolute orientation using
|
* Horn, Hilden, "Closed-form solution of absolute orientation using
|
||||||
* orthonormal matrices"
|
* orthonormal matrices"
|
||||||
*/
|
*/
|
||||||
void AbsoluteOrientation(const Mat3X &X,
|
void AbsoluteOrientation(const Mat3X& X, const Mat3X& Xp, Mat3* R, Vec3* t);
|
||||||
const Mat3X &Xp,
|
|
||||||
Mat3 *R,
|
|
||||||
Vec3 *t);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Computes the extrinsic parameters, R and t for a calibrated camera from 4 or
|
* Computes the extrinsic parameters, R and t for a calibrated camera from 4 or
|
||||||
@@ -104,7 +103,8 @@ void AbsoluteOrientation(const Mat3X &X,
|
|||||||
*/
|
*/
|
||||||
void EuclideanResectionAnsarDaniilidis(const Mat2X& x_camera,
|
void EuclideanResectionAnsarDaniilidis(const Mat2X& x_camera,
|
||||||
const Mat3X& X_world,
|
const Mat3X& X_world,
|
||||||
Mat3 *R, Vec3 *t);
|
Mat3* R,
|
||||||
|
Vec3* t);
|
||||||
/**
|
/**
|
||||||
* Computes the extrinsic parameters, R and t for a calibrated camera from 4 or
|
* Computes the extrinsic parameters, R and t for a calibrated camera from 4 or
|
||||||
* more 3D points and their images.
|
* more 3D points and their images.
|
||||||
@@ -122,7 +122,8 @@ void EuclideanResectionAnsarDaniilidis(const Mat2X &x_camera,
|
|||||||
*/
|
*/
|
||||||
bool EuclideanResectionEPnP(const Mat2X& x_camera,
|
bool EuclideanResectionEPnP(const Mat2X& x_camera,
|
||||||
const Mat3X& X_world,
|
const Mat3X& X_world,
|
||||||
Mat3 *R, Vec3 *t);
|
Mat3* R,
|
||||||
|
Vec3* t);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Computes the extrinsic parameters, R and t for a calibrated camera from 4 or
|
* Computes the extrinsic parameters, R and t for a calibrated camera from 4 or
|
||||||
@@ -139,10 +140,10 @@ bool EuclideanResectionEPnP(const Mat2X &x_camera,
|
|||||||
*/
|
*/
|
||||||
bool EuclideanResectionPPnP(const Mat2X& x_camera,
|
bool EuclideanResectionPPnP(const Mat2X& x_camera,
|
||||||
const Mat3X& X_world,
|
const Mat3X& X_world,
|
||||||
Mat3 *R, Vec3 *t);
|
Mat3* R,
|
||||||
|
Vec3* t);
|
||||||
|
|
||||||
} // namespace euclidean_resection
|
} // namespace euclidean_resection
|
||||||
} // namespace libmv
|
} // namespace libmv
|
||||||
|
|
||||||
|
|
||||||
#endif /* LIBMV_MULTIVIEW_EUCLIDEAN_RESECTION_H_ */
|
#endif /* LIBMV_MULTIVIEW_EUCLIDEAN_RESECTION_H_ */
|
||||||
|
@@ -19,9 +19,9 @@
|
|||||||
// IN THE SOFTWARE.
|
// IN THE SOFTWARE.
|
||||||
|
|
||||||
#include "libmv/multiview/euclidean_resection.h"
|
#include "libmv/multiview/euclidean_resection.h"
|
||||||
#include "libmv/numeric/numeric.h"
|
|
||||||
#include "libmv/logging/logging.h"
|
#include "libmv/logging/logging.h"
|
||||||
#include "libmv/multiview/projection.h"
|
#include "libmv/multiview/projection.h"
|
||||||
|
#include "libmv/numeric/numeric.h"
|
||||||
#include "testing/testing.h"
|
#include "testing/testing.h"
|
||||||
|
|
||||||
using namespace libmv::euclidean_resection;
|
using namespace libmv::euclidean_resection;
|
||||||
@@ -76,9 +76,9 @@ TEST(AbsoluteOrientation, QuaternionSolution) {
|
|||||||
|
|
||||||
// Create a random translation and rotation.
|
// Create a random translation and rotation.
|
||||||
Mat3 R_input;
|
Mat3 R_input;
|
||||||
R_input = Eigen::AngleAxisd(rand(), Eigen::Vector3d::UnitZ())
|
R_input = Eigen::AngleAxisd(rand(), Eigen::Vector3d::UnitZ()) *
|
||||||
* Eigen::AngleAxisd(rand(), Eigen::Vector3d::UnitY())
|
Eigen::AngleAxisd(rand(), Eigen::Vector3d::UnitY()) *
|
||||||
* Eigen::AngleAxisd(rand(), Eigen::Vector3d::UnitZ());
|
Eigen::AngleAxisd(rand(), Eigen::Vector3d::UnitZ());
|
||||||
|
|
||||||
Vec3 t_input;
|
Vec3 t_input;
|
||||||
t_input.setRandom();
|
t_input.setRandom();
|
||||||
@@ -109,26 +109,29 @@ TEST(EuclideanResection, Points4KnownImagePointsRandomTranslationRotation) {
|
|||||||
image_dimensions << 1600, 1200;
|
image_dimensions << 1600, 1200;
|
||||||
|
|
||||||
Mat3 KK;
|
Mat3 KK;
|
||||||
|
// clang-format off
|
||||||
KK << 2796, 0, 804,
|
KK << 2796, 0, 804,
|
||||||
0 , 2796, 641,
|
0 , 2796, 641,
|
||||||
0, 0, 1;
|
0, 0, 1;
|
||||||
|
// clang-format on
|
||||||
|
|
||||||
// The real image points.
|
// The real image points.
|
||||||
int num_points = 4;
|
int num_points = 4;
|
||||||
Mat3X x_image(3, num_points);
|
Mat3X x_image(3, num_points);
|
||||||
|
// clang-format off
|
||||||
x_image << 1164.06, 734.948, 749.599, 430.727,
|
x_image << 1164.06, 734.948, 749.599, 430.727,
|
||||||
681.386, 844.59, 496.315, 580.775,
|
681.386, 844.59, 496.315, 580.775,
|
||||||
1, 1, 1, 1;
|
1, 1, 1, 1;
|
||||||
|
// clang-format on
|
||||||
|
|
||||||
// A vector of the 4 distances to the 3D points.
|
// A vector of the 4 distances to the 3D points.
|
||||||
Vec X_distances = 100 * Vec::Random(num_points).array().abs();
|
Vec X_distances = 100 * Vec::Random(num_points).array().abs();
|
||||||
|
|
||||||
// Create the random camera motion R and t that resection should recover.
|
// Create the random camera motion R and t that resection should recover.
|
||||||
Mat3 R_input;
|
Mat3 R_input;
|
||||||
R_input = Eigen::AngleAxisd(rand(), Eigen::Vector3d::UnitZ())
|
R_input = Eigen::AngleAxisd(rand(), Eigen::Vector3d::UnitZ()) *
|
||||||
* Eigen::AngleAxisd(rand(), Eigen::Vector3d::UnitY())
|
Eigen::AngleAxisd(rand(), Eigen::Vector3d::UnitY()) *
|
||||||
* Eigen::AngleAxisd(rand(), Eigen::Vector3d::UnitZ());
|
Eigen::AngleAxisd(rand(), Eigen::Vector3d::UnitZ());
|
||||||
|
|
||||||
Vec3 T_input;
|
Vec3 T_input;
|
||||||
T_input.setRandom();
|
T_input.setRandom();
|
||||||
@@ -140,15 +143,21 @@ TEST(EuclideanResection, Points4KnownImagePointsRandomTranslationRotation) {
|
|||||||
Vec3 T_expected;
|
Vec3 T_expected;
|
||||||
Mat3X X_world;
|
Mat3X X_world;
|
||||||
Mat2X x_camera;
|
Mat2X x_camera;
|
||||||
CreateCameraSystem(KK, x_image, X_distances, R_input, T_input,
|
CreateCameraSystem(KK,
|
||||||
&x_camera, &X_world, &R_expected, &T_expected);
|
x_image,
|
||||||
|
X_distances,
|
||||||
|
R_input,
|
||||||
|
T_input,
|
||||||
|
&x_camera,
|
||||||
|
&X_world,
|
||||||
|
&R_expected,
|
||||||
|
&T_expected);
|
||||||
|
|
||||||
// Finally, run the code under test.
|
// Finally, run the code under test.
|
||||||
Mat3 R_output;
|
Mat3 R_output;
|
||||||
Vec3 T_output;
|
Vec3 T_output;
|
||||||
EuclideanResection(x_camera, X_world,
|
EuclideanResection(
|
||||||
&R_output, &T_output,
|
x_camera, X_world, &R_output, &T_output, RESECTION_ANSAR_DANIILIDIS);
|
||||||
RESECTION_ANSAR_DANIILIDIS);
|
|
||||||
|
|
||||||
EXPECT_MATRIX_NEAR(T_output, T_expected, 1e-5);
|
EXPECT_MATRIX_NEAR(T_output, T_expected, 1e-5);
|
||||||
EXPECT_MATRIX_NEAR(R_output, R_expected, 1e-7);
|
EXPECT_MATRIX_NEAR(R_output, R_expected, 1e-7);
|
||||||
@@ -173,9 +182,11 @@ TEST(EuclideanResection, Points4KnownImagePointsRandomTranslationRotation) {
|
|||||||
// TODO(jmichot): Reduce the code duplication here with the code above.
|
// TODO(jmichot): Reduce the code duplication here with the code above.
|
||||||
TEST(EuclideanResection, Points6AllRandomInput) {
|
TEST(EuclideanResection, Points6AllRandomInput) {
|
||||||
Mat3 KK;
|
Mat3 KK;
|
||||||
|
// clang-format off
|
||||||
KK << 2796, 0, 804,
|
KK << 2796, 0, 804,
|
||||||
0 , 2796, 641,
|
0 , 2796, 641,
|
||||||
0, 0, 1;
|
0, 0, 1;
|
||||||
|
// clang-format on
|
||||||
|
|
||||||
// Create random image points for a 1600x1200 image.
|
// Create random image points for a 1600x1200 image.
|
||||||
int w = 1600;
|
int w = 1600;
|
||||||
@@ -192,9 +203,9 @@ TEST(EuclideanResection, Points6AllRandomInput) {
|
|||||||
|
|
||||||
// Create the random camera motion R and t that resection should recover.
|
// Create the random camera motion R and t that resection should recover.
|
||||||
Mat3 R_input;
|
Mat3 R_input;
|
||||||
R_input = Eigen::AngleAxisd(rand(), Eigen::Vector3d::UnitZ())
|
R_input = Eigen::AngleAxisd(rand(), Eigen::Vector3d::UnitZ()) *
|
||||||
* Eigen::AngleAxisd(rand(), Eigen::Vector3d::UnitY())
|
Eigen::AngleAxisd(rand(), Eigen::Vector3d::UnitY()) *
|
||||||
* Eigen::AngleAxisd(rand(), Eigen::Vector3d::UnitZ());
|
Eigen::AngleAxisd(rand(), Eigen::Vector3d::UnitZ());
|
||||||
|
|
||||||
Vec3 T_input;
|
Vec3 T_input;
|
||||||
T_input.setRandom();
|
T_input.setRandom();
|
||||||
@@ -204,33 +215,36 @@ TEST(EuclideanResection, Points6AllRandomInput) {
|
|||||||
Mat3 R_expected;
|
Mat3 R_expected;
|
||||||
Vec3 T_expected;
|
Vec3 T_expected;
|
||||||
Mat3X X_world;
|
Mat3X X_world;
|
||||||
CreateCameraSystem(KK, x_image, X_distances, R_input, T_input,
|
CreateCameraSystem(KK,
|
||||||
&x_camera, &X_world, &R_expected, &T_expected);
|
x_image,
|
||||||
|
X_distances,
|
||||||
|
R_input,
|
||||||
|
T_input,
|
||||||
|
&x_camera,
|
||||||
|
&X_world,
|
||||||
|
&R_expected,
|
||||||
|
&T_expected);
|
||||||
|
|
||||||
// Test each of the resection methods.
|
// Test each of the resection methods.
|
||||||
{
|
{
|
||||||
Mat3 R_output;
|
Mat3 R_output;
|
||||||
Vec3 T_output;
|
Vec3 T_output;
|
||||||
EuclideanResection(x_camera, X_world,
|
EuclideanResection(
|
||||||
&R_output, &T_output,
|
x_camera, X_world, &R_output, &T_output, RESECTION_ANSAR_DANIILIDIS);
|
||||||
RESECTION_ANSAR_DANIILIDIS);
|
|
||||||
EXPECT_MATRIX_NEAR(T_output, T_expected, 1e-5);
|
EXPECT_MATRIX_NEAR(T_output, T_expected, 1e-5);
|
||||||
EXPECT_MATRIX_NEAR(R_output, R_expected, 1e-7);
|
EXPECT_MATRIX_NEAR(R_output, R_expected, 1e-7);
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
Mat3 R_output;
|
Mat3 R_output;
|
||||||
Vec3 T_output;
|
Vec3 T_output;
|
||||||
EuclideanResection(x_camera, X_world,
|
EuclideanResection(x_camera, X_world, &R_output, &T_output, RESECTION_EPNP);
|
||||||
&R_output, &T_output,
|
|
||||||
RESECTION_EPNP);
|
|
||||||
EXPECT_MATRIX_NEAR(T_output, T_expected, 1e-5);
|
EXPECT_MATRIX_NEAR(T_output, T_expected, 1e-5);
|
||||||
EXPECT_MATRIX_NEAR(R_output, R_expected, 1e-7);
|
EXPECT_MATRIX_NEAR(R_output, R_expected, 1e-7);
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
Mat3 R_output;
|
Mat3 R_output;
|
||||||
Vec3 T_output;
|
Vec3 T_output;
|
||||||
EuclideanResection(x_image, X_world, KK,
|
EuclideanResection(x_image, X_world, KK, &R_output, &T_output);
|
||||||
&R_output, &T_output);
|
|
||||||
EXPECT_MATRIX_NEAR(T_output, T_expected, 1e-5);
|
EXPECT_MATRIX_NEAR(T_output, T_expected, 1e-5);
|
||||||
EXPECT_MATRIX_NEAR(R_output, R_expected, 1e-7);
|
EXPECT_MATRIX_NEAR(R_output, R_expected, 1e-7);
|
||||||
}
|
}
|
||||||
|
@@ -22,11 +22,11 @@
|
|||||||
|
|
||||||
#include "ceres/ceres.h"
|
#include "ceres/ceres.h"
|
||||||
#include "libmv/logging/logging.h"
|
#include "libmv/logging/logging.h"
|
||||||
#include "libmv/numeric/numeric.h"
|
|
||||||
#include "libmv/numeric/poly.h"
|
|
||||||
#include "libmv/multiview/conditioning.h"
|
#include "libmv/multiview/conditioning.h"
|
||||||
#include "libmv/multiview/projection.h"
|
#include "libmv/multiview/projection.h"
|
||||||
#include "libmv/multiview/triangulation.h"
|
#include "libmv/multiview/triangulation.h"
|
||||||
|
#include "libmv/numeric/numeric.h"
|
||||||
|
#include "libmv/numeric/poly.h"
|
||||||
|
|
||||||
namespace libmv {
|
namespace libmv {
|
||||||
|
|
||||||
@@ -106,9 +106,7 @@ void EnforceFundamentalRank2Constraint(Mat3 *F) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// HZ 11.2 pag.281 (x1 = x, x2 = x')
|
// HZ 11.2 pag.281 (x1 = x, x2 = x')
|
||||||
double NormalizedEightPointSolver(const Mat &x1,
|
double NormalizedEightPointSolver(const Mat& x1, const Mat& x2, Mat3* F) {
|
||||||
const Mat &x2,
|
|
||||||
Mat3 *F) {
|
|
||||||
DCHECK_EQ(x1.rows(), 2);
|
DCHECK_EQ(x1.rows(), 2);
|
||||||
DCHECK_GE(x1.cols(), 8);
|
DCHECK_GE(x1.cols(), 8);
|
||||||
DCHECK_EQ(x1.rows(), x2.rows());
|
DCHECK_EQ(x1.rows(), x2.rows());
|
||||||
@@ -169,24 +167,28 @@ double FundamentalFrom7CorrespondencesLinear(const Mat &x1,
|
|||||||
|
|
||||||
// Then, use the condition det(F) = 0 to determine F. In other words, solve
|
// Then, use the condition det(F) = 0 to determine F. In other words, solve
|
||||||
// det(F1 + a*F2) = 0 for a.
|
// det(F1 + a*F2) = 0 for a.
|
||||||
double a = F1(0, 0), j = F2(0, 0),
|
double a = F1(0, 0), j = F2(0, 0);
|
||||||
b = F1(0, 1), k = F2(0, 1),
|
double b = F1(0, 1), k = F2(0, 1);
|
||||||
c = F1(0, 2), l = F2(0, 2),
|
double c = F1(0, 2), l = F2(0, 2);
|
||||||
d = F1(1, 0), m = F2(1, 0),
|
double d = F1(1, 0), m = F2(1, 0);
|
||||||
e = F1(1, 1), n = F2(1, 1),
|
double e = F1(1, 1), n = F2(1, 1);
|
||||||
f = F1(1, 2), o = F2(1, 2),
|
double f = F1(1, 2), o = F2(1, 2);
|
||||||
g = F1(2, 0), p = F2(2, 0),
|
double g = F1(2, 0), p = F2(2, 0);
|
||||||
h = F1(2, 1), q = F2(2, 1),
|
double h = F1(2, 1), q = F2(2, 1);
|
||||||
i = F1(2, 2), r = F2(2, 2);
|
double i = F1(2, 2), r = F2(2, 2);
|
||||||
|
|
||||||
// Run fundamental_7point_coeffs.py to get the below coefficients.
|
// Run fundamental_7point_coeffs.py to get the below coefficients.
|
||||||
// The coefficients are in ascending powers of alpha, i.e. P[N]*x^N.
|
// The coefficients are in ascending powers of alpha, i.e. P[N]*x^N.
|
||||||
double P[4] = {
|
double P[4] = {
|
||||||
a * e * i + b * f * g + c * d * h - a * f * h - b * d * i - c * e * g,
|
a * e * i + b * f * g + c * d * h - a * f * h - b * d * i - c * e * g,
|
||||||
a*e*r + a*i*n + b*f*p + b*g*o + c*d*q + c*h*m + d*h*l + e*i*j + f*g*k -
|
a * e * r + a * i * n + b * f * p + b * g * o + c * d * q + c * h * m +
|
||||||
a*f*q - a*h*o - b*d*r - b*i*m - c*e*p - c*g*n - d*i*k - e*g*l - f*h*j,
|
d * h * l + e * i * j + f * g * k - a * f * q - a * h * o -
|
||||||
a*n*r + b*o*p + c*m*q + d*l*q + e*j*r + f*k*p + g*k*o + h*l*m + i*j*n -
|
b * d * r - b * i * m - c * e * p - c * g * n - d * i * k -
|
||||||
a*o*q - b*m*r - c*n*p - d*k*r - e*l*p - f*j*q - g*l*n - h*j*o - i*k*m,
|
e * g * l - f * h * j,
|
||||||
|
a * n * r + b * o * p + c * m * q + d * l * q + e * j * r + f * k * p +
|
||||||
|
g * k * o + h * l * m + i * j * n - a * o * q - b * m * r -
|
||||||
|
c * n * p - d * k * r - e * l * p - f * j * q - g * l * n -
|
||||||
|
h * j * o - i * k * m,
|
||||||
j * n * r + k * o * p + l * m * q - j * o * q - k * m * r - l * n * p,
|
j * n * r + k * o * p + l * m * q - j * o * q - k * m * r - l * n * p,
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -218,8 +220,8 @@ double FundamentalFromCorrespondences7Point(const Mat &x1,
|
|||||||
ApplyTransformationToPoints(x2, T2, &x2_normalized);
|
ApplyTransformationToPoints(x2, T2, &x2_normalized);
|
||||||
|
|
||||||
// Estimate the fundamental matrix.
|
// Estimate the fundamental matrix.
|
||||||
double smaller_singular_value =
|
double smaller_singular_value = FundamentalFrom7CorrespondencesLinear(
|
||||||
FundamentalFrom7CorrespondencesLinear(x1_normalized, x2_normalized, &(*F));
|
x1_normalized, x2_normalized, &(*F));
|
||||||
|
|
||||||
for (int k = 0; k < F->size(); ++k) {
|
for (int k = 0; k < F->size(); ++k) {
|
||||||
Mat3& Fmat = (*F)[k];
|
Mat3& Fmat = (*F)[k];
|
||||||
@@ -244,8 +246,8 @@ double SampsonDistance(const Mat &F, const Vec2 &x1, const Vec2 &x2) {
|
|||||||
Vec3 Ft_y = F.transpose() * y;
|
Vec3 Ft_y = F.transpose() * y;
|
||||||
double y_F_x = y.dot(F_x);
|
double y_F_x = y.dot(F_x);
|
||||||
|
|
||||||
return Square(y_F_x) / ( F_x.head<2>().squaredNorm()
|
return Square(y_F_x) /
|
||||||
+ Ft_y.head<2>().squaredNorm());
|
(F_x.head<2>().squaredNorm() + Ft_y.head<2>().squaredNorm());
|
||||||
}
|
}
|
||||||
|
|
||||||
double SymmetricEpipolarDistance(const Mat& F, const Vec2& x1, const Vec2& x2) {
|
double SymmetricEpipolarDistance(const Mat& F, const Vec2& x1, const Vec2& x2) {
|
||||||
@@ -256,8 +258,8 @@ double SymmetricEpipolarDistance(const Mat &F, const Vec2 &x1, const Vec2 &x2) {
|
|||||||
Vec3 Ft_y = F.transpose() * y;
|
Vec3 Ft_y = F.transpose() * y;
|
||||||
double y_F_x = y.dot(F_x);
|
double y_F_x = y.dot(F_x);
|
||||||
|
|
||||||
return Square(y_F_x) * ( 1 / F_x.head<2>().squaredNorm()
|
return Square(y_F_x) *
|
||||||
+ 1 / Ft_y.head<2>().squaredNorm());
|
(1 / F_x.head<2>().squaredNorm() + 1 / Ft_y.head<2>().squaredNorm());
|
||||||
}
|
}
|
||||||
|
|
||||||
// HZ 9.6 pag 257 (formula 9.12)
|
// HZ 9.6 pag 257 (formula 9.12)
|
||||||
@@ -288,11 +290,8 @@ void RelativeCameraMotion(const Mat3 &R1,
|
|||||||
}
|
}
|
||||||
|
|
||||||
// HZ 9.6 pag 257
|
// HZ 9.6 pag 257
|
||||||
void EssentialFromRt(const Mat3 &R1,
|
void EssentialFromRt(
|
||||||
const Vec3 &t1,
|
const Mat3& R1, const Vec3& t1, const Mat3& R2, const Vec3& t2, Mat3* E) {
|
||||||
const Mat3 &R2,
|
|
||||||
const Vec3 &t2,
|
|
||||||
Mat3 *E) {
|
|
||||||
Mat3 R;
|
Mat3 R;
|
||||||
Vec3 t;
|
Vec3 t;
|
||||||
RelativeCameraMotion(R1, t1, R2, t2, &R, &t);
|
RelativeCameraMotion(R1, t1, R2, t2, &R, &t);
|
||||||
@@ -318,9 +317,11 @@ void MotionFromEssential(const Mat3 &E,
|
|||||||
}
|
}
|
||||||
|
|
||||||
Mat3 W;
|
Mat3 W;
|
||||||
|
// clang-format off
|
||||||
W << 0, -1, 0,
|
W << 0, -1, 0,
|
||||||
1, 0, 0,
|
1, 0, 0,
|
||||||
0, 0, 1;
|
0, 0, 1;
|
||||||
|
// clang-format on
|
||||||
|
|
||||||
Mat3 U_W_Vt = U * W * Vt;
|
Mat3 U_W_Vt = U * W * Vt;
|
||||||
Mat3 U_Wt_Vt = U * W.transpose() * Vt;
|
Mat3 U_Wt_Vt = U * W.transpose() * Vt;
|
||||||
@@ -399,8 +400,8 @@ void FundamentalToEssential(const Mat3 &F, Mat3 *E) {
|
|||||||
double s = (a + b) / 2.0;
|
double s = (a + b) / 2.0;
|
||||||
|
|
||||||
LG << "Initial reconstruction's rotation is non-euclidean by "
|
LG << "Initial reconstruction's rotation is non-euclidean by "
|
||||||
<< (((a - b) / std::max(a, b)) * 100) << "%; singular values:"
|
<< (((a - b) / std::max(a, b)) * 100)
|
||||||
<< svd.singularValues().transpose();
|
<< "%; singular values:" << svd.singularValues().transpose();
|
||||||
|
|
||||||
Vec3 diag;
|
Vec3 diag;
|
||||||
diag << s, s, 0;
|
diag << s, s, 0;
|
||||||
@@ -410,9 +411,8 @@ void FundamentalToEssential(const Mat3 &F, Mat3 *E) {
|
|||||||
|
|
||||||
// Default settings for fundamental estimation which should be suitable
|
// Default settings for fundamental estimation which should be suitable
|
||||||
// for a wide range of use cases.
|
// for a wide range of use cases.
|
||||||
EstimateFundamentalOptions::EstimateFundamentalOptions(void) :
|
EstimateFundamentalOptions::EstimateFundamentalOptions(void)
|
||||||
max_num_iterations(50),
|
: max_num_iterations(50), expected_average_symmetric_distance(1e-16) {
|
||||||
expected_average_symmetric_distance(1e-16) {
|
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
@@ -420,8 +420,7 @@ namespace {
|
|||||||
// used for fundamental matrix refinement.
|
// used for fundamental matrix refinement.
|
||||||
class FundamentalSymmetricEpipolarCostFunctor {
|
class FundamentalSymmetricEpipolarCostFunctor {
|
||||||
public:
|
public:
|
||||||
FundamentalSymmetricEpipolarCostFunctor(const Vec2 &x,
|
FundamentalSymmetricEpipolarCostFunctor(const Vec2& x, const Vec2& y)
|
||||||
const Vec2 &y)
|
|
||||||
: x_(x), y_(y) {}
|
: x_(x), y_(y) {}
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
@@ -454,7 +453,8 @@ class FundamentalSymmetricEpipolarCostFunctor {
|
|||||||
// average value.
|
// average value.
|
||||||
class TerminationCheckingCallback : public ceres::IterationCallback {
|
class TerminationCheckingCallback : public ceres::IterationCallback {
|
||||||
public:
|
public:
|
||||||
TerminationCheckingCallback(const Mat &x1, const Mat &x2,
|
TerminationCheckingCallback(const Mat& x1,
|
||||||
|
const Mat& x2,
|
||||||
const EstimateFundamentalOptions& options,
|
const EstimateFundamentalOptions& options,
|
||||||
Mat3* F)
|
Mat3* F)
|
||||||
: options_(options), x1_(x1), x2_(x2), F_(F) {}
|
: options_(options), x1_(x1), x2_(x2), F_(F) {}
|
||||||
@@ -469,9 +469,7 @@ class TerminationCheckingCallback : public ceres::IterationCallback {
|
|||||||
// Calculate average of symmetric epipolar distance.
|
// Calculate average of symmetric epipolar distance.
|
||||||
double average_distance = 0.0;
|
double average_distance = 0.0;
|
||||||
for (int i = 0; i < x1_.cols(); i++) {
|
for (int i = 0; i < x1_.cols(); i++) {
|
||||||
average_distance = SymmetricEpipolarDistance(*F_,
|
average_distance = SymmetricEpipolarDistance(*F_, x1_.col(i), x2_.col(i));
|
||||||
x1_.col(i),
|
|
||||||
x2_.col(i));
|
|
||||||
}
|
}
|
||||||
average_distance /= x1_.cols();
|
average_distance /= x1_.cols();
|
||||||
|
|
||||||
@@ -506,16 +504,15 @@ bool EstimateFundamentalFromCorrespondences(
|
|||||||
// Step 2: Refine matrix using Ceres minimizer.
|
// Step 2: Refine matrix using Ceres minimizer.
|
||||||
ceres::Problem problem;
|
ceres::Problem problem;
|
||||||
for (int i = 0; i < x1.cols(); i++) {
|
for (int i = 0; i < x1.cols(); i++) {
|
||||||
FundamentalSymmetricEpipolarCostFunctor
|
FundamentalSymmetricEpipolarCostFunctor*
|
||||||
*fundamental_symmetric_epipolar_cost_function =
|
fundamental_symmetric_epipolar_cost_function =
|
||||||
new FundamentalSymmetricEpipolarCostFunctor(x1.col(i),
|
new FundamentalSymmetricEpipolarCostFunctor(x1.col(i), x2.col(i));
|
||||||
x2.col(i));
|
|
||||||
|
|
||||||
problem.AddResidualBlock(
|
problem.AddResidualBlock(
|
||||||
new ceres::AutoDiffCostFunction<
|
new ceres::AutoDiffCostFunction<FundamentalSymmetricEpipolarCostFunctor,
|
||||||
FundamentalSymmetricEpipolarCostFunctor,
|
|
||||||
2, // num_residuals
|
2, // num_residuals
|
||||||
9>(fundamental_symmetric_epipolar_cost_function),
|
9>(
|
||||||
|
fundamental_symmetric_epipolar_cost_function),
|
||||||
NULL,
|
NULL,
|
||||||
F->data());
|
F->data());
|
||||||
}
|
}
|
||||||
|
@@ -47,9 +47,7 @@ double FundamentalFromCorrespondences7Point(const Mat &x1,
|
|||||||
/**
|
/**
|
||||||
* 8 points (points coordinates must be in image space):
|
* 8 points (points coordinates must be in image space):
|
||||||
*/
|
*/
|
||||||
double NormalizedEightPointSolver(const Mat &x1,
|
double NormalizedEightPointSolver(const Mat& x1, const Mat& x2, Mat3* F);
|
||||||
const Mat &x2,
|
|
||||||
Mat3 *F);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Fundamental matrix utility function:
|
* Fundamental matrix utility function:
|
||||||
@@ -98,11 +96,8 @@ void FundamentalFromEssential(const Mat3 &E,
|
|||||||
const Mat3& K2,
|
const Mat3& K2,
|
||||||
Mat3* F);
|
Mat3* F);
|
||||||
|
|
||||||
void EssentialFromRt(const Mat3 &R1,
|
void EssentialFromRt(
|
||||||
const Vec3 &t1,
|
const Mat3& R1, const Vec3& t1, const Mat3& R2, const Vec3& t2, Mat3* E);
|
||||||
const Mat3 &R2,
|
|
||||||
const Vec3 &t2,
|
|
||||||
Mat3 *E);
|
|
||||||
|
|
||||||
void MotionFromEssential(const Mat3& E,
|
void MotionFromEssential(const Mat3& E,
|
||||||
std::vector<Mat3>* Rs,
|
std::vector<Mat3>* Rs,
|
||||||
|
@@ -34,12 +34,14 @@ using namespace libmv;
|
|||||||
|
|
||||||
TEST(Fundamental, FundamentalFromProjections) {
|
TEST(Fundamental, FundamentalFromProjections) {
|
||||||
Mat34 P1_gt, P2_gt;
|
Mat34 P1_gt, P2_gt;
|
||||||
|
// clang-format off
|
||||||
P1_gt << 1, 0, 0, 0,
|
P1_gt << 1, 0, 0, 0,
|
||||||
0, 1, 0, 0,
|
0, 1, 0, 0,
|
||||||
0, 0, 1, 0;
|
0, 0, 1, 0;
|
||||||
P2_gt << 1, 1, 1, 3,
|
P2_gt << 1, 1, 1, 3,
|
||||||
0, 2, 0, 3,
|
0, 2, 0, 3,
|
||||||
0, 1, 1, 0;
|
0, 1, 1, 0;
|
||||||
|
// clang-format on
|
||||||
Mat3 F_gt;
|
Mat3 F_gt;
|
||||||
FundamentalFromProjections(P1_gt, P2_gt, &F_gt);
|
FundamentalFromProjections(P1_gt, P2_gt, &F_gt);
|
||||||
|
|
||||||
@@ -55,8 +57,10 @@ TEST(Fundamental, FundamentalFromProjections) {
|
|||||||
TEST(Fundamental, PreconditionerFromPoints) {
|
TEST(Fundamental, PreconditionerFromPoints) {
|
||||||
int n = 4;
|
int n = 4;
|
||||||
Mat points(2, n);
|
Mat points(2, n);
|
||||||
|
// clang-format off
|
||||||
points << 0, 0, 1, 1,
|
points << 0, 0, 1, 1,
|
||||||
0, 2, 1, 3;
|
0, 2, 1, 3;
|
||||||
|
// clang-format on
|
||||||
|
|
||||||
Mat3 T;
|
Mat3 T;
|
||||||
PreconditionerFromPoints(points, &T);
|
PreconditionerFromPoints(points, &T);
|
||||||
@@ -152,8 +156,8 @@ TEST(Fundamental, MotionFromEssentialAndCorrespondence) {
|
|||||||
|
|
||||||
Mat3 R_estimated;
|
Mat3 R_estimated;
|
||||||
Vec3 t_estimated;
|
Vec3 t_estimated;
|
||||||
MotionFromEssentialAndCorrespondence(E, d.K1, x1, d.K2, x2,
|
MotionFromEssentialAndCorrespondence(
|
||||||
&R_estimated, &t_estimated);
|
E, d.K1, x1, d.K2, x2, &R_estimated, &t_estimated);
|
||||||
|
|
||||||
EXPECT_LE(FrobeniusDistance(R_estimated, R), 1e-8);
|
EXPECT_LE(FrobeniusDistance(R_estimated, R), 1e-8);
|
||||||
EXPECT_LE(DistanceL2(t_estimated, t), 1e-8);
|
EXPECT_LE(DistanceL2(t_estimated, t), 1e-8);
|
||||||
|
@@ -44,10 +44,7 @@ namespace libmv {
|
|||||||
* (-x2*a+x1*d)*y1 + (-x2*b+x1*e)*y2 + -x2*c+x1*f |0|
|
* (-x2*a+x1*d)*y1 + (-x2*b+x1*e)*y2 + -x2*c+x1*f |0|
|
||||||
*/
|
*/
|
||||||
static bool Homography2DFromCorrespondencesLinearEuc(
|
static bool Homography2DFromCorrespondencesLinearEuc(
|
||||||
const Mat &x1,
|
const Mat& x1, const Mat& x2, Mat3* H, double expected_precision) {
|
||||||
const Mat &x2,
|
|
||||||
Mat3 *H,
|
|
||||||
double expected_precision) {
|
|
||||||
assert(2 == x1.rows());
|
assert(2 == x1.rows());
|
||||||
assert(4 <= x1.cols());
|
assert(4 <= x1.cols());
|
||||||
assert(x1.rows() == x2.rows());
|
assert(x1.rows() == x2.rows());
|
||||||
@@ -93,6 +90,7 @@ static bool Homography2DFromCorrespondencesLinearEuc(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// clang-format off
|
||||||
/** 2D Homography transformation estimation in the case that points are in
|
/** 2D Homography transformation estimation in the case that points are in
|
||||||
* homogeneous coordinates.
|
* homogeneous coordinates.
|
||||||
*
|
*
|
||||||
@@ -101,13 +99,14 @@ static bool Homography2DFromCorrespondencesLinearEuc(
|
|||||||
* |-x2 x1 0| |g h 1| |y3| -x2*a+x1*d -x2*b+x1*e -x2*c+x1*f |y3| (-x2*a+x1*d)*y1 (-x2*b+x1*e)*y2 (-x2*c+x1*f)*y3 |0|
|
* |-x2 x1 0| |g h 1| |y3| -x2*a+x1*d -x2*b+x1*e -x2*c+x1*f |y3| (-x2*a+x1*d)*y1 (-x2*b+x1*e)*y2 (-x2*c+x1*f)*y3 |0|
|
||||||
* X = |a b c d e f g h|^t
|
* X = |a b c d e f g h|^t
|
||||||
*/
|
*/
|
||||||
|
// clang-format on
|
||||||
bool Homography2DFromCorrespondencesLinear(const Mat& x1,
|
bool Homography2DFromCorrespondencesLinear(const Mat& x1,
|
||||||
const Mat& x2,
|
const Mat& x2,
|
||||||
Mat3* H,
|
Mat3* H,
|
||||||
double expected_precision) {
|
double expected_precision) {
|
||||||
if (x1.rows() == 2) {
|
if (x1.rows() == 2) {
|
||||||
return Homography2DFromCorrespondencesLinearEuc(x1, x2, H,
|
return Homography2DFromCorrespondencesLinearEuc(
|
||||||
expected_precision);
|
x1, x2, H, expected_precision);
|
||||||
}
|
}
|
||||||
assert(3 == x1.rows());
|
assert(3 == x1.rows());
|
||||||
assert(4 <= x1.cols());
|
assert(4 <= x1.cols());
|
||||||
@@ -158,8 +157,8 @@ bool Homography2DFromCorrespondencesLinear(const Mat &x1,
|
|||||||
|
|
||||||
// Default settings for homography estimation which should be suitable
|
// Default settings for homography estimation which should be suitable
|
||||||
// for a wide range of use cases.
|
// for a wide range of use cases.
|
||||||
EstimateHomographyOptions::EstimateHomographyOptions(void) :
|
EstimateHomographyOptions::EstimateHomographyOptions(void)
|
||||||
use_normalization(true),
|
: use_normalization(true),
|
||||||
max_num_iterations(50),
|
max_num_iterations(50),
|
||||||
expected_average_symmetric_distance(1e-16) {
|
expected_average_symmetric_distance(1e-16) {
|
||||||
}
|
}
|
||||||
@@ -169,17 +168,15 @@ void GetNormalizedPoints(const Mat &original_points,
|
|||||||
Mat* normalized_points,
|
Mat* normalized_points,
|
||||||
Mat3* normalization_matrix) {
|
Mat3* normalization_matrix) {
|
||||||
IsotropicPreconditionerFromPoints(original_points, normalization_matrix);
|
IsotropicPreconditionerFromPoints(original_points, normalization_matrix);
|
||||||
ApplyTransformationToPoints(original_points,
|
ApplyTransformationToPoints(
|
||||||
*normalization_matrix,
|
original_points, *normalization_matrix, normalized_points);
|
||||||
normalized_points);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Cost functor which computes symmetric geometric distance
|
// Cost functor which computes symmetric geometric distance
|
||||||
// used for homography matrix refinement.
|
// used for homography matrix refinement.
|
||||||
class HomographySymmetricGeometricCostFunctor {
|
class HomographySymmetricGeometricCostFunctor {
|
||||||
public:
|
public:
|
||||||
HomographySymmetricGeometricCostFunctor(const Vec2 &x,
|
HomographySymmetricGeometricCostFunctor(const Vec2& x, const Vec2& y)
|
||||||
const Vec2 &y)
|
|
||||||
: x_(x), y_(y) {}
|
: x_(x), y_(y) {}
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
@@ -221,7 +218,8 @@ class HomographySymmetricGeometricCostFunctor {
|
|||||||
// average value.
|
// average value.
|
||||||
class TerminationCheckingCallback : public ceres::IterationCallback {
|
class TerminationCheckingCallback : public ceres::IterationCallback {
|
||||||
public:
|
public:
|
||||||
TerminationCheckingCallback(const Mat &x1, const Mat &x2,
|
TerminationCheckingCallback(const Mat& x1,
|
||||||
|
const Mat& x2,
|
||||||
const EstimateHomographyOptions& options,
|
const EstimateHomographyOptions& options,
|
||||||
Mat3* H)
|
Mat3* H)
|
||||||
: options_(options), x1_(x1), x2_(x2), H_(H) {}
|
: options_(options), x1_(x1), x2_(x2), H_(H) {}
|
||||||
@@ -236,9 +234,8 @@ class TerminationCheckingCallback : public ceres::IterationCallback {
|
|||||||
// Calculate average of symmetric geometric distance.
|
// Calculate average of symmetric geometric distance.
|
||||||
double average_distance = 0.0;
|
double average_distance = 0.0;
|
||||||
for (int i = 0; i < x1_.cols(); i++) {
|
for (int i = 0; i < x1_.cols(); i++) {
|
||||||
average_distance = SymmetricGeometricDistance(*H_,
|
average_distance =
|
||||||
x1_.col(i),
|
SymmetricGeometricDistance(*H_, x1_.col(i), x2_.col(i));
|
||||||
x2_.col(i));
|
|
||||||
}
|
}
|
||||||
average_distance /= x1_.cols();
|
average_distance /= x1_.cols();
|
||||||
|
|
||||||
@@ -272,8 +269,7 @@ bool EstimateHomography2DFromCorrespondences(
|
|||||||
assert(x1.rows() == x2.rows());
|
assert(x1.rows() == x2.rows());
|
||||||
assert(x1.cols() == x2.cols());
|
assert(x1.cols() == x2.cols());
|
||||||
|
|
||||||
Mat3 T1 = Mat3::Identity(),
|
Mat3 T1 = Mat3::Identity(), T2 = Mat3::Identity();
|
||||||
T2 = Mat3::Identity();
|
|
||||||
|
|
||||||
// Step 1: Algebraic homography estimation.
|
// Step 1: Algebraic homography estimation.
|
||||||
Mat x1_normalized, x2_normalized;
|
Mat x1_normalized, x2_normalized;
|
||||||
@@ -300,16 +296,15 @@ bool EstimateHomography2DFromCorrespondences(
|
|||||||
// Step 2: Refine matrix using Ceres minimizer.
|
// Step 2: Refine matrix using Ceres minimizer.
|
||||||
ceres::Problem problem;
|
ceres::Problem problem;
|
||||||
for (int i = 0; i < x1.cols(); i++) {
|
for (int i = 0; i < x1.cols(); i++) {
|
||||||
HomographySymmetricGeometricCostFunctor
|
HomographySymmetricGeometricCostFunctor*
|
||||||
*homography_symmetric_geometric_cost_function =
|
homography_symmetric_geometric_cost_function =
|
||||||
new HomographySymmetricGeometricCostFunctor(x1.col(i),
|
new HomographySymmetricGeometricCostFunctor(x1.col(i), x2.col(i));
|
||||||
x2.col(i));
|
|
||||||
|
|
||||||
problem.AddResidualBlock(
|
problem.AddResidualBlock(
|
||||||
new ceres::AutoDiffCostFunction<
|
new ceres::AutoDiffCostFunction<HomographySymmetricGeometricCostFunctor,
|
||||||
HomographySymmetricGeometricCostFunctor,
|
|
||||||
4, // num_residuals
|
4, // num_residuals
|
||||||
9>(homography_symmetric_geometric_cost_function),
|
9>(
|
||||||
|
homography_symmetric_geometric_cost_function),
|
||||||
NULL,
|
NULL,
|
||||||
H->data());
|
H->data());
|
||||||
}
|
}
|
||||||
@@ -335,6 +330,7 @@ bool EstimateHomography2DFromCorrespondences(
|
|||||||
return summary.IsSolutionUsable();
|
return summary.IsSolutionUsable();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// clang-format off
|
||||||
/**
|
/**
|
||||||
* x2 ~ A * x1
|
* x2 ~ A * x1
|
||||||
* x2^t * Hi * A *x1 = 0
|
* x2^t * Hi * A *x1 = 0
|
||||||
@@ -362,6 +358,7 @@ bool EstimateHomography2DFromCorrespondences(
|
|||||||
*
|
*
|
||||||
* X = |a b c d e f g h i j k l m n o|^t
|
* X = |a b c d e f g h i j k l m n o|^t
|
||||||
*/
|
*/
|
||||||
|
// clang-format on
|
||||||
bool Homography3DFromCorrespondencesLinear(const Mat& x1,
|
bool Homography3DFromCorrespondencesLinear(const Mat& x1,
|
||||||
const Mat& x2,
|
const Mat& x2,
|
||||||
Mat4* H,
|
Mat4* H,
|
||||||
|
@@ -49,11 +49,11 @@ namespace libmv {
|
|||||||
* \return True if the transformation estimation has succeeded.
|
* \return True if the transformation estimation has succeeded.
|
||||||
* \note There must be at least 4 non-colinear points.
|
* \note There must be at least 4 non-colinear points.
|
||||||
*/
|
*/
|
||||||
bool Homography2DFromCorrespondencesLinear(const Mat &x1,
|
bool Homography2DFromCorrespondencesLinear(
|
||||||
|
const Mat& x1,
|
||||||
const Mat& x2,
|
const Mat& x2,
|
||||||
Mat3* H,
|
Mat3* H,
|
||||||
double expected_precision =
|
double expected_precision = EigenDouble::dummy_precision());
|
||||||
EigenDouble::dummy_precision());
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This structure contains options that controls how the homography
|
* This structure contains options that controls how the homography
|
||||||
@@ -129,11 +129,11 @@ bool EstimateHomography2DFromCorrespondences(
|
|||||||
* \note Need at least 5 non coplanar points
|
* \note Need at least 5 non coplanar points
|
||||||
* \note Points coordinates must be in homogeneous coordinates
|
* \note Points coordinates must be in homogeneous coordinates
|
||||||
*/
|
*/
|
||||||
bool Homography3DFromCorrespondencesLinear(const Mat &x1,
|
bool Homography3DFromCorrespondencesLinear(
|
||||||
|
const Mat& x1,
|
||||||
const Mat& x2,
|
const Mat& x2,
|
||||||
Mat4* H,
|
Mat4* H,
|
||||||
double expected_precision =
|
double expected_precision = EigenDouble::dummy_precision());
|
||||||
EigenDouble::dummy_precision());
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Calculate symmetric geometric cost:
|
* Calculate symmetric geometric cost:
|
||||||
|
@@ -47,8 +47,7 @@ struct AsymmetricError {
|
|||||||
* \param[in] x2 A set of 2D points (2xN or 3xN matrix of column vectors).
|
* \param[in] x2 A set of 2D points (2xN or 3xN matrix of column vectors).
|
||||||
* \param[out] dx A 2xN matrix of column vectors of residuals errors
|
* \param[out] dx A 2xN matrix of column vectors of residuals errors
|
||||||
*/
|
*/
|
||||||
static void Residuals(const Mat &H, const Mat &x1,
|
static void Residuals(const Mat& H, const Mat& x1, const Mat& x2, Mat2X* dx) {
|
||||||
const Mat &x2, Mat2X *dx) {
|
|
||||||
dx->resize(2, x1.cols());
|
dx->resize(2, x1.cols());
|
||||||
Mat3X x2h_est;
|
Mat3X x2h_est;
|
||||||
if (x1.rows() == 2)
|
if (x1.rows() == 2)
|
||||||
@@ -74,8 +73,7 @@ struct AsymmetricError {
|
|||||||
* \param[in] x2 A 2D point (vector of size 2 or 3 (euclidean/homogeneous))
|
* \param[in] x2 A 2D point (vector of size 2 or 3 (euclidean/homogeneous))
|
||||||
* \param[out] dx A vector of size 2 of the residual error
|
* \param[out] dx A vector of size 2 of the residual error
|
||||||
*/
|
*/
|
||||||
static void Residuals(const Mat &H, const Vec &x1,
|
static void Residuals(const Mat& H, const Vec& x1, const Vec& x2, Vec2* dx) {
|
||||||
const Vec &x2, Vec2 *dx) {
|
|
||||||
Vec3 x2h_est;
|
Vec3 x2h_est;
|
||||||
if (x1.rows() == 2)
|
if (x1.rows() == 2)
|
||||||
x2h_est = H * EuclideanToHomogeneous(static_cast<Vec2>(x1));
|
x2h_est = H * EuclideanToHomogeneous(static_cast<Vec2>(x1));
|
||||||
@@ -171,8 +169,7 @@ struct AlgebraicError {
|
|||||||
* \param[in] x2 A set of 2D points (2xN or 3xN matrix of column vectors).
|
* \param[in] x2 A set of 2D points (2xN or 3xN matrix of column vectors).
|
||||||
* \param[out] dx A 3xN matrix of column vectors of residuals errors
|
* \param[out] dx A 3xN matrix of column vectors of residuals errors
|
||||||
*/
|
*/
|
||||||
static void Residuals(const Mat &H, const Mat &x1,
|
static void Residuals(const Mat& H, const Mat& x1, const Mat& x2, Mat3X* dx) {
|
||||||
const Mat &x2, Mat3X *dx) {
|
|
||||||
dx->resize(3, x1.cols());
|
dx->resize(3, x1.cols());
|
||||||
Vec3 col;
|
Vec3 col;
|
||||||
for (int i = 0; i < x1.cols(); ++i) {
|
for (int i = 0; i < x1.cols(); ++i) {
|
||||||
@@ -191,8 +188,7 @@ struct AlgebraicError {
|
|||||||
* \param[in] x2 A 2D point (vector of size 2 or 3 (euclidean/homogeneous))
|
* \param[in] x2 A 2D point (vector of size 2 or 3 (euclidean/homogeneous))
|
||||||
* \param[out] dx A vector of size 3 of the residual error
|
* \param[out] dx A vector of size 3 of the residual error
|
||||||
*/
|
*/
|
||||||
static void Residuals(const Mat &H, const Vec &x1,
|
static void Residuals(const Mat& H, const Vec& x1, const Vec& x2, Vec3* dx) {
|
||||||
const Vec &x2, Vec3 *dx) {
|
|
||||||
Vec3 x2h_est;
|
Vec3 x2h_est;
|
||||||
if (x1.rows() == 2)
|
if (x1.rows() == 2)
|
||||||
x2h_est = H * EuclideanToHomogeneous(static_cast<Vec2>(x1));
|
x2h_est = H * EuclideanToHomogeneous(static_cast<Vec2>(x1));
|
||||||
|
@@ -41,16 +41,20 @@ class Homography2DNormalizedParameterization {
|
|||||||
|
|
||||||
/// Convert from the 8 parameters to a H matrix.
|
/// Convert from the 8 parameters to a H matrix.
|
||||||
static void To(const Parameters& p, Parameterized* h) {
|
static void To(const Parameters& p, Parameterized* h) {
|
||||||
|
// clang-format off
|
||||||
*h << p(0), p(1), p(2),
|
*h << p(0), p(1), p(2),
|
||||||
p(3), p(4), p(5),
|
p(3), p(4), p(5),
|
||||||
p(6), p(7), 1.0;
|
p(6), p(7), 1.0;
|
||||||
|
// clang-format on
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Convert from a H matrix to the 8 parameters.
|
/// Convert from a H matrix to the 8 parameters.
|
||||||
static void From(const Parameterized& h, Parameters* p) {
|
static void From(const Parameterized& h, Parameters* p) {
|
||||||
|
// clang-format off
|
||||||
*p << h(0, 0), h(0, 1), h(0, 2),
|
*p << h(0, 0), h(0, 1), h(0, 2),
|
||||||
h(1, 0), h(1, 1), h(1, 2),
|
h(1, 0), h(1, 1), h(1, 2),
|
||||||
h(2, 0), h(2, 1);
|
h(2, 0), h(2, 1);
|
||||||
|
// clang-format on
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -71,18 +75,22 @@ class Homography3DNormalizedParameterization {
|
|||||||
|
|
||||||
/// Convert from the 15 parameters to a H matrix.
|
/// Convert from the 15 parameters to a H matrix.
|
||||||
static void To(const Parameters& p, Parameterized* h) {
|
static void To(const Parameters& p, Parameterized* h) {
|
||||||
|
// clang-format off
|
||||||
*h << p(0), p(1), p(2), p(3),
|
*h << p(0), p(1), p(2), p(3),
|
||||||
p(4), p(5), p(6), p(7),
|
p(4), p(5), p(6), p(7),
|
||||||
p(8), p(9), p(10), p(11),
|
p(8), p(9), p(10), p(11),
|
||||||
p(12), p(13), p(14), 1.0;
|
p(12), p(13), p(14), 1.0;
|
||||||
|
// clang-format on
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Convert from a H matrix to the 15 parameters.
|
/// Convert from a H matrix to the 15 parameters.
|
||||||
static void From(const Parameterized& h, Parameters* p) {
|
static void From(const Parameterized& h, Parameters* p) {
|
||||||
|
// clang-format off
|
||||||
*p << h(0, 0), h(0, 1), h(0, 2), h(0, 3),
|
*p << h(0, 0), h(0, 1), h(0, 2), h(0, 3),
|
||||||
h(1, 0), h(1, 1), h(1, 2), h(1, 3),
|
h(1, 0), h(1, 1), h(1, 2), h(1, 3),
|
||||||
h(2, 0), h(2, 1), h(2, 2), h(2, 3),
|
h(2, 0), h(2, 1), h(2, 2), h(2, 3),
|
||||||
h(3, 0), h(3, 1), h(3, 2);
|
h(3, 0), h(3, 1), h(3, 2);
|
||||||
|
// clang-format on
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@@ -18,10 +18,10 @@
|
|||||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||||
// IN THE SOFTWARE.
|
// IN THE SOFTWARE.
|
||||||
|
|
||||||
#include "testing/testing.h"
|
#include "libmv/multiview/homography.h"
|
||||||
#include "libmv/logging/logging.h"
|
#include "libmv/logging/logging.h"
|
||||||
#include "libmv/multiview/projection.h"
|
#include "libmv/multiview/projection.h"
|
||||||
#include "libmv/multiview/homography.h"
|
#include "testing/testing.h"
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
using namespace libmv;
|
using namespace libmv;
|
||||||
@@ -34,9 +34,7 @@ namespace {
|
|||||||
// TODO(sergey): Consider using this in all tests since possible homography
|
// TODO(sergey): Consider using this in all tests since possible homography
|
||||||
// matrix is not fixed to a single value and different-looking matrices
|
// matrix is not fixed to a single value and different-looking matrices
|
||||||
// might actually crrespond to the same exact transform.
|
// might actually crrespond to the same exact transform.
|
||||||
void CheckHomography2DTransform(const Mat3 &H,
|
void CheckHomography2DTransform(const Mat3& H, const Mat& x1, const Mat& x2) {
|
||||||
const Mat &x1,
|
|
||||||
const Mat &x2) {
|
|
||||||
for (int i = 0; i < x2.cols(); ++i) {
|
for (int i = 0; i < x2.cols(); ++i) {
|
||||||
Vec3 x2_expected = x2.col(i);
|
Vec3 x2_expected = x2.col(i);
|
||||||
Vec3 x2_observed = H * x1.col(i);
|
Vec3 x2_observed = H * x1.col(i);
|
||||||
@@ -49,15 +47,19 @@ void CheckHomography2DTransform(const Mat3 &H,
|
|||||||
|
|
||||||
TEST(Homography2DTest, Rotation45AndTranslationXY) {
|
TEST(Homography2DTest, Rotation45AndTranslationXY) {
|
||||||
Mat x1(3, 4);
|
Mat x1(3, 4);
|
||||||
|
// clang-format off
|
||||||
x1 << 0, 1, 0, 5,
|
x1 << 0, 1, 0, 5,
|
||||||
0, 0, 2, 3,
|
0, 0, 2, 3,
|
||||||
1, 1, 1, 1;
|
1, 1, 1, 1;
|
||||||
|
// clang-format on
|
||||||
|
|
||||||
double angle = 45.0;
|
double angle = 45.0;
|
||||||
Mat3 m;
|
Mat3 m;
|
||||||
|
// clang-format off
|
||||||
m << cos(angle), -sin(angle), -2,
|
m << cos(angle), -sin(angle), -2,
|
||||||
sin(angle), cos(angle), 5,
|
sin(angle), cos(angle), 5,
|
||||||
0, 0, 1;
|
0, 0, 1;
|
||||||
|
// clang-format on
|
||||||
|
|
||||||
Mat x2 = x1;
|
Mat x2 = x1;
|
||||||
// Transform point from ground truth matrix
|
// Transform point from ground truth matrix
|
||||||
@@ -76,13 +78,17 @@ TEST(Homography2DTest, Rotation45AndTranslationXY) {
|
|||||||
TEST(Homography2DTest, AffineGeneral4) {
|
TEST(Homography2DTest, AffineGeneral4) {
|
||||||
// TODO(julien) find why it doesn't work with 4 points!!!
|
// TODO(julien) find why it doesn't work with 4 points!!!
|
||||||
Mat x1(3, 4);
|
Mat x1(3, 4);
|
||||||
|
// clang-format off
|
||||||
x1 << 0, 1, 0, 2,
|
x1 << 0, 1, 0, 2,
|
||||||
0, 0, 1, 2,
|
0, 0, 1, 2,
|
||||||
1, 1, 1, 1;
|
1, 1, 1, 1;
|
||||||
|
// clang-format on
|
||||||
Mat3 m;
|
Mat3 m;
|
||||||
|
// clang-format off
|
||||||
m << 3, -1, 4,
|
m << 3, -1, 4,
|
||||||
6, -2, -3,
|
6, -2, -3,
|
||||||
0, 0, 1;
|
0, 0, 1;
|
||||||
|
// clang-format on
|
||||||
|
|
||||||
Mat x2 = x1;
|
Mat x2 = x1;
|
||||||
for (int i = 0; i < x2.cols(); ++i) {
|
for (int i = 0; i < x2.cols(); ++i) {
|
||||||
@@ -109,13 +115,17 @@ TEST(Homography2DTest, AffineGeneral4) {
|
|||||||
|
|
||||||
TEST(Homography2DTest, AffineGeneral5) {
|
TEST(Homography2DTest, AffineGeneral5) {
|
||||||
Mat x1(3, 5);
|
Mat x1(3, 5);
|
||||||
|
// clang-format off
|
||||||
x1 << 0, 1, 0, 2, 5,
|
x1 << 0, 1, 0, 2, 5,
|
||||||
0, 0, 1, 2, 2,
|
0, 0, 1, 2, 2,
|
||||||
1, 1, 1, 1, 1;
|
1, 1, 1, 1, 1;
|
||||||
|
// clang-format on
|
||||||
Mat3 m;
|
Mat3 m;
|
||||||
|
// clang-format off
|
||||||
m << 3, -1, 4,
|
m << 3, -1, 4,
|
||||||
6, -2, -3,
|
6, -2, -3,
|
||||||
0, 0, 1;
|
0, 0, 1;
|
||||||
|
// clang-format on
|
||||||
|
|
||||||
Mat x2 = x1;
|
Mat x2 = x1;
|
||||||
for (int i = 0; i < x2.cols(); ++i)
|
for (int i = 0; i < x2.cols(); ++i)
|
||||||
@@ -142,13 +152,17 @@ TEST(Homography2DTest, AffineGeneral5) {
|
|||||||
|
|
||||||
TEST(Homography2DTest, HomographyGeneral) {
|
TEST(Homography2DTest, HomographyGeneral) {
|
||||||
Mat x1(3, 4);
|
Mat x1(3, 4);
|
||||||
|
// clang-format off
|
||||||
x1 << 0, 1, 0, 5,
|
x1 << 0, 1, 0, 5,
|
||||||
0, 0, 2, 3,
|
0, 0, 2, 3,
|
||||||
1, 1, 1, 1;
|
1, 1, 1, 1;
|
||||||
|
// clang-format on
|
||||||
Mat3 m;
|
Mat3 m;
|
||||||
|
// clang-format off
|
||||||
m << 3, -1, 4,
|
m << 3, -1, 4,
|
||||||
6, -2, -3,
|
6, -2, -3,
|
||||||
1, -3, 1;
|
1, -3, 1;
|
||||||
|
// clang-format on
|
||||||
|
|
||||||
Mat x2 = x1;
|
Mat x2 = x1;
|
||||||
for (int i = 0; i < x2.cols(); ++i)
|
for (int i = 0; i < x2.cols(); ++i)
|
||||||
@@ -164,10 +178,12 @@ TEST(Homography2DTest, HomographyGeneral) {
|
|||||||
|
|
||||||
TEST(Homography3DTest, RotationAndTranslationXYZ) {
|
TEST(Homography3DTest, RotationAndTranslationXYZ) {
|
||||||
Mat x1(4, 5);
|
Mat x1(4, 5);
|
||||||
|
// clang-format off
|
||||||
x1 << 0, 0, 1, 5, 2,
|
x1 << 0, 0, 1, 5, 2,
|
||||||
0, 1, 2, 3, 5,
|
0, 1, 2, 3, 5,
|
||||||
0, 2, 0, 1, 5,
|
0, 2, 0, 1, 5,
|
||||||
1, 1, 1, 1, 1;
|
1, 1, 1, 1, 1;
|
||||||
|
// clang-format on
|
||||||
Mat4 M;
|
Mat4 M;
|
||||||
M.setIdentity();
|
M.setIdentity();
|
||||||
/*
|
/*
|
||||||
@@ -178,24 +194,30 @@ TEST(Homography3DTest, RotationAndTranslationXYZ) {
|
|||||||
// Rotation on x + translation
|
// Rotation on x + translation
|
||||||
double angle = 45.0;
|
double angle = 45.0;
|
||||||
Mat4 rot;
|
Mat4 rot;
|
||||||
|
// clang-format off
|
||||||
rot << 1, 0, 0, 1,
|
rot << 1, 0, 0, 1,
|
||||||
0, cos(angle), -sin(angle), 3,
|
0, cos(angle), -sin(angle), 3,
|
||||||
0, sin(angle), cos(angle), -2,
|
0, sin(angle), cos(angle), -2,
|
||||||
0, 0, 0, 1;
|
0, 0, 0, 1;
|
||||||
|
// clang-format on
|
||||||
M *= rot;
|
M *= rot;
|
||||||
// Rotation on y
|
// Rotation on y
|
||||||
angle = 25.0;
|
angle = 25.0;
|
||||||
|
// clang-format off
|
||||||
rot << cos(angle), 0, sin(angle), 0,
|
rot << cos(angle), 0, sin(angle), 0,
|
||||||
0, 1, 0, 0,
|
0, 1, 0, 0,
|
||||||
-sin(angle), 0, cos(angle), 0,
|
-sin(angle), 0, cos(angle), 0,
|
||||||
0, 0, 0, 1;
|
0, 0, 0, 1;
|
||||||
|
// clang-format on
|
||||||
M *= rot;
|
M *= rot;
|
||||||
// Rotation on z
|
// Rotation on z
|
||||||
angle = 5.0;
|
angle = 5.0;
|
||||||
|
// clang-format off
|
||||||
rot << cos(angle), -sin(angle), 0, 0,
|
rot << cos(angle), -sin(angle), 0, 0,
|
||||||
sin(angle), cos(angle), 0, 0,
|
sin(angle), cos(angle), 0, 0,
|
||||||
0, 0, 1, 0,
|
0, 0, 1, 0,
|
||||||
0, 0, 0, 1;
|
0, 0, 0, 1;
|
||||||
|
// clang-format on
|
||||||
M *= rot;
|
M *= rot;
|
||||||
Mat x2 = x1;
|
Mat x2 = x1;
|
||||||
for (int i = 0; i < x2.cols(); ++i) {
|
for (int i = 0; i < x2.cols(); ++i) {
|
||||||
@@ -212,15 +234,19 @@ TEST(Homography3DTest, RotationAndTranslationXYZ) {
|
|||||||
|
|
||||||
TEST(Homography3DTest, AffineGeneral) {
|
TEST(Homography3DTest, AffineGeneral) {
|
||||||
Mat x1(4, 5);
|
Mat x1(4, 5);
|
||||||
|
// clang-format off
|
||||||
x1 << 0, 0, 1, 5, 2,
|
x1 << 0, 0, 1, 5, 2,
|
||||||
0, 1, 2, 3, 5,
|
0, 1, 2, 3, 5,
|
||||||
0, 2, 0, 1, 5,
|
0, 2, 0, 1, 5,
|
||||||
1, 1, 1, 1, 1;
|
1, 1, 1, 1, 1;
|
||||||
|
// clang-format on
|
||||||
Mat4 m;
|
Mat4 m;
|
||||||
|
// clang-format off
|
||||||
m << 3, -1, 4, 1,
|
m << 3, -1, 4, 1,
|
||||||
6, -2, -3, -6,
|
6, -2, -3, -6,
|
||||||
1, 0, 1, 2,
|
1, 0, 1, 2,
|
||||||
0, 0, 0, 1;
|
0, 0, 0, 1;
|
||||||
|
// clang-format on
|
||||||
|
|
||||||
Mat x2 = x1;
|
Mat x2 = x1;
|
||||||
for (int i = 0; i < x2.cols(); ++i) {
|
for (int i = 0; i < x2.cols(); ++i) {
|
||||||
@@ -236,15 +262,19 @@ TEST(Homography3DTest, AffineGeneral) {
|
|||||||
|
|
||||||
TEST(Homography3DTest, HomographyGeneral) {
|
TEST(Homography3DTest, HomographyGeneral) {
|
||||||
Mat x1(4, 5);
|
Mat x1(4, 5);
|
||||||
|
// clang-format off
|
||||||
x1 << 0, 0, 1, 5, 2,
|
x1 << 0, 0, 1, 5, 2,
|
||||||
0, 1, 2, 3, 5,
|
0, 1, 2, 3, 5,
|
||||||
0, 2, 0, 1, 5,
|
0, 2, 0, 1, 5,
|
||||||
1, 1, 1, 1, 1;
|
1, 1, 1, 1, 1;
|
||||||
|
// clang-format on
|
||||||
Mat4 m;
|
Mat4 m;
|
||||||
|
// clang-format off
|
||||||
m << 3, -1, 4, 1,
|
m << 3, -1, 4, 1,
|
||||||
6, -2, -3, -6,
|
6, -2, -3, -6,
|
||||||
1, 0, 1, 2,
|
1, 0, 1, 2,
|
||||||
-3, 1, 0, 1;
|
-3, 1, 0, 1;
|
||||||
|
// clang-format on
|
||||||
|
|
||||||
Mat x2 = x1;
|
Mat x2 = x1;
|
||||||
for (int i = 0; i < x2.cols(); ++i) {
|
for (int i = 0; i < x2.cols(); ++i) {
|
||||||
|
@@ -24,7 +24,8 @@
|
|||||||
namespace libmv {
|
namespace libmv {
|
||||||
|
|
||||||
static bool Build_Minimal2Point_PolynomialFactor(
|
static bool Build_Minimal2Point_PolynomialFactor(
|
||||||
const Mat & x1, const Mat & x2,
|
const Mat& x1,
|
||||||
|
const Mat& x2,
|
||||||
double* P) { // P must be a double[4]
|
double* P) { // P must be a double[4]
|
||||||
assert(2 == x1.rows());
|
assert(2 == x1.rows());
|
||||||
assert(2 == x1.cols());
|
assert(2 == x1.cols());
|
||||||
@@ -53,8 +54,10 @@ static bool Build_Minimal2Point_PolynomialFactor(
|
|||||||
// Coefficients in ascending powers of alpha, i.e. P[N]*x^N.
|
// Coefficients in ascending powers of alpha, i.e. P[N]*x^N.
|
||||||
// Run panography_coeffs.py to get the below coefficients.
|
// Run panography_coeffs.py to get the below coefficients.
|
||||||
P[0] = b1 * b2 * a12 * a12 - a1 * a2 * b12 * b12;
|
P[0] = b1 * b2 * a12 * a12 - a1 * a2 * b12 * b12;
|
||||||
P[1] = -2*a1*a2*b12+2*a12*b1*b2+b1*a12*a12+b2*a12*a12-a1*b12*b12-a2*b12*b12;
|
P[1] = -2 * a1 * a2 * b12 + 2 * a12 * b1 * b2 + b1 * a12 * a12 +
|
||||||
P[2] = b1*b2-a1*a2-2*a1*b12-2*a2*b12+2*a12*b1+2*a12*b2+a12*a12-b12*b12;
|
b2 * a12 * a12 - a1 * b12 * b12 - a2 * b12 * b12;
|
||||||
|
P[2] = b1 * b2 - a1 * a2 - 2 * a1 * b12 - 2 * a2 * b12 + 2 * a12 * b1 +
|
||||||
|
2 * a12 * b2 + a12 * a12 - b12 * b12;
|
||||||
P[3] = b1 + b2 - 2 * b12 - a1 - a2 + 2 * a12;
|
P[3] = b1 + b2 - 2 * b12 - a1 - a2 + 2 * a12;
|
||||||
|
|
||||||
// If P[3] equal to 0 we get ill conditionned data
|
// If P[3] equal to 0 we get ill conditionned data
|
||||||
@@ -67,7 +70,8 @@ static bool Build_Minimal2Point_PolynomialFactor(
|
|||||||
//
|
//
|
||||||
// [1] M. Brown and R. Hartley and D. Nister. Minimal Solutions for Panoramic
|
// [1] M. Brown and R. Hartley and D. Nister. Minimal Solutions for Panoramic
|
||||||
// Stitching. CVPR07.
|
// Stitching. CVPR07.
|
||||||
void F_FromCorrespondance_2points(const Mat &x1, const Mat &x2,
|
void F_FromCorrespondance_2points(const Mat& x1,
|
||||||
|
const Mat& x2,
|
||||||
vector<double>* fs) {
|
vector<double>* fs) {
|
||||||
// Build Polynomial factor to get squared focal value.
|
// Build Polynomial factor to get squared focal value.
|
||||||
double P[4];
|
double P[4];
|
||||||
@@ -92,7 +96,8 @@ void F_FromCorrespondance_2points(const Mat &x1, const Mat &x2,
|
|||||||
// K. Arun,T. Huand and D. Blostein. Least-squares fitting of 2 3-D point
|
// K. Arun,T. Huand and D. Blostein. Least-squares fitting of 2 3-D point
|
||||||
// sets. IEEE Transactions on Pattern Analysis and Machine Intelligence,
|
// sets. IEEE Transactions on Pattern Analysis and Machine Intelligence,
|
||||||
// 9:698-700, 1987.
|
// 9:698-700, 1987.
|
||||||
void GetR_FixedCameraCenter(const Mat &x1, const Mat &x2,
|
void GetR_FixedCameraCenter(const Mat& x1,
|
||||||
|
const Mat& x2,
|
||||||
const double focal,
|
const double focal,
|
||||||
Mat3* R) {
|
Mat3* R) {
|
||||||
assert(3 == x1.rows());
|
assert(3 == x1.rows());
|
||||||
@@ -115,8 +120,8 @@ void GetR_FixedCameraCenter(const Mat &x1, const Mat &x2,
|
|||||||
// Solve for rotation. Equations (24) and (25) in [1].
|
// Solve for rotation. Equations (24) and (25) in [1].
|
||||||
Eigen::JacobiSVD<Mat> svd(C, Eigen::ComputeThinU | Eigen::ComputeThinV);
|
Eigen::JacobiSVD<Mat> svd(C, Eigen::ComputeThinU | Eigen::ComputeThinV);
|
||||||
Mat3 scale = Mat3::Identity();
|
Mat3 scale = Mat3::Identity();
|
||||||
scale(2, 2) = ((svd.matrixU() * svd.matrixV().transpose()).determinant() > 0.0)
|
scale(2, 2) =
|
||||||
? 1.0
|
((svd.matrixU() * svd.matrixV().transpose()).determinant() > 0.0) ? 1.0
|
||||||
: -1.0;
|
: -1.0;
|
||||||
|
|
||||||
(*R) = svd.matrixU() * scale * svd.matrixV().transpose();
|
(*R) = svd.matrixU() * scale * svd.matrixV().transpose();
|
||||||
|
@@ -22,9 +22,9 @@
|
|||||||
#ifndef LIBMV_MULTIVIEW_PANOGRAPHY_H
|
#ifndef LIBMV_MULTIVIEW_PANOGRAPHY_H
|
||||||
#define LIBMV_MULTIVIEW_PANOGRAPHY_H
|
#define LIBMV_MULTIVIEW_PANOGRAPHY_H
|
||||||
|
|
||||||
|
#include "libmv/base/vector.h"
|
||||||
#include "libmv/numeric/numeric.h"
|
#include "libmv/numeric/numeric.h"
|
||||||
#include "libmv/numeric/poly.h"
|
#include "libmv/numeric/poly.h"
|
||||||
#include "libmv/base/vector.h"
|
|
||||||
|
|
||||||
namespace libmv {
|
namespace libmv {
|
||||||
|
|
||||||
@@ -53,7 +53,8 @@ namespace libmv {
|
|||||||
// K = [0 f 0]
|
// K = [0 f 0]
|
||||||
// [0 0 1]
|
// [0 0 1]
|
||||||
//
|
//
|
||||||
void F_FromCorrespondance_2points(const Mat &x1, const Mat &x2,
|
void F_FromCorrespondance_2points(const Mat& x1,
|
||||||
|
const Mat& x2,
|
||||||
vector<double>* fs);
|
vector<double>* fs);
|
||||||
|
|
||||||
// Compute the 3x3 rotation matrix that fits two 3D point clouds in the least
|
// Compute the 3x3 rotation matrix that fits two 3D point clouds in the least
|
||||||
@@ -90,7 +91,8 @@ void F_FromCorrespondance_2points(const Mat &x1, const Mat &x2,
|
|||||||
//
|
//
|
||||||
// R = arg min || X2 - R * x1 ||
|
// R = arg min || X2 - R * x1 ||
|
||||||
//
|
//
|
||||||
void GetR_FixedCameraCenter(const Mat &x1, const Mat &x2,
|
void GetR_FixedCameraCenter(const Mat& x1,
|
||||||
|
const Mat& x2,
|
||||||
const double focal,
|
const double focal,
|
||||||
Mat3* R);
|
Mat3* R);
|
||||||
|
|
||||||
|
@@ -23,9 +23,9 @@
|
|||||||
|
|
||||||
#include "libmv/base/vector.h"
|
#include "libmv/base/vector.h"
|
||||||
#include "libmv/multiview/conditioning.h"
|
#include "libmv/multiview/conditioning.h"
|
||||||
|
#include "libmv/multiview/homography_error.h"
|
||||||
#include "libmv/multiview/projection.h"
|
#include "libmv/multiview/projection.h"
|
||||||
#include "libmv/multiview/two_view_kernel.h"
|
#include "libmv/multiview/two_view_kernel.h"
|
||||||
#include "libmv/multiview/homography_error.h"
|
|
||||||
#include "libmv/numeric/numeric.h"
|
#include "libmv/numeric/numeric.h"
|
||||||
|
|
||||||
namespace libmv {
|
namespace libmv {
|
||||||
@@ -37,8 +37,8 @@ struct TwoPointSolver {
|
|||||||
static void Solve(const Mat& x1, const Mat& x2, vector<Mat3>* Hs);
|
static void Solve(const Mat& x1, const Mat& x2, vector<Mat3>* Hs);
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef two_view::kernel::Kernel<
|
typedef two_view::kernel::
|
||||||
TwoPointSolver, homography::homography2D::AsymmetricError, Mat3>
|
Kernel<TwoPointSolver, homography::homography2D::AsymmetricError, Mat3>
|
||||||
UnnormalizedKernel;
|
UnnormalizedKernel;
|
||||||
|
|
||||||
typedef two_view::kernel::Kernel<
|
typedef two_view::kernel::Kernel<
|
||||||
|
@@ -18,8 +18,8 @@
|
|||||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||||
// IN THE SOFTWARE.
|
// IN THE SOFTWARE.
|
||||||
|
|
||||||
#include "libmv/logging/logging.h"
|
|
||||||
#include "libmv/multiview/panography.h"
|
#include "libmv/multiview/panography.h"
|
||||||
|
#include "libmv/logging/logging.h"
|
||||||
#include "libmv/multiview/panography_kernel.h"
|
#include "libmv/multiview/panography_kernel.h"
|
||||||
#include "libmv/multiview/projection.h"
|
#include "libmv/multiview/projection.h"
|
||||||
#include "libmv/numeric/numeric.h"
|
#include "libmv/numeric/numeric.h"
|
||||||
@@ -30,10 +30,8 @@ namespace {
|
|||||||
|
|
||||||
TEST(Panography, PrintSomeSharedFocalEstimationValues) {
|
TEST(Panography, PrintSomeSharedFocalEstimationValues) {
|
||||||
Mat x1(2, 2), x2(2, 2);
|
Mat x1(2, 2), x2(2, 2);
|
||||||
x1<< 158, 78,
|
x1 << 158, 78, 124, 113;
|
||||||
124, 113;
|
x2 << 300, 214, 125, 114;
|
||||||
x2<< 300, 214,
|
|
||||||
125, 114;
|
|
||||||
|
|
||||||
// Normalize data (set principal point 0,0 and image border to 1.0).
|
// Normalize data (set principal point 0,0 and image border to 1.0).
|
||||||
x1.block<1, 2>(0, 0) /= 320;
|
x1.block<1, 2>(0, 0) /= 320;
|
||||||
@@ -53,9 +51,11 @@ TEST(Panography, PrintSomeSharedFocalEstimationValues) {
|
|||||||
|
|
||||||
TEST(Panography, GetR_FixedCameraCenterWithIdentity) {
|
TEST(Panography, GetR_FixedCameraCenterWithIdentity) {
|
||||||
Mat x1(3, 3);
|
Mat x1(3, 3);
|
||||||
|
// clang-format off
|
||||||
x1 << 0.5, 0.6, 0.7,
|
x1 << 0.5, 0.6, 0.7,
|
||||||
0.5, 0.5, 0.4,
|
0.5, 0.5, 0.4,
|
||||||
10.0, 10.0, 10.0;
|
10.0, 10.0, 10.0;
|
||||||
|
// clang-format on
|
||||||
|
|
||||||
Mat3 R;
|
Mat3 R;
|
||||||
GetR_FixedCameraCenter(x1, x1, 1.0, &R);
|
GetR_FixedCameraCenter(x1, x1, 1.0, &R);
|
||||||
@@ -68,16 +68,20 @@ TEST(Panography, Homography_GetR_Test_PitchY30) {
|
|||||||
int n = 3;
|
int n = 3;
|
||||||
|
|
||||||
Mat x1(3, n);
|
Mat x1(3, n);
|
||||||
|
// clang-format off
|
||||||
x1 << 0.5, 0.6, 0.7,
|
x1 << 0.5, 0.6, 0.7,
|
||||||
0.5, 0.5, 0.4,
|
0.5, 0.5, 0.4,
|
||||||
10, 10, 10;
|
10, 10, 10;
|
||||||
|
// clang-format on
|
||||||
|
|
||||||
Mat x2 = x1;
|
Mat x2 = x1;
|
||||||
const double alpha = 30.0 * M_PI / 180.0;
|
const double alpha = 30.0 * M_PI / 180.0;
|
||||||
Mat3 rotY;
|
Mat3 rotY;
|
||||||
|
// clang-format off
|
||||||
rotY << cos(alpha), 0, -sin(alpha),
|
rotY << cos(alpha), 0, -sin(alpha),
|
||||||
0, 1, 0,
|
0, 1, 0,
|
||||||
sin(alpha), 0, cos(alpha);
|
sin(alpha), 0, cos(alpha);
|
||||||
|
// clang-format on
|
||||||
|
|
||||||
for (int i = 0; i < n; ++i) {
|
for (int i = 0; i < n; ++i) {
|
||||||
x2.block<3, 1>(0, i) = rotY * x1.col(i);
|
x2.block<3, 1>(0, i) = rotY * x1.col(i);
|
||||||
@@ -101,17 +105,23 @@ TEST(Panography, Homography_GetR_Test_PitchY30) {
|
|||||||
TEST(MinimalPanoramic, Real_Case_Kernel) {
|
TEST(MinimalPanoramic, Real_Case_Kernel) {
|
||||||
const int n = 2;
|
const int n = 2;
|
||||||
Mat x1(2, n); // From image 0.jpg
|
Mat x1(2, n); // From image 0.jpg
|
||||||
|
// clang-format off
|
||||||
x1<< 158, 78,
|
x1<< 158, 78,
|
||||||
124, 113;
|
124, 113;
|
||||||
|
// clang-format on
|
||||||
|
|
||||||
Mat x2(2, n); // From image 3.jpg
|
Mat x2(2, n); // From image 3.jpg
|
||||||
|
// clang-format off
|
||||||
x2<< 300, 214,
|
x2<< 300, 214,
|
||||||
125, 114;
|
125, 114;
|
||||||
|
// clang-format on
|
||||||
|
|
||||||
Mat3 Ground_TruthHomography;
|
Mat3 Ground_TruthHomography;
|
||||||
|
// clang-format off
|
||||||
Ground_TruthHomography<< 1, 0.02, 129.83,
|
Ground_TruthHomography<< 1, 0.02, 129.83,
|
||||||
-0.02, 1.012, 0.07823,
|
-0.02, 1.012, 0.07823,
|
||||||
0, 0, 1;
|
0, 0, 1;
|
||||||
|
// clang-format on
|
||||||
|
|
||||||
vector<Mat3> Hs;
|
vector<Mat3> Hs;
|
||||||
|
|
||||||
|
@@ -44,9 +44,11 @@ void KRt_From_P(const Mat34 &P, Mat3 *Kp, Mat3 *Rp, Vec3 *tp) {
|
|||||||
c /= l;
|
c /= l;
|
||||||
s /= l;
|
s /= l;
|
||||||
Mat3 Qx;
|
Mat3 Qx;
|
||||||
|
// clang-format off
|
||||||
Qx << 1, 0, 0,
|
Qx << 1, 0, 0,
|
||||||
0, c, -s,
|
0, c, -s,
|
||||||
0, s, c;
|
0, s, c;
|
||||||
|
// clang-format on
|
||||||
K = K * Qx;
|
K = K * Qx;
|
||||||
Q = Qx.transpose() * Q;
|
Q = Qx.transpose() * Q;
|
||||||
}
|
}
|
||||||
@@ -58,9 +60,11 @@ void KRt_From_P(const Mat34 &P, Mat3 *Kp, Mat3 *Rp, Vec3 *tp) {
|
|||||||
c /= l;
|
c /= l;
|
||||||
s /= l;
|
s /= l;
|
||||||
Mat3 Qy;
|
Mat3 Qy;
|
||||||
|
// clang-format off
|
||||||
Qy << c, 0, s,
|
Qy << c, 0, s,
|
||||||
0, 1, 0,
|
0, 1, 0,
|
||||||
-s, 0, c;
|
-s, 0, c;
|
||||||
|
// clang-format on
|
||||||
K = K * Qy;
|
K = K * Qy;
|
||||||
Q = Qy.transpose() * Q;
|
Q = Qy.transpose() * Q;
|
||||||
}
|
}
|
||||||
@@ -72,9 +76,11 @@ void KRt_From_P(const Mat34 &P, Mat3 *Kp, Mat3 *Rp, Vec3 *tp) {
|
|||||||
c /= l;
|
c /= l;
|
||||||
s /= l;
|
s /= l;
|
||||||
Mat3 Qz;
|
Mat3 Qz;
|
||||||
|
// clang-format off
|
||||||
Qz << c, -s, 0,
|
Qz << c, -s, 0,
|
||||||
s, c, 0,
|
s, c, 0,
|
||||||
0, 0, 1;
|
0, 0, 1;
|
||||||
|
// clang-format on
|
||||||
K = K * Qz;
|
K = K * Qz;
|
||||||
Q = Qz.transpose() * Q;
|
Q = Qz.transpose() * Q;
|
||||||
}
|
}
|
||||||
@@ -92,17 +98,21 @@ void KRt_From_P(const Mat34 &P, Mat3 *Kp, Mat3 *Rp, Vec3 *tp) {
|
|||||||
}
|
}
|
||||||
if (K(1, 1) < 0) {
|
if (K(1, 1) < 0) {
|
||||||
Mat3 S;
|
Mat3 S;
|
||||||
|
// clang-format off
|
||||||
S << 1, 0, 0,
|
S << 1, 0, 0,
|
||||||
0, -1, 0,
|
0, -1, 0,
|
||||||
0, 0, 1;
|
0, 0, 1;
|
||||||
|
// clang-format on
|
||||||
K = K * S;
|
K = K * S;
|
||||||
R = S * R;
|
R = S * R;
|
||||||
}
|
}
|
||||||
if (K(0, 0) < 0) {
|
if (K(0, 0) < 0) {
|
||||||
Mat3 S;
|
Mat3 S;
|
||||||
|
// clang-format off
|
||||||
S << -1, 0, 0,
|
S << -1, 0, 0,
|
||||||
0, 1, 0,
|
0, 1, 0,
|
||||||
0, 0, 1;
|
0, 0, 1;
|
||||||
|
// clang-format on
|
||||||
K = K * S;
|
K = K * S;
|
||||||
R = S * R;
|
R = S * R;
|
||||||
}
|
}
|
||||||
@@ -127,9 +137,11 @@ void ProjectionShiftPrincipalPoint(const Mat34 &P,
|
|||||||
const Vec2& principal_point_new,
|
const Vec2& principal_point_new,
|
||||||
Mat34* P_new) {
|
Mat34* P_new) {
|
||||||
Mat3 T;
|
Mat3 T;
|
||||||
|
// clang-format off
|
||||||
T << 1, 0, principal_point_new(0) - principal_point(0),
|
T << 1, 0, principal_point_new(0) - principal_point(0),
|
||||||
0, 1, principal_point_new(1) - principal_point(1),
|
0, 1, principal_point_new(1) - principal_point(1),
|
||||||
0, 0, 1;
|
0, 0, 1;
|
||||||
|
// clang-format on
|
||||||
*P_new = T * P;
|
*P_new = T * P;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -139,9 +151,11 @@ void ProjectionChangeAspectRatio(const Mat34 &P,
|
|||||||
double aspect_ratio_new,
|
double aspect_ratio_new,
|
||||||
Mat34* P_new) {
|
Mat34* P_new) {
|
||||||
Mat3 T;
|
Mat3 T;
|
||||||
|
// clang-format off
|
||||||
T << 1, 0, 0,
|
T << 1, 0, 0,
|
||||||
0, aspect_ratio_new / aspect_ratio, 0,
|
0, aspect_ratio_new / aspect_ratio, 0,
|
||||||
0, 0, 1;
|
0, 0, 1;
|
||||||
|
// clang-format on
|
||||||
Mat34 P_temp;
|
Mat34 P_temp;
|
||||||
|
|
||||||
ProjectionShiftPrincipalPoint(P, principal_point, Vec2(0, 0), &P_temp);
|
ProjectionShiftPrincipalPoint(P, principal_point, Vec2(0, 0), &P_temp);
|
||||||
|
@@ -29,14 +29,18 @@ using namespace libmv;
|
|||||||
|
|
||||||
TEST(Projection, P_From_KRt) {
|
TEST(Projection, P_From_KRt) {
|
||||||
Mat3 K, Kp;
|
Mat3 K, Kp;
|
||||||
|
// clang-format off
|
||||||
K << 10, 1, 30,
|
K << 10, 1, 30,
|
||||||
0, 20, 40,
|
0, 20, 40,
|
||||||
0, 0, 1;
|
0, 0, 1;
|
||||||
|
// clang-format on
|
||||||
|
|
||||||
Mat3 R, Rp;
|
Mat3 R, Rp;
|
||||||
|
// clang-format off
|
||||||
R << 1, 0, 0,
|
R << 1, 0, 0,
|
||||||
0, 1, 0,
|
0, 1, 0,
|
||||||
0, 0, 1;
|
0, 0, 1;
|
||||||
|
// clang-format on
|
||||||
|
|
||||||
Vec3 t, tp;
|
Vec3 t, tp;
|
||||||
t << 1, 2, 3;
|
t << 1, 2, 3;
|
||||||
@@ -62,9 +66,11 @@ Vec4 GetRandomPoint() {
|
|||||||
|
|
||||||
TEST(Projection, isInFrontOfCamera) {
|
TEST(Projection, isInFrontOfCamera) {
|
||||||
Mat34 P;
|
Mat34 P;
|
||||||
|
// clang-format off
|
||||||
P << 1, 0, 0, 0,
|
P << 1, 0, 0, 0,
|
||||||
0, 1, 0, 0,
|
0, 1, 0, 0,
|
||||||
0, 0, 1, 0;
|
0, 0, 1, 0;
|
||||||
|
// clang-format on
|
||||||
|
|
||||||
Vec4 X_front = GetRandomPoint();
|
Vec4 X_front = GetRandomPoint();
|
||||||
Vec4 X_back = GetRandomPoint();
|
Vec4 X_back = GetRandomPoint();
|
||||||
@@ -82,12 +88,14 @@ TEST(Projection, isInFrontOfCamera) {
|
|||||||
|
|
||||||
TEST(AutoCalibration, ProjectionShiftPrincipalPoint) {
|
TEST(AutoCalibration, ProjectionShiftPrincipalPoint) {
|
||||||
Mat34 P1, P2;
|
Mat34 P1, P2;
|
||||||
|
// clang-format off
|
||||||
P1 << 1, 0, 0, 0,
|
P1 << 1, 0, 0, 0,
|
||||||
0, 1, 0, 0,
|
0, 1, 0, 0,
|
||||||
0, 0, 1, 0;
|
0, 0, 1, 0;
|
||||||
P2 << 1, 0, 3, 0,
|
P2 << 1, 0, 3, 0,
|
||||||
0, 1, 4, 0,
|
0, 1, 4, 0,
|
||||||
0, 0, 1, 0;
|
0, 0, 1, 0;
|
||||||
|
// clang-format on
|
||||||
Mat34 P1_computed, P2_computed;
|
Mat34 P1_computed, P2_computed;
|
||||||
ProjectionShiftPrincipalPoint(P1, Vec2(0, 0), Vec2(3, 4), &P2_computed);
|
ProjectionShiftPrincipalPoint(P1, Vec2(0, 0), Vec2(3, 4), &P2_computed);
|
||||||
ProjectionShiftPrincipalPoint(P2, Vec2(3, 4), Vec2(0, 0), &P1_computed);
|
ProjectionShiftPrincipalPoint(P2, Vec2(3, 4), Vec2(0, 0), &P1_computed);
|
||||||
@@ -98,12 +106,14 @@ TEST(AutoCalibration, ProjectionShiftPrincipalPoint) {
|
|||||||
|
|
||||||
TEST(AutoCalibration, ProjectionChangeAspectRatio) {
|
TEST(AutoCalibration, ProjectionChangeAspectRatio) {
|
||||||
Mat34 P1, P2;
|
Mat34 P1, P2;
|
||||||
|
// clang-format off
|
||||||
P1 << 1, 0, 3, 0,
|
P1 << 1, 0, 3, 0,
|
||||||
0, 1, 4, 0,
|
0, 1, 4, 0,
|
||||||
0, 0, 1, 0;
|
0, 0, 1, 0;
|
||||||
P2 << 1, 0, 3, 0,
|
P2 << 1, 0, 3, 0,
|
||||||
0, 2, 4, 0,
|
0, 2, 4, 0,
|
||||||
0, 0, 1, 0;
|
0, 0, 1, 0;
|
||||||
|
// clang-format on
|
||||||
Mat34 P1_computed, P2_computed;
|
Mat34 P1_computed, P2_computed;
|
||||||
ProjectionChangeAspectRatio(P1, Vec2(3, 4), 1, 2, &P2_computed);
|
ProjectionChangeAspectRatio(P1, Vec2(3, 4), 1, 2, &P2_computed);
|
||||||
ProjectionChangeAspectRatio(P2, Vec2(3, 4), 2, 1, &P1_computed);
|
ProjectionChangeAspectRatio(P2, Vec2(3, 4), 2, 1, &P1_computed);
|
||||||
|
@@ -20,12 +20,12 @@
|
|||||||
|
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
|
||||||
|
#include "libmv/logging/logging.h"
|
||||||
#include "libmv/multiview/projection.h"
|
#include "libmv/multiview/projection.h"
|
||||||
#include "libmv/multiview/resection.h"
|
#include "libmv/multiview/resection.h"
|
||||||
#include "libmv/multiview/test_data_sets.h"
|
#include "libmv/multiview/test_data_sets.h"
|
||||||
#include "libmv/numeric/numeric.h"
|
#include "libmv/numeric/numeric.h"
|
||||||
#include "testing/testing.h"
|
#include "testing/testing.h"
|
||||||
#include "libmv/logging/logging.h"
|
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
|
@@ -22,24 +22,28 @@
|
|||||||
|
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
|
|
||||||
#include "libmv/numeric/numeric.h"
|
|
||||||
#include "libmv/multiview/projection.h"
|
|
||||||
#include "libmv/multiview/fundamental.h"
|
#include "libmv/multiview/fundamental.h"
|
||||||
|
#include "libmv/multiview/projection.h"
|
||||||
|
#include "libmv/numeric/numeric.h"
|
||||||
|
|
||||||
namespace libmv {
|
namespace libmv {
|
||||||
|
|
||||||
TwoViewDataSet TwoRealisticCameras(bool same_K) {
|
TwoViewDataSet TwoRealisticCameras(bool same_K) {
|
||||||
TwoViewDataSet d;
|
TwoViewDataSet d;
|
||||||
|
|
||||||
|
// clang-format off
|
||||||
d.K1 << 320, 0, 160,
|
d.K1 << 320, 0, 160,
|
||||||
0, 320, 120,
|
0, 320, 120,
|
||||||
0, 0, 1;
|
0, 0, 1;
|
||||||
|
// clang-format on
|
||||||
if (same_K) {
|
if (same_K) {
|
||||||
d.K2 = d.K1;
|
d.K2 = d.K1;
|
||||||
} else {
|
} else {
|
||||||
|
// clang-format off
|
||||||
d.K2 << 360, 0, 170,
|
d.K2 << 360, 0, 170,
|
||||||
0, 360, 110,
|
0, 360, 110,
|
||||||
0, 0, 1;
|
0, 0, 1;
|
||||||
|
// clang-format on
|
||||||
}
|
}
|
||||||
d.R1 = RotationAroundZ(-0.1);
|
d.R1 = RotationAroundZ(-0.1);
|
||||||
d.R2 = RotationAroundX(-0.1);
|
d.R2 = RotationAroundX(-0.1);
|
||||||
@@ -59,10 +63,8 @@ TwoViewDataSet TwoRealisticCameras(bool same_K) {
|
|||||||
return d;
|
return d;
|
||||||
}
|
}
|
||||||
|
|
||||||
nViewDatasetConfigator::nViewDatasetConfigator(int fx , int fy,
|
nViewDatasetConfigator::nViewDatasetConfigator(
|
||||||
int cx, int cy,
|
int fx, int fy, int cx, int cy, double distance, double jitter_amount) {
|
||||||
double distance,
|
|
||||||
double jitter_amount) {
|
|
||||||
_fx = fx;
|
_fx = fx;
|
||||||
_fy = fy;
|
_fy = fy;
|
||||||
_cx = cx;
|
_cx = cx;
|
||||||
@@ -71,7 +73,8 @@ nViewDatasetConfigator::nViewDatasetConfigator(int fx , int fy,
|
|||||||
_jitter_amount = jitter_amount;
|
_jitter_amount = jitter_amount;
|
||||||
}
|
}
|
||||||
|
|
||||||
NViewDataSet NRealisticCamerasFull(int nviews, int npoints,
|
NViewDataSet NRealisticCamerasFull(int nviews,
|
||||||
|
int npoints,
|
||||||
const nViewDatasetConfigator config) {
|
const nViewDatasetConfigator config) {
|
||||||
NViewDataSet d;
|
NViewDataSet d;
|
||||||
d.n = nviews;
|
d.n = nviews;
|
||||||
@@ -102,9 +105,11 @@ NViewDataSet NRealisticCamerasFull(int nviews, int npoints,
|
|||||||
jitter *= config._jitter_amount / camera_center.norm();
|
jitter *= config._jitter_amount / camera_center.norm();
|
||||||
lookdir = -camera_center + jitter;
|
lookdir = -camera_center + jitter;
|
||||||
|
|
||||||
|
// clang-format off
|
||||||
d.K[i] << config._fx, 0, config._cx,
|
d.K[i] << config._fx, 0, config._cx,
|
||||||
0, config._fy, config._cy,
|
0, config._fy, config._cy,
|
||||||
0, 0, 1;
|
0, 0, 1;
|
||||||
|
// clang-format on
|
||||||
d.R[i] = LookAt(lookdir);
|
d.R[i] = LookAt(lookdir);
|
||||||
d.t[i] = -d.R[i] * camera_center;
|
d.t[i] = -d.R[i] * camera_center;
|
||||||
d.x[i] = Project(d.P(i), d.X);
|
d.x[i] = Project(d.P(i), d.X);
|
||||||
@@ -113,9 +118,10 @@ NViewDataSet NRealisticCamerasFull(int nviews, int npoints,
|
|||||||
return d;
|
return d;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
NViewDataSet NRealisticCamerasSparse(int nviews,
|
||||||
NViewDataSet NRealisticCamerasSparse(int nviews, int npoints,
|
int npoints,
|
||||||
float view_ratio, unsigned min_projections,
|
float view_ratio,
|
||||||
|
unsigned min_projections,
|
||||||
const nViewDatasetConfigator config) {
|
const nViewDatasetConfigator config) {
|
||||||
assert(view_ratio <= 1.0);
|
assert(view_ratio <= 1.0);
|
||||||
assert(view_ratio > 0.0);
|
assert(view_ratio > 0.0);
|
||||||
@@ -174,9 +180,11 @@ NViewDataSet NRealisticCamerasSparse(int nviews, int npoints,
|
|||||||
jitter *= config._jitter_amount / camera_center.norm();
|
jitter *= config._jitter_amount / camera_center.norm();
|
||||||
lookdir = -camera_center + jitter;
|
lookdir = -camera_center + jitter;
|
||||||
|
|
||||||
|
// clang-format off
|
||||||
d.K[i] << config._fx, 0, config._cx,
|
d.K[i] << config._fx, 0, config._cx,
|
||||||
0, config._fy, config._cy,
|
0, config._fy, config._cy,
|
||||||
0, 0, 1;
|
0, 0, 1;
|
||||||
|
// clang-format on
|
||||||
d.R[i] = LookAt(lookdir);
|
d.R[i] = LookAt(lookdir);
|
||||||
d.t[i] = -d.R[i] * camera_center;
|
d.t[i] = -d.R[i] * camera_center;
|
||||||
j_visible = 0;
|
j_visible = 0;
|
||||||
@@ -192,5 +200,4 @@ NViewDataSet NRealisticCamerasSparse(int nviews, int npoints,
|
|||||||
return d;
|
return d;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
} // namespace libmv
|
} // namespace libmv
|
||||||
|
@@ -83,22 +83,26 @@ struct nViewDatasetConfigator {
|
|||||||
double _dist;
|
double _dist;
|
||||||
double _jitter_amount;
|
double _jitter_amount;
|
||||||
|
|
||||||
nViewDatasetConfigator(int fx = 1000, int fy = 1000,
|
nViewDatasetConfigator(int fx = 1000,
|
||||||
int cx = 500, int cy = 500,
|
int fy = 1000,
|
||||||
|
int cx = 500,
|
||||||
|
int cy = 500,
|
||||||
double distance = 1.5,
|
double distance = 1.5,
|
||||||
double jitter_amount = 0.01);
|
double jitter_amount = 0.01);
|
||||||
};
|
};
|
||||||
|
|
||||||
NViewDataSet NRealisticCamerasFull(int nviews, int npoints,
|
NViewDataSet NRealisticCamerasFull(
|
||||||
const nViewDatasetConfigator
|
int nviews,
|
||||||
config = nViewDatasetConfigator());
|
int npoints,
|
||||||
|
const nViewDatasetConfigator config = nViewDatasetConfigator());
|
||||||
|
|
||||||
// Generates sparse projections (not all points are projected)
|
// Generates sparse projections (not all points are projected)
|
||||||
NViewDataSet NRealisticCamerasSparse(int nviews, int npoints,
|
NViewDataSet NRealisticCamerasSparse(
|
||||||
|
int nviews,
|
||||||
|
int npoints,
|
||||||
float view_ratio = 0.6,
|
float view_ratio = 0.6,
|
||||||
unsigned min_projections = 3,
|
unsigned min_projections = 3,
|
||||||
const nViewDatasetConfigator
|
const nViewDatasetConfigator config = nViewDatasetConfigator());
|
||||||
config = nViewDatasetConfigator());
|
|
||||||
|
|
||||||
} // namespace libmv
|
} // namespace libmv
|
||||||
|
|
||||||
|
@@ -20,14 +20,16 @@
|
|||||||
|
|
||||||
#include "libmv/multiview/triangulation.h"
|
#include "libmv/multiview/triangulation.h"
|
||||||
|
|
||||||
#include "libmv/numeric/numeric.h"
|
|
||||||
#include "libmv/multiview/projection.h"
|
#include "libmv/multiview/projection.h"
|
||||||
|
#include "libmv/numeric/numeric.h"
|
||||||
|
|
||||||
namespace libmv {
|
namespace libmv {
|
||||||
|
|
||||||
// HZ 12.2 pag.312
|
// HZ 12.2 pag.312
|
||||||
void TriangulateDLT(const Mat34 &P1, const Vec2 &x1,
|
void TriangulateDLT(const Mat34& P1,
|
||||||
const Mat34 &P2, const Vec2 &x2,
|
const Vec2& x1,
|
||||||
|
const Mat34& P2,
|
||||||
|
const Vec2& x2,
|
||||||
Vec4* X_homogeneous) {
|
Vec4* X_homogeneous) {
|
||||||
Mat4 design;
|
Mat4 design;
|
||||||
for (int i = 0; i < 4; ++i) {
|
for (int i = 0; i < 4; ++i) {
|
||||||
@@ -39,8 +41,10 @@ void TriangulateDLT(const Mat34 &P1, const Vec2 &x1,
|
|||||||
Nullspace(&design, X_homogeneous);
|
Nullspace(&design, X_homogeneous);
|
||||||
}
|
}
|
||||||
|
|
||||||
void TriangulateDLT(const Mat34 &P1, const Vec2 &x1,
|
void TriangulateDLT(const Mat34& P1,
|
||||||
const Mat34 &P2, const Vec2 &x2,
|
const Vec2& x1,
|
||||||
|
const Mat34& P2,
|
||||||
|
const Vec2& x2,
|
||||||
Vec3* X_euclidean) {
|
Vec3* X_euclidean) {
|
||||||
Vec4 X_homogeneous;
|
Vec4 X_homogeneous;
|
||||||
TriangulateDLT(P1, x1, P2, x2, &X_homogeneous);
|
TriangulateDLT(P1, x1, P2, x2, &X_homogeneous);
|
||||||
|
@@ -25,12 +25,16 @@
|
|||||||
|
|
||||||
namespace libmv {
|
namespace libmv {
|
||||||
|
|
||||||
void TriangulateDLT(const Mat34 &P1, const Vec2 &x1,
|
void TriangulateDLT(const Mat34& P1,
|
||||||
const Mat34 &P2, const Vec2 &x2,
|
const Vec2& x1,
|
||||||
|
const Mat34& P2,
|
||||||
|
const Vec2& x2,
|
||||||
Vec4* X_homogeneous);
|
Vec4* X_homogeneous);
|
||||||
|
|
||||||
void TriangulateDLT(const Mat34 &P1, const Vec2 &x1,
|
void TriangulateDLT(const Mat34& P1,
|
||||||
const Mat34 &P2, const Vec2 &x2,
|
const Vec2& x1,
|
||||||
|
const Mat34& P2,
|
||||||
|
const Vec2& x2,
|
||||||
Vec3* X_euclidean);
|
Vec3* X_euclidean);
|
||||||
|
|
||||||
} // namespace libmv
|
} // namespace libmv
|
||||||
|
@@ -20,10 +20,10 @@
|
|||||||
|
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
|
||||||
#include "libmv/multiview/triangulation.h"
|
|
||||||
#include "libmv/multiview/fundamental.h"
|
#include "libmv/multiview/fundamental.h"
|
||||||
#include "libmv/multiview/projection.h"
|
#include "libmv/multiview/projection.h"
|
||||||
#include "libmv/multiview/test_data_sets.h"
|
#include "libmv/multiview/test_data_sets.h"
|
||||||
|
#include "libmv/multiview/triangulation.h"
|
||||||
#include "libmv/numeric/numeric.h"
|
#include "libmv/numeric/numeric.h"
|
||||||
#include "testing/testing.h"
|
#include "testing/testing.h"
|
||||||
|
|
||||||
|
@@ -99,9 +99,7 @@ struct IsotropicNormalizedSolver {
|
|||||||
//
|
//
|
||||||
// The fit routine must not clear existing entries in the vector of models; it
|
// The fit routine must not clear existing entries in the vector of models; it
|
||||||
// should append new solutions to the end.
|
// should append new solutions to the end.
|
||||||
template<typename SolverArg,
|
template <typename SolverArg, typename ErrorArg, typename ModelArg = Mat3>
|
||||||
typename ErrorArg,
|
|
||||||
typename ModelArg = Mat3>
|
|
||||||
class Kernel {
|
class Kernel {
|
||||||
public:
|
public:
|
||||||
Kernel(const Mat& x1, const Mat& x2) : x1_(x1), x2_(x2) {}
|
Kernel(const Mat& x1, const Mat& x2) : x1_(x1), x2_(x2) {}
|
||||||
@@ -118,13 +116,12 @@ class Kernel {
|
|||||||
static_cast<Vec>(x1_.col(sample)),
|
static_cast<Vec>(x1_.col(sample)),
|
||||||
static_cast<Vec>(x2_.col(sample)));
|
static_cast<Vec>(x2_.col(sample)));
|
||||||
}
|
}
|
||||||
int NumSamples() const {
|
int NumSamples() const { return x1_.cols(); }
|
||||||
return x1_.cols();
|
|
||||||
}
|
|
||||||
static void Solve(const Mat& x1, const Mat& x2, vector<Model>* models) {
|
static void Solve(const Mat& x1, const Mat& x2, vector<Model>* models) {
|
||||||
// By offering this, Kernel types can be passed to templates.
|
// By offering this, Kernel types can be passed to templates.
|
||||||
Solver::Solve(x1, x2, models);
|
Solver::Solve(x1, x2, models);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
const Mat& x1_;
|
const Mat& x1_;
|
||||||
const Mat& x2_;
|
const Mat& x2_;
|
||||||
|
@@ -32,9 +32,9 @@
|
|||||||
#include <cmath>
|
#include <cmath>
|
||||||
#include <cstdio>
|
#include <cstdio>
|
||||||
|
|
||||||
#include "libmv/numeric/numeric.h"
|
|
||||||
#include "libmv/numeric/function_derivative.h"
|
|
||||||
#include "libmv/logging/logging.h"
|
#include "libmv/logging/logging.h"
|
||||||
|
#include "libmv/numeric/function_derivative.h"
|
||||||
|
#include "libmv/numeric/numeric.h"
|
||||||
|
|
||||||
namespace libmv {
|
namespace libmv {
|
||||||
|
|
||||||
@@ -51,10 +51,12 @@ class Dogleg {
|
|||||||
typedef typename Function::XMatrixType Parameters;
|
typedef typename Function::XMatrixType Parameters;
|
||||||
typedef Matrix<typename Function::FMatrixType::RealScalar,
|
typedef Matrix<typename Function::FMatrixType::RealScalar,
|
||||||
Function::FMatrixType::RowsAtCompileTime,
|
Function::FMatrixType::RowsAtCompileTime,
|
||||||
Function::XMatrixType::RowsAtCompileTime> JMatrixType;
|
Function::XMatrixType::RowsAtCompileTime>
|
||||||
|
JMatrixType;
|
||||||
typedef Matrix<typename JMatrixType::RealScalar,
|
typedef Matrix<typename JMatrixType::RealScalar,
|
||||||
JMatrixType::ColsAtCompileTime,
|
JMatrixType::ColsAtCompileTime,
|
||||||
JMatrixType::ColsAtCompileTime> AMatrixType;
|
JMatrixType::ColsAtCompileTime>
|
||||||
|
AMatrixType;
|
||||||
|
|
||||||
enum Status {
|
enum Status {
|
||||||
RUNNING,
|
RUNNING,
|
||||||
@@ -71,8 +73,7 @@ class Dogleg {
|
|||||||
STEEPEST_DESCENT,
|
STEEPEST_DESCENT,
|
||||||
};
|
};
|
||||||
|
|
||||||
Dogleg(const Function &f)
|
Dogleg(const Function& f) : f_(f), df_(f) {}
|
||||||
: f_(f), df_(f) {}
|
|
||||||
|
|
||||||
struct SolverParameters {
|
struct SolverParameters {
|
||||||
SolverParameters()
|
SolverParameters()
|
||||||
@@ -95,10 +96,15 @@ class Dogleg {
|
|||||||
Status status;
|
Status status;
|
||||||
};
|
};
|
||||||
|
|
||||||
Status Update(const Parameters &x, const SolverParameters ¶ms,
|
Status Update(const Parameters& x,
|
||||||
JMatrixType *J, AMatrixType *A, FVec *error, Parameters *g) {
|
const SolverParameters& params,
|
||||||
|
JMatrixType* J,
|
||||||
|
AMatrixType* A,
|
||||||
|
FVec* error,
|
||||||
|
Parameters* g) {
|
||||||
*J = df_(x);
|
*J = df_(x);
|
||||||
// TODO(keir): In the case of m = n, avoid computing A and just do J^-1 directly.
|
// TODO(keir): In the case of m = n, avoid computing A and just do J^-1
|
||||||
|
// directly.
|
||||||
*A = (*J).transpose() * (*J);
|
*A = (*J).transpose() * (*J);
|
||||||
*error = f_(x);
|
*error = f_(x);
|
||||||
*g = (*J).transpose() * *error;
|
*g = (*J).transpose() * *error;
|
||||||
@@ -137,8 +143,7 @@ class Dogleg {
|
|||||||
if (c <= 0) {
|
if (c <= 0) {
|
||||||
*beta = (-c + sqrt(c * c + Mbma2 * (radius2 - Ma2))) / (Mbma2);
|
*beta = (-c + sqrt(c * c + Mbma2 * (radius2 - Ma2))) / (Mbma2);
|
||||||
} else {
|
} else {
|
||||||
*beta = (radius2 - Ma2) /
|
*beta = (radius2 - Ma2) / (c + sqrt(c * c + Mbma2 * (radius2 - Ma2)));
|
||||||
(c + sqrt(c*c + Mbma2*(radius2 - Ma2)));
|
|
||||||
}
|
}
|
||||||
*dx_dl = alpha * dx_sd + (*beta) * (dx_gn - alpha * dx_sd);
|
*dx_dl = alpha * dx_sd + (*beta) * (dx_gn - alpha * dx_sd);
|
||||||
return DOGLEG;
|
return DOGLEG;
|
||||||
@@ -171,7 +176,10 @@ class Dogleg {
|
|||||||
int i = 0;
|
int i = 0;
|
||||||
for (; results.status == RUNNING && i < params.max_iterations; ++i) {
|
for (; results.status == RUNNING && i < params.max_iterations; ++i) {
|
||||||
printf("%9d %12g %12g %12g",
|
printf("%9d %12g %12g %12g",
|
||||||
i, f_(x).norm(), g.array().abs().maxCoeff(), radius);
|
i,
|
||||||
|
f_(x).norm(),
|
||||||
|
g.array().abs().maxCoeff(),
|
||||||
|
radius);
|
||||||
|
|
||||||
// LG << "iteration: " << i;
|
// LG << "iteration: " << i;
|
||||||
// LG << "||f(x)||: " << f_(x).norm();
|
// LG << "||f(x)||: " << f_(x).norm();
|
||||||
@@ -199,8 +207,8 @@ class Dogleg {
|
|||||||
|
|
||||||
// Solve for dogleg direction dx_dl.
|
// Solve for dogleg direction dx_dl.
|
||||||
Scalar beta = 0;
|
Scalar beta = 0;
|
||||||
Step step = SolveDoglegDirection(dx_sd, dx_gn, radius, alpha,
|
Step step =
|
||||||
&dx_dl, &beta);
|
SolveDoglegDirection(dx_sd, dx_gn, radius, alpha, &dx_dl, &beta);
|
||||||
|
|
||||||
Scalar e3 = params.relative_step_threshold;
|
Scalar e3 = params.relative_step_threshold;
|
||||||
if (dx_dl.norm() < e3 * (x.norm() + e3)) {
|
if (dx_dl.norm() < e3 * (x.norm() + e3)) {
|
||||||
@@ -221,9 +229,12 @@ class Dogleg {
|
|||||||
}
|
}
|
||||||
Scalar rho = actual / predicted;
|
Scalar rho = actual / predicted;
|
||||||
|
|
||||||
if (step == GAUSS_NEWTON) printf(" GAUSS");
|
if (step == GAUSS_NEWTON)
|
||||||
if (step == STEEPEST_DESCENT) printf(" STEE");
|
printf(" GAUSS");
|
||||||
if (step == DOGLEG) printf(" DOGL");
|
if (step == STEEPEST_DESCENT)
|
||||||
|
printf(" STEE");
|
||||||
|
if (step == DOGLEG)
|
||||||
|
printf(" DOGL");
|
||||||
|
|
||||||
printf(" %12g %12g %12g\n", rho, actual, predicted);
|
printf(" %12g %12g %12g\n", rho, actual, predicted);
|
||||||
|
|
||||||
@@ -256,6 +267,6 @@ class Dogleg {
|
|||||||
Jacobian df_;
|
Jacobian df_;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace mv
|
} // namespace libmv
|
||||||
|
|
||||||
#endif // LIBMV_NUMERIC_DOGLEG_H
|
#endif // LIBMV_NUMERIC_DOGLEG_H
|
||||||
|
@@ -18,8 +18,8 @@
|
|||||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||||
// IN THE SOFTWARE.
|
// IN THE SOFTWARE.
|
||||||
|
|
||||||
#include "testing/testing.h"
|
|
||||||
#include "libmv/numeric/dogleg.h"
|
#include "libmv/numeric/dogleg.h"
|
||||||
|
#include "testing/testing.h"
|
||||||
|
|
||||||
using namespace libmv;
|
using namespace libmv;
|
||||||
|
|
||||||
@@ -33,22 +33,22 @@ class F {
|
|||||||
double x1 = x.x() - 2;
|
double x1 = x.x() - 2;
|
||||||
double y1 = x.y() - 5;
|
double y1 = x.y() - 5;
|
||||||
double z1 = x.z();
|
double z1 = x.z();
|
||||||
Vec4 fx; fx << x1*x1 + z1*z1,
|
Vec4 fx;
|
||||||
y1*y1 + z1*z1,
|
fx << x1 * x1 + z1 * z1, y1 * y1 + z1 * z1, z1 * z1, x1 * x1;
|
||||||
z1*z1,
|
|
||||||
x1*x1;
|
|
||||||
return fx;
|
return fx;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
TEST(Dogleg, SimpleCase) {
|
TEST(Dogleg, SimpleCase) {
|
||||||
Vec3 x; x << 0.76026643, -30.01799744, 0.55192142;
|
Vec3 x;
|
||||||
|
x << 0.76026643, -30.01799744, 0.55192142;
|
||||||
F f;
|
F f;
|
||||||
Dogleg<F>::SolverParameters params;
|
Dogleg<F>::SolverParameters params;
|
||||||
Dogleg<F> lm(f);
|
Dogleg<F> lm(f);
|
||||||
/* TODO(sergey): Better error handling. */
|
/* TODO(sergey): Better error handling. */
|
||||||
/* Dogleg<F>::Results results = */ lm.minimize(params, &x);
|
/* Dogleg<F>::Results results = */ lm.minimize(params, &x);
|
||||||
Vec3 expected_min_x; expected_min_x << 2, 5, 0;
|
Vec3 expected_min_x;
|
||||||
|
expected_min_x << 2, 5, 0;
|
||||||
|
|
||||||
EXPECT_MATRIX_NEAR(expected_min_x, x, 1e-5);
|
EXPECT_MATRIX_NEAR(expected_min_x, x, 1e-5);
|
||||||
}
|
}
|
||||||
@@ -62,7 +62,8 @@ class F32 {
|
|||||||
Vec2 operator()(const Vec2& x) const {
|
Vec2 operator()(const Vec2& x) const {
|
||||||
double x1 = x(0);
|
double x1 = x(0);
|
||||||
double x2 = 10 * x(0) / (x(0) + 0.1) + 2 * x(1) * x(1);
|
double x2 = 10 * x(0) / (x(0) + 0.1) + 2 * x(1) * x(1);
|
||||||
Vec2 fx; fx << x1, x2;
|
Vec2 fx;
|
||||||
|
fx << x1, x2;
|
||||||
return fx;
|
return fx;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -71,8 +72,8 @@ class JF32 {
|
|||||||
public:
|
public:
|
||||||
JF32(const F32& f) { (void)f; }
|
JF32(const F32& f) { (void)f; }
|
||||||
Mat2 operator()(const Vec2& x) {
|
Mat2 operator()(const Vec2& x) {
|
||||||
Mat2 J; J << 1, 0,
|
Mat2 J;
|
||||||
1./pow(x(0) + 0.1, 2), 4*x(1)*x(1);
|
J << 1, 0, 1. / pow(x(0) + 0.1, 2), 4 * x(1) * x(1);
|
||||||
return J;
|
return J;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@@ -23,8 +23,8 @@
|
|||||||
|
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
|
|
||||||
#include "libmv/numeric/numeric.h"
|
|
||||||
#include "libmv/logging/logging.h"
|
#include "libmv/logging/logging.h"
|
||||||
|
#include "libmv/numeric/numeric.h"
|
||||||
|
|
||||||
namespace libmv {
|
namespace libmv {
|
||||||
|
|
||||||
@@ -87,6 +87,7 @@ class NumericJacobian {
|
|||||||
}
|
}
|
||||||
return jacobian;
|
return jacobian;
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
const Function& f_;
|
const Function& f_;
|
||||||
};
|
};
|
||||||
|
@@ -18,9 +18,9 @@
|
|||||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||||
// IN THE SOFTWARE.
|
// IN THE SOFTWARE.
|
||||||
|
|
||||||
#include "testing/testing.h"
|
|
||||||
#include "libmv/numeric/numeric.h"
|
|
||||||
#include "libmv/numeric/function_derivative.h"
|
#include "libmv/numeric/function_derivative.h"
|
||||||
|
#include "libmv/numeric/numeric.h"
|
||||||
|
#include "testing/testing.h"
|
||||||
|
|
||||||
using namespace libmv;
|
using namespace libmv;
|
||||||
|
|
||||||
@@ -38,14 +38,14 @@ class F {
|
|||||||
}
|
}
|
||||||
Mat23 J(const Vec3& x) const {
|
Mat23 J(const Vec3& x) const {
|
||||||
Mat23 jacobian;
|
Mat23 jacobian;
|
||||||
jacobian << 0.19, 2*0.19*x(1), 1.0,
|
jacobian << 0.19, 2 * 0.19 * x(1), 1.0, 3 * cos(x(0)), -2 * sin(x(1)), 0;
|
||||||
3*cos(x(0)), -2*sin(x(1)), 0;
|
|
||||||
return jacobian;
|
return jacobian;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
TEST(FunctionDerivative, SimpleCase) {
|
TEST(FunctionDerivative, SimpleCase) {
|
||||||
Vec3 x; x << 0.76026643, 0.01799744, 0.55192142;
|
Vec3 x;
|
||||||
|
x << 0.76026643, 0.01799744, 0.55192142;
|
||||||
F f;
|
F f;
|
||||||
NumericJacobian<F, CENTRAL> J(f);
|
NumericJacobian<F, CENTRAL> J(f);
|
||||||
EXPECT_MATRIX_NEAR(f.J(x), J(x), 1e-8);
|
EXPECT_MATRIX_NEAR(f.J(x), J(x), 1e-8);
|
||||||
|
@@ -31,9 +31,9 @@
|
|||||||
|
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
|
|
||||||
#include "libmv/numeric/numeric.h"
|
|
||||||
#include "libmv/numeric/function_derivative.h"
|
|
||||||
#include "libmv/logging/logging.h"
|
#include "libmv/logging/logging.h"
|
||||||
|
#include "libmv/numeric/function_derivative.h"
|
||||||
|
#include "libmv/numeric/numeric.h"
|
||||||
|
|
||||||
namespace libmv {
|
namespace libmv {
|
||||||
|
|
||||||
@@ -50,10 +50,12 @@ class LevenbergMarquardt {
|
|||||||
typedef typename Function::XMatrixType Parameters;
|
typedef typename Function::XMatrixType Parameters;
|
||||||
typedef Matrix<typename Function::FMatrixType::RealScalar,
|
typedef Matrix<typename Function::FMatrixType::RealScalar,
|
||||||
Function::FMatrixType::RowsAtCompileTime,
|
Function::FMatrixType::RowsAtCompileTime,
|
||||||
Function::XMatrixType::RowsAtCompileTime> JMatrixType;
|
Function::XMatrixType::RowsAtCompileTime>
|
||||||
|
JMatrixType;
|
||||||
typedef Matrix<typename JMatrixType::RealScalar,
|
typedef Matrix<typename JMatrixType::RealScalar,
|
||||||
JMatrixType::ColsAtCompileTime,
|
JMatrixType::ColsAtCompileTime,
|
||||||
JMatrixType::ColsAtCompileTime> AMatrixType;
|
JMatrixType::ColsAtCompileTime>
|
||||||
|
AMatrixType;
|
||||||
|
|
||||||
// TODO(keir): Some of these knobs can be derived from each other and
|
// TODO(keir): Some of these knobs can be derived from each other and
|
||||||
// removed, instead of requiring the user to set them.
|
// removed, instead of requiring the user to set them.
|
||||||
@@ -65,8 +67,7 @@ class LevenbergMarquardt {
|
|||||||
HIT_MAX_ITERATIONS,
|
HIT_MAX_ITERATIONS,
|
||||||
};
|
};
|
||||||
|
|
||||||
LevenbergMarquardt(const Function &f)
|
LevenbergMarquardt(const Function& f) : f_(f), df_(f) {}
|
||||||
: f_(f), df_(f) {}
|
|
||||||
|
|
||||||
struct SolverParameters {
|
struct SolverParameters {
|
||||||
SolverParameters()
|
SolverParameters()
|
||||||
@@ -89,8 +90,12 @@ class LevenbergMarquardt {
|
|||||||
Status status;
|
Status status;
|
||||||
};
|
};
|
||||||
|
|
||||||
Status Update(const Parameters &x, const SolverParameters ¶ms,
|
Status Update(const Parameters& x,
|
||||||
JMatrixType *J, AMatrixType *A, FVec *error, Parameters *g) {
|
const SolverParameters& params,
|
||||||
|
JMatrixType* J,
|
||||||
|
AMatrixType* A,
|
||||||
|
FVec* error,
|
||||||
|
Parameters* g) {
|
||||||
*J = df_(x);
|
*J = df_(x);
|
||||||
*A = (*J).transpose() * (*J);
|
*A = (*J).transpose() * (*J);
|
||||||
*error = -f_(x);
|
*error = -f_(x);
|
||||||
@@ -130,7 +135,8 @@ class LevenbergMarquardt {
|
|||||||
VLOG(3) << "u: " << u;
|
VLOG(3) << "u: " << u;
|
||||||
VLOG(3) << "v: " << v;
|
VLOG(3) << "v: " << v;
|
||||||
|
|
||||||
AMatrixType A_augmented = A + u*AMatrixType::Identity(J.cols(), J.cols());
|
AMatrixType A_augmented =
|
||||||
|
A + u * AMatrixType::Identity(J.cols(), J.cols());
|
||||||
Solver solver(A_augmented);
|
Solver solver(A_augmented);
|
||||||
dx = solver.solve(g);
|
dx = solver.solve(g);
|
||||||
bool solved = (A_augmented * dx).isApprox(g);
|
bool solved = (A_augmented * dx).isApprox(g);
|
||||||
@@ -146,8 +152,8 @@ class LevenbergMarquardt {
|
|||||||
// Rho is the ratio of the actual reduction in error to the reduction
|
// Rho is the ratio of the actual reduction in error to the reduction
|
||||||
// in error that would be obtained if the problem was linear.
|
// in error that would be obtained if the problem was linear.
|
||||||
// See [1] for details.
|
// See [1] for details.
|
||||||
Scalar rho((error.squaredNorm() - f_(x_new).squaredNorm())
|
Scalar rho((error.squaredNorm() - f_(x_new).squaredNorm()) /
|
||||||
/ dx.dot(u*dx + g));
|
dx.dot(u * dx + g));
|
||||||
if (rho > 0) {
|
if (rho > 0) {
|
||||||
// Accept the Gauss-Newton step because the linear model fits well.
|
// Accept the Gauss-Newton step because the linear model fits well.
|
||||||
x = x_new;
|
x = x_new;
|
||||||
@@ -178,6 +184,6 @@ class LevenbergMarquardt {
|
|||||||
Jacobian df_;
|
Jacobian df_;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace mv
|
} // namespace libmv
|
||||||
|
|
||||||
#endif // LIBMV_NUMERIC_LEVENBERG_MARQUARDT_H
|
#endif // LIBMV_NUMERIC_LEVENBERG_MARQUARDT_H
|
||||||
|
@@ -18,8 +18,8 @@
|
|||||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||||
// IN THE SOFTWARE.
|
// IN THE SOFTWARE.
|
||||||
|
|
||||||
#include "testing/testing.h"
|
|
||||||
#include "libmv/numeric/levenberg_marquardt.h"
|
#include "libmv/numeric/levenberg_marquardt.h"
|
||||||
|
#include "testing/testing.h"
|
||||||
|
|
||||||
using namespace libmv;
|
using namespace libmv;
|
||||||
|
|
||||||
@@ -33,10 +33,8 @@ class F {
|
|||||||
double x1 = x.x() - 2;
|
double x1 = x.x() - 2;
|
||||||
double y1 = x.y() - 5;
|
double y1 = x.y() - 5;
|
||||||
double z1 = x.z();
|
double z1 = x.z();
|
||||||
Vec4 fx; fx << x1*x1 + z1*z1,
|
Vec4 fx;
|
||||||
y1*y1 + z1*z1,
|
fx << x1 * x1 + z1 * z1, y1 * y1 + z1 * z1, z1 * z1, x1 * x1;
|
||||||
z1*z1,
|
|
||||||
x1*x1;
|
|
||||||
return fx;
|
return fx;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@@ -18,7 +18,6 @@
|
|||||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||||
// IN THE SOFTWARE.
|
// IN THE SOFTWARE.
|
||||||
|
|
||||||
|
|
||||||
#include "libmv/numeric/numeric.h"
|
#include "libmv/numeric/numeric.h"
|
||||||
|
|
||||||
namespace libmv {
|
namespace libmv {
|
||||||
@@ -27,9 +26,11 @@ Mat3 RotationAroundX(double angle) {
|
|||||||
double c, s;
|
double c, s;
|
||||||
sincos(angle, &s, &c);
|
sincos(angle, &s, &c);
|
||||||
Mat3 R;
|
Mat3 R;
|
||||||
|
// clang-format off
|
||||||
R << 1, 0, 0,
|
R << 1, 0, 0,
|
||||||
0, c, -s,
|
0, c, -s,
|
||||||
0, s, c;
|
0, s, c;
|
||||||
|
// clang-format on
|
||||||
return R;
|
return R;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -37,9 +38,11 @@ Mat3 RotationAroundY(double angle) {
|
|||||||
double c, s;
|
double c, s;
|
||||||
sincos(angle, &s, &c);
|
sincos(angle, &s, &c);
|
||||||
Mat3 R;
|
Mat3 R;
|
||||||
|
// clang-format off
|
||||||
R << c, 0, s,
|
R << c, 0, s,
|
||||||
0, 1, 0,
|
0, 1, 0,
|
||||||
-s, 0, c;
|
-s, 0, c;
|
||||||
|
// clang-format on
|
||||||
return R;
|
return R;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -47,13 +50,14 @@ Mat3 RotationAroundZ(double angle) {
|
|||||||
double c, s;
|
double c, s;
|
||||||
sincos(angle, &s, &c);
|
sincos(angle, &s, &c);
|
||||||
Mat3 R;
|
Mat3 R;
|
||||||
|
// clang-format off
|
||||||
R << c, -s, 0,
|
R << c, -s, 0,
|
||||||
s, c, 0,
|
s, c, 0,
|
||||||
0, 0, 1;
|
0, 0, 1;
|
||||||
|
// clang-format on
|
||||||
return R;
|
return R;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
Mat3 RotationRodrigues(const Vec3& axis) {
|
Mat3 RotationRodrigues(const Vec3& axis) {
|
||||||
double theta = axis.norm();
|
double theta = axis.norm();
|
||||||
Vec3 w = axis / theta;
|
Vec3 w = axis / theta;
|
||||||
@@ -62,7 +66,6 @@ Mat3 RotationRodrigues(const Vec3 &axis) {
|
|||||||
return Mat3::Identity() + sin(theta) * W + (1 - cos(theta)) * W * W;
|
return Mat3::Identity() + sin(theta) * W + (1 - cos(theta)) * W * W;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
Mat3 LookAt(Vec3 center) {
|
Mat3 LookAt(Vec3 center) {
|
||||||
Vec3 zc = center.normalized();
|
Vec3 zc = center.normalized();
|
||||||
Vec3 xc = Vec3::UnitY().cross(zc).normalized();
|
Vec3 xc = Vec3::UnitY().cross(zc).normalized();
|
||||||
@@ -76,9 +79,11 @@ Mat3 LookAt(Vec3 center) {
|
|||||||
|
|
||||||
Mat3 CrossProductMatrix(const Vec3& x) {
|
Mat3 CrossProductMatrix(const Vec3& x) {
|
||||||
Mat3 X;
|
Mat3 X;
|
||||||
|
// clang-format off
|
||||||
X << 0, -x(2), x(1),
|
X << 0, -x(2), x(1),
|
||||||
x(2), 0, -x(0),
|
x(2), 0, -x(0),
|
||||||
-x(1), x(0), 0;
|
-x(1), x(0), 0;
|
||||||
|
// clang-format on
|
||||||
return X;
|
return X;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -133,4 +138,3 @@ void MatrixColumn(const Mat &A, int i, Vec4 *v) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
} // namespace libmv
|
} // namespace libmv
|
||||||
|
|
||||||
|
@@ -34,9 +34,8 @@
|
|||||||
#include <Eigen/SVD>
|
#include <Eigen/SVD>
|
||||||
|
|
||||||
#if !defined(__MINGW64__)
|
#if !defined(__MINGW64__)
|
||||||
# if defined(_WIN32) || defined(__APPLE__) || \
|
# if defined(_WIN32) || defined(__APPLE__) || defined(__FreeBSD__) || \
|
||||||
defined(__FreeBSD__) || defined(__NetBSD__) || \
|
defined(__NetBSD__) || defined(__HAIKU__)
|
||||||
defined(__HAIKU__)
|
|
||||||
inline void sincos(double x, double* sinx, double* cosx) {
|
inline void sincos(double x, double* sinx, double* cosx) {
|
||||||
*sinx = sin(x);
|
*sinx = sin(x);
|
||||||
*cosx = cos(x);
|
*cosx = cos(x);
|
||||||
@@ -133,15 +132,13 @@ typedef Eigen::Vector2i Vec2i;
|
|||||||
typedef Eigen::Vector3i Vec3i;
|
typedef Eigen::Vector3i Vec3i;
|
||||||
typedef Eigen::Vector4i Vec4i;
|
typedef Eigen::Vector4i Vec4i;
|
||||||
|
|
||||||
typedef Eigen::Matrix<float,
|
typedef Eigen::Matrix<float, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor>
|
||||||
Eigen::Dynamic,
|
RMatf;
|
||||||
Eigen::Dynamic,
|
|
||||||
Eigen::RowMajor> RMatf;
|
|
||||||
|
|
||||||
typedef Eigen::NumTraits<double> EigenDouble;
|
typedef Eigen::NumTraits<double> EigenDouble;
|
||||||
|
|
||||||
using Eigen::Map;
|
|
||||||
using Eigen::Dynamic;
|
using Eigen::Dynamic;
|
||||||
|
using Eigen::Map;
|
||||||
using Eigen::Matrix;
|
using Eigen::Matrix;
|
||||||
|
|
||||||
// Find U, s, and VT such that
|
// Find U, s, and VT such that
|
||||||
@@ -294,7 +291,8 @@ void MeanAndVarianceAlongRows(const Mat &A,
|
|||||||
// TODO(bomboze): un-#if this for both platforms once tested under Windows
|
// TODO(bomboze): un-#if this for both platforms once tested under Windows
|
||||||
/* This solution was extensively discussed here
|
/* This solution was extensively discussed here
|
||||||
http://forum.kde.org/viewtopic.php?f=74&t=61940 */
|
http://forum.kde.org/viewtopic.php?f=74&t=61940 */
|
||||||
#define SUM_OR_DYNAMIC(x, y) (x == Eigen::Dynamic || y == Eigen::Dynamic) ? Eigen::Dynamic : (x+y)
|
# define SUM_OR_DYNAMIC(x, y) \
|
||||||
|
(x == Eigen::Dynamic || y == Eigen::Dynamic) ? Eigen::Dynamic : (x + y)
|
||||||
|
|
||||||
template <typename Derived1, typename Derived2>
|
template <typename Derived1, typename Derived2>
|
||||||
struct hstack_return {
|
struct hstack_return {
|
||||||
@@ -313,12 +311,13 @@ void MeanAndVarianceAlongRows(const Mat &A,
|
|||||||
ColsAtCompileTime,
|
ColsAtCompileTime,
|
||||||
Options,
|
Options,
|
||||||
MaxRowsAtCompileTime,
|
MaxRowsAtCompileTime,
|
||||||
MaxColsAtCompileTime> type;
|
MaxColsAtCompileTime>
|
||||||
|
type;
|
||||||
};
|
};
|
||||||
|
|
||||||
template <typename Derived1, typename Derived2>
|
template <typename Derived1, typename Derived2>
|
||||||
typename hstack_return<Derived1, Derived2>::type
|
typename hstack_return<Derived1, Derived2>::type HStack(
|
||||||
HStack(const Eigen::MatrixBase<Derived1>& lhs,
|
const Eigen::MatrixBase<Derived1>& lhs,
|
||||||
const Eigen::MatrixBase<Derived2>& rhs) {
|
const Eigen::MatrixBase<Derived2>& rhs) {
|
||||||
typename hstack_return<Derived1, Derived2>::type res;
|
typename hstack_return<Derived1, Derived2>::type res;
|
||||||
res.resize(lhs.rows(), lhs.cols() + rhs.cols());
|
res.resize(lhs.rows(), lhs.cols() + rhs.cols());
|
||||||
@@ -326,7 +325,6 @@ void MeanAndVarianceAlongRows(const Mat &A,
|
|||||||
return res;
|
return res;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
template <typename Derived1, typename Derived2>
|
template <typename Derived1, typename Derived2>
|
||||||
struct vstack_return {
|
struct vstack_return {
|
||||||
typedef typename Derived1::Scalar Scalar;
|
typedef typename Derived1::Scalar Scalar;
|
||||||
@@ -344,12 +342,13 @@ void MeanAndVarianceAlongRows(const Mat &A,
|
|||||||
ColsAtCompileTime,
|
ColsAtCompileTime,
|
||||||
Options,
|
Options,
|
||||||
MaxRowsAtCompileTime,
|
MaxRowsAtCompileTime,
|
||||||
MaxColsAtCompileTime> type;
|
MaxColsAtCompileTime>
|
||||||
|
type;
|
||||||
};
|
};
|
||||||
|
|
||||||
template <typename Derived1, typename Derived2>
|
template <typename Derived1, typename Derived2>
|
||||||
typename vstack_return<Derived1, Derived2>::type
|
typename vstack_return<Derived1, Derived2>::type VStack(
|
||||||
VStack(const Eigen::MatrixBase<Derived1>& lhs,
|
const Eigen::MatrixBase<Derived1>& lhs,
|
||||||
const Eigen::MatrixBase<Derived2>& rhs) {
|
const Eigen::MatrixBase<Derived2>& rhs) {
|
||||||
typename vstack_return<Derived1, Derived2>::type res;
|
typename vstack_return<Derived1, Derived2>::type res;
|
||||||
res.resize(lhs.rows() + rhs.rows(), lhs.cols());
|
res.resize(lhs.rows() + rhs.rows(), lhs.cols());
|
||||||
@@ -357,29 +356,25 @@ void MeanAndVarianceAlongRows(const Mat &A,
|
|||||||
return res;
|
return res;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
#else // _WIN32
|
#else // _WIN32
|
||||||
|
|
||||||
// Since it is not possible to typedef privately here, use a macro.
|
// Since it is not possible to typedef privately here, use a macro.
|
||||||
// Always take dynamic columns if either side is dynamic.
|
// Always take dynamic columns if either side is dynamic.
|
||||||
# define COLS \
|
# define COLS \
|
||||||
((ColsLeft == Eigen::Dynamic || ColsRight == Eigen::Dynamic) \
|
((ColsLeft == Eigen::Dynamic || ColsRight == Eigen::Dynamic) \
|
||||||
? Eigen::Dynamic : (ColsLeft + ColsRight))
|
? Eigen::Dynamic \
|
||||||
|
: (ColsLeft + ColsRight))
|
||||||
|
|
||||||
// Same as above, except that prefer fixed size if either is fixed.
|
// Same as above, except that prefer fixed size if either is fixed.
|
||||||
# define ROWS \
|
# define ROWS \
|
||||||
((RowsLeft == Eigen::Dynamic && RowsRight == Eigen::Dynamic) \
|
((RowsLeft == Eigen::Dynamic && RowsRight == Eigen::Dynamic) \
|
||||||
? Eigen::Dynamic \
|
? Eigen::Dynamic \
|
||||||
: ((RowsLeft == Eigen::Dynamic) \
|
: ((RowsLeft == Eigen::Dynamic) ? RowsRight : RowsLeft))
|
||||||
? RowsRight \
|
|
||||||
: RowsLeft \
|
|
||||||
) \
|
|
||||||
)
|
|
||||||
|
|
||||||
// TODO(keir): Add a static assert if both rows are at compiletime.
|
// TODO(keir): Add a static assert if both rows are at compiletime.
|
||||||
template <typename T, int RowsLeft, int RowsRight, int ColsLeft, int ColsRight>
|
template <typename T, int RowsLeft, int RowsRight, int ColsLeft, int ColsRight>
|
||||||
Eigen::Matrix<T, ROWS, COLS>
|
Eigen::Matrix<T, ROWS, COLS> HStack(
|
||||||
HStack(const Eigen::Matrix<T, RowsLeft, ColsLeft> &left,
|
const Eigen::Matrix<T, RowsLeft, ColsLeft>& left,
|
||||||
const Eigen::Matrix<T, RowsRight, ColsRight>& right) {
|
const Eigen::Matrix<T, RowsRight, ColsRight>& right) {
|
||||||
assert(left.rows() == right.rows());
|
assert(left.rows() == right.rows());
|
||||||
int n = left.rows();
|
int n = left.rows();
|
||||||
@@ -398,8 +393,8 @@ void MeanAndVarianceAlongRows(const Mat &A,
|
|||||||
// TODO(keir): Mail eigen list about making this work for general expressions
|
// TODO(keir): Mail eigen list about making this work for general expressions
|
||||||
// rather than only matrix types.
|
// rather than only matrix types.
|
||||||
template <typename T, int RowsLeft, int RowsRight, int ColsLeft, int ColsRight>
|
template <typename T, int RowsLeft, int RowsRight, int ColsLeft, int ColsRight>
|
||||||
Eigen::Matrix<T, COLS, ROWS>
|
Eigen::Matrix<T, COLS, ROWS> VStack(
|
||||||
VStack(const Eigen::Matrix<T, ColsLeft, RowsLeft> &top,
|
const Eigen::Matrix<T, ColsLeft, RowsLeft>& top,
|
||||||
const Eigen::Matrix<T, ColsRight, RowsRight>& bottom) {
|
const Eigen::Matrix<T, ColsRight, RowsRight>& bottom) {
|
||||||
assert(top.cols() == bottom.cols());
|
assert(top.cols() == bottom.cols());
|
||||||
int n1 = top.rows();
|
int n1 = top.rows();
|
||||||
@@ -415,8 +410,6 @@ void MeanAndVarianceAlongRows(const Mat &A,
|
|||||||
# undef ROWS
|
# undef ROWS
|
||||||
#endif // _WIN32
|
#endif // _WIN32
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
void HorizontalStack(const Mat& left, const Mat& right, Mat* stacked);
|
void HorizontalStack(const Mat& left, const Mat& right, Mat* stacked);
|
||||||
|
|
||||||
template <typename TTop, typename TBot, typename TStacked>
|
template <typename TTop, typename TBot, typename TStacked>
|
||||||
@@ -474,17 +467,14 @@ FloatType ceil0(const FloatType& value) {
|
|||||||
/// Returns the skew anti-symmetric matrix of a vector
|
/// Returns the skew anti-symmetric matrix of a vector
|
||||||
inline Mat3 SkewMat(const Vec3& x) {
|
inline Mat3 SkewMat(const Vec3& x) {
|
||||||
Mat3 skew;
|
Mat3 skew;
|
||||||
skew << 0 , -x(2), x(1),
|
skew << 0, -x(2), x(1), x(2), 0, -x(0), -x(1), x(0), 0;
|
||||||
x(2), 0 , -x(0),
|
|
||||||
-x(1), x(0), 0;
|
|
||||||
return skew;
|
return skew;
|
||||||
}
|
}
|
||||||
/// Returns the skew anti-symmetric matrix of a vector with only
|
/// Returns the skew anti-symmetric matrix of a vector with only
|
||||||
/// the first two (independent) lines
|
/// the first two (independent) lines
|
||||||
inline Mat23 SkewMatMinimal(const Vec2& x) {
|
inline Mat23 SkewMatMinimal(const Vec2& x) {
|
||||||
Mat23 skew;
|
Mat23 skew;
|
||||||
skew << 0, -1, x(1),
|
skew << 0, -1, x(1), 1, 0, -x(0);
|
||||||
1, 0, -x(0);
|
|
||||||
return skew;
|
return skew;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -496,7 +486,8 @@ inline Mat3 RotationFromEulerVector(Vec3 euler_vector) {
|
|||||||
}
|
}
|
||||||
Vec3 w = euler_vector / theta;
|
Vec3 w = euler_vector / theta;
|
||||||
Mat3 w_hat = CrossProductMatrix(w);
|
Mat3 w_hat = CrossProductMatrix(w);
|
||||||
return Mat3::Identity() + w_hat*sin(theta) + w_hat*w_hat*(1 - cos(theta));
|
return Mat3::Identity() + w_hat * sin(theta) +
|
||||||
|
w_hat * w_hat * (1 - cos(theta));
|
||||||
}
|
}
|
||||||
} // namespace libmv
|
} // namespace libmv
|
||||||
|
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user