Skip to content

Commit 26dd2b9

Browse files
author
Fabien Servant
committed
Adding Image groups
1 parent 3a7d6e1 commit 26dd2b9

File tree

22 files changed

+810
-10
lines changed

22 files changed

+810
-10
lines changed

meshroom/aliceVision/CameraInit.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -272,6 +272,12 @@
272272
label="Undistortion Params",
273273
description="Undistortion parameters."
274274
),
275+
desc.BoolParam(
276+
name="isSequence",
277+
label="Images are a sequence",
278+
description="The images provided as input are part of a sequence with temporal coherency.",
279+
value=False,
280+
),
275281
desc.BoolParam(
276282
name="locked",
277283
label="Locked",

meshroom/aliceVision/ListImages.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,12 @@ class ListImages(desc.AVCommandLineNode):
2323
description="Set of paths to image files and/or folders.",
2424
exposed=True,
2525
),
26+
desc.BoolParam(
27+
name="isSequence",
28+
label="Images are a sequence",
29+
description="The images provided as input are part of a sequence with temporal coherency.",
30+
value=False,
31+
),
2632
desc.ChoiceParam(
2733
name="verboseLevel",
2834
label="Verbose Level",

pyTests/sfmData/test_imagegroup.py

Lines changed: 262 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,262 @@
1+
"""
2+
Collection of unit tests for the ImageGroup, ImageSet, and ImageSequence classes.
3+
"""
4+
5+
from pyalicevision import sfmData as av
6+
7+
8+
def test_imagegroup_type_to_string():
9+
""" Test converting ImageGroup Type enum to string. """
10+
# Get Type enum value using stringToType first
11+
type_value = av.ImageGroup.stringToType("ImageSet")
12+
type_str = av.ImageGroup.typeToString(type_value)
13+
14+
assert isinstance(type_str, str), "typeToString should return a string"
15+
assert type_str == "ImageSet", "Type ImageSet should convert to 'ImageSet'"
16+
17+
18+
def test_imagegroup_string_to_type():
19+
""" Test converting string to ImageGroup Type enum. """
20+
type_value = av.ImageGroup.stringToType("ImageSet")
21+
22+
# Verify it returns a valid type object
23+
assert type_value is not None, "stringToType should return a valid type"
24+
# Verify round-trip conversion
25+
type_str = av.ImageGroup.typeToString(type_value)
26+
assert type_str == "ImageSet", "Round-trip conversion should preserve type"
27+
28+
29+
def test_imagegroup_create_factory():
30+
""" Test creating ImageGroup using the factory method. """
31+
type_value = av.ImageGroup.stringToType("ImageSet")
32+
image_group = av.ImageGroup.create(type_value)
33+
34+
assert image_group is not None, "Factory should create a valid ImageGroup object"
35+
# Verify the type by converting back to string
36+
actual_type_str = av.ImageGroup.typeToString(image_group.getType())
37+
assert actual_type_str == "ImageSet", "Created ImageGroup should have Type ImageSet"
38+
39+
40+
def test_imageset_default_constructor():
41+
""" Test creating an ImageSet object with default parameters. """
42+
image_set = av.ImageSet()
43+
44+
assert image_set is not None, "ImageSet default constructor should succeed"
45+
# Verify the type by converting to string
46+
type_str = av.ImageGroup.typeToString(image_set.getType())
47+
assert type_str == "ImageSet", "ImageSet should have Type ImageSet"
48+
49+
50+
def test_imageset_get_type():
51+
""" Test getting the type of an ImageSet object. """
52+
image_set = av.ImageSet()
53+
type_value = image_set.getType()
54+
55+
# Verify by converting to string
56+
type_str = av.ImageGroup.typeToString(type_value)
57+
assert type_str == "ImageSet", "ImageSet.getType() should return ImageSet type"
58+
59+
60+
def test_imageset_clone():
61+
""" Test cloning an ImageSet object. """
62+
image_set1 = av.ImageSet()
63+
image_set2 = image_set1.clone()
64+
65+
assert image_set2 is not None, "Clone should return a valid object"
66+
assert image_set1 == image_set2, "Cloned ImageSet should be equal to the original"
67+
assert image_set1.getType() == image_set2.getType(), \
68+
"Cloned ImageSet should have the same type"
69+
70+
71+
def test_imageset_equality():
72+
""" Test comparing two ImageSet objects using the '==' operator. """
73+
image_set1 = av.ImageSet()
74+
image_set2 = av.ImageSet()
75+
76+
assert image_set1 == image_set2, "Two default ImageSet objects should be equal"
77+
78+
79+
def test_imageset_inequality():
80+
""" Test comparing two ImageSet objects using the '!=' operator. """
81+
image_set1 = av.ImageSet()
82+
image_set2 = av.ImageSet()
83+
84+
# Since ImageSet currently has no distinguishing properties,
85+
# two default instances are equal
86+
assert not (image_set1 != image_set2), \
87+
"Two default ImageSet objects should not be unequal"
88+
89+
90+
def test_imagegroup_polymorphism():
91+
""" Test using ImageSet through ImageGroup interface (polymorphism). """
92+
# Create via factory
93+
type_value = av.ImageGroup.stringToType("ImageSet")
94+
image_group = av.ImageGroup.create(type_value)
95+
96+
# Should be able to call ImageGroup methods
97+
type_str = av.ImageGroup.typeToString(image_group.getType())
98+
assert type_str == "ImageSet", "Polymorphic getType() should work correctly"
99+
100+
# Clone should work through base class interface
101+
cloned = image_group.clone()
102+
assert cloned is not None, "Polymorphic clone should work"
103+
cloned_type_str = av.ImageGroup.typeToString(cloned.getType())
104+
assert cloned_type_str == "ImageSet", "Cloned object should have correct type"
105+
106+
107+
def test_imagegroup_type_conversion_roundtrip():
108+
""" Test that type conversion works correctly in both directions. """
109+
# String to Type to String
110+
original_str = "ImageSet"
111+
type_value = av.ImageGroup.stringToType(original_str)
112+
result_str = av.ImageGroup.typeToString(type_value)
113+
114+
assert result_str == original_str, "Round-trip type conversion should preserve the type name"
115+
116+
117+
def test_imageset_from_factory_equality():
118+
""" Test that ImageSets created different ways are equal. """
119+
image_set_direct = av.ImageSet()
120+
type_value = av.ImageGroup.stringToType("ImageSet")
121+
image_set_factory = av.ImageGroup.create(type_value)
122+
123+
# Both should be equal since they have the same type
124+
assert image_set_direct == image_set_factory, \
125+
"ImageSet created directly should equal one created via factory"
126+
127+
128+
# ImageSequence Tests
129+
130+
def test_imagesequence_type_to_string():
131+
""" Test converting ImageSequence Type enum to string. """
132+
type_value = av.ImageGroup.stringToType("ImageSequence")
133+
type_str = av.ImageGroup.typeToString(type_value)
134+
135+
assert isinstance(type_str, str), "typeToString should return a string"
136+
assert type_str == "ImageSequence", "Type ImageSequence should convert to 'ImageSequence'"
137+
138+
139+
def test_imagesequence_string_to_type():
140+
""" Test converting string to ImageSequence Type enum. """
141+
type_value = av.ImageGroup.stringToType("ImageSequence")
142+
143+
# Verify it returns a valid type object
144+
assert type_value is not None, "stringToType should return a valid type"
145+
# Verify round-trip conversion
146+
type_str = av.ImageGroup.typeToString(type_value)
147+
assert type_str == "ImageSequence", "Round-trip conversion should preserve type"
148+
149+
150+
def test_imagesequence_default_constructor():
151+
""" Test creating an ImageSequence object with default parameters. """
152+
image_seq = av.ImageSequence()
153+
154+
assert image_seq is not None, "ImageSequence default constructor should succeed"
155+
# Verify the type by converting to string
156+
type_str = av.ImageGroup.typeToString(image_seq.getType())
157+
assert type_str == "ImageSequence", "ImageSequence should have Type ImageSequence"
158+
159+
160+
def test_imagesequence_get_type():
161+
""" Test getting the type of an ImageSequence object. """
162+
image_seq = av.ImageSequence()
163+
type_value = image_seq.getType()
164+
165+
# Verify by converting to string
166+
type_str = av.ImageGroup.typeToString(type_value)
167+
assert type_str == "ImageSequence", "ImageSequence.getType() should return ImageSequence type"
168+
169+
170+
def test_imagesequence_clone():
171+
""" Test cloning an ImageSequence object. """
172+
image_seq1 = av.ImageSequence()
173+
image_seq2 = image_seq1.clone()
174+
175+
assert image_seq2 is not None, "Clone should return a valid object"
176+
assert image_seq1 == image_seq2, "Cloned ImageSequence should be equal to the original"
177+
assert image_seq1.getType() == image_seq2.getType(), \
178+
"Cloned ImageSequence should have the same type"
179+
180+
181+
def test_imagesequence_equality():
182+
""" Test comparing two ImageSequence objects using the '==' operator. """
183+
image_seq1 = av.ImageSequence()
184+
image_seq2 = av.ImageSequence()
185+
186+
assert image_seq1 == image_seq2, "Two default ImageSequence objects should be equal"
187+
188+
189+
def test_imagesequence_inequality():
190+
""" Test comparing two ImageSequence objects using the '!=' operator. """
191+
image_seq1 = av.ImageSequence()
192+
image_seq2 = av.ImageSequence()
193+
194+
# Since ImageSequence currently has no distinguishing properties,
195+
# two default instances are equal
196+
assert not (image_seq1 != image_seq2), \
197+
"Two default ImageSequence objects should not be unequal"
198+
199+
200+
def test_imagesequence_from_factory():
201+
""" Test creating ImageSequence using the factory method. """
202+
type_value = av.ImageGroup.stringToType("ImageSequence")
203+
image_seq = av.ImageGroup.create(type_value)
204+
205+
assert image_seq is not None, "Factory should create a valid ImageSequence object"
206+
# Verify the type by converting back to string
207+
actual_type_str = av.ImageGroup.typeToString(image_seq.getType())
208+
assert actual_type_str == "ImageSequence", "Created ImageGroup should have Type ImageSequence"
209+
210+
211+
def test_imagesequence_from_factory_equality():
212+
""" Test that ImageSequences created different ways are equal. """
213+
image_seq_direct = av.ImageSequence()
214+
type_value = av.ImageGroup.stringToType("ImageSequence")
215+
image_seq_factory = av.ImageGroup.create(type_value)
216+
217+
# Both should be equal since they have the same type
218+
assert image_seq_direct == image_seq_factory, \
219+
"ImageSequence created directly should equal one created via factory"
220+
221+
222+
# Cross-type Tests
223+
224+
def test_imageset_vs_imagesequence_inequality():
225+
""" Test that ImageSet and ImageSequence are not equal. """
226+
image_set = av.ImageSet()
227+
image_seq = av.ImageSequence()
228+
229+
assert image_set != image_seq, "ImageSet and ImageSequence should not be equal"
230+
assert not (image_set == image_seq), "ImageSet should not equal ImageSequence"
231+
232+
233+
def test_cross_type_comparison():
234+
""" Test comparing different ImageGroup types. """
235+
image_set = av.ImageSet()
236+
image_seq = av.ImageSequence()
237+
238+
# Verify they have different types
239+
set_type_str = av.ImageGroup.typeToString(image_set.getType())
240+
seq_type_str = av.ImageGroup.typeToString(image_seq.getType())
241+
242+
assert set_type_str == "ImageSet", "ImageSet should have ImageSet type"
243+
assert seq_type_str == "ImageSequence", "ImageSequence should have ImageSequence type"
244+
assert set_type_str != seq_type_str, "Type strings should be different"
245+
246+
247+
def test_imagegroup_factory_creates_correct_types():
248+
""" Test that factory creates different concrete types correctly. """
249+
# Create ImageSet via factory
250+
set_type = av.ImageGroup.stringToType("ImageSet")
251+
image_set = av.ImageGroup.create(set_type)
252+
253+
# Create ImageSequence via factory
254+
seq_type = av.ImageGroup.stringToType("ImageSequence")
255+
image_seq = av.ImageGroup.create(seq_type)
256+
257+
# Verify types
258+
assert av.ImageGroup.typeToString(image_set.getType()) == "ImageSet"
259+
assert av.ImageGroup.typeToString(image_seq.getType()) == "ImageSequence"
260+
261+
# Verify they're not equal
262+
assert image_set != image_seq, "Different types should not be equal"

src/aliceVision/sfmData/CMakeLists.txt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,9 @@ set(sfmData_files_headers
33
SfMData.hpp
44
CameraPose.hpp
55
Landmark.hpp
6+
ImageGroup.hpp
7+
ImageSet.hpp
8+
ImageSequence.hpp
69
View.hpp
710
Rig.hpp
811
uid.hpp
@@ -25,6 +28,7 @@ set(sfmData_files_sources
2528
exif.cpp
2629
ImageInfo.cpp
2730
Landmark.cpp
31+
ImageGroup.cpp
2832
Observation.cpp
2933
)
3034

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
// This file is part of the AliceVision project.
2+
// Copyright (c) 2026 AliceVision contributors.
3+
// This Source Code Form is subject to the terms of the Mozilla Public License,
4+
// v. 2.0. If a copy of the MPL was not distributed with this file,
5+
// You can obtain one at https://mozilla.org/MPL/2.0/.
6+
7+
#include "ImageGroup.hpp"
8+
#include <aliceVision/sfmData/ImageSet.hpp>
9+
#include <aliceVision/sfmData/ImageSequence.hpp>
10+
11+
namespace aliceVision {
12+
namespace sfmData {
13+
14+
std::string ImageGroup::typeToString(Type type)
15+
{
16+
switch (type)
17+
{
18+
case Type::ImageSet:
19+
return "ImageSet";
20+
case Type::ImageSequence:
21+
return "ImageSequence";
22+
}
23+
24+
throw std::invalid_argument("Invalid ImageGroup::Type enum value");
25+
}
26+
27+
ImageGroup::Type ImageGroup::stringToType(const std::string& typeStr)
28+
{
29+
if (typeStr == "ImageSet")
30+
{
31+
return ImageGroup::Type::ImageSet;
32+
}
33+
34+
if (typeStr == "ImageSequence")
35+
{
36+
return ImageGroup::Type::ImageSequence;
37+
}
38+
39+
throw std::invalid_argument("Invalid ImageGroup type string: " + typeStr);
40+
}
41+
42+
ImageGroup::sptr ImageGroup::create(const ImageGroup::Type & type)
43+
{
44+
if (type == ImageGroup::Type::ImageSet)
45+
{
46+
return std::make_shared<ImageSet>();
47+
}
48+
else if (type == ImageGroup::Type::ImageSequence)
49+
{
50+
return std::make_shared<ImageSequence>();
51+
}
52+
53+
return nullptr;
54+
}
55+
56+
} // namespace sfmData
57+
} // namespace aliceVision

0 commit comments

Comments
 (0)