DataAugmentation
			
			
			
			
			
			"""
			
			    testapply(tfm, item)
			    testapply(tfm, I)
			
			Test `apply` invariants of `tfm` on `item` or item type `I`.
			
			1. With a constant `randstate` parameter, `apply` should always return the
			    same result.
			"""
			
			
			function
			 
			
	
			testapply
			(
			
			tfm
			::
	
			Transform
			,
			 
			
			item
			::
	
			AbstractItem
			)
			
			
    
			# Invariant 1
			
    
			
			r
			 
			=
			 
			
	
			getrandstate
			(
			tfm
			)
			
    
			
			titems
			 
			=
			 
			
			[
			
			
	
			apply
			(
			tfm
			,
			 
			item
			
			;
			 
			
			randstate
			 
			=
			 
			r
			)
			 
			for
			
			 
			i
			 
			=
			
			 
			1
			:
			8
			]
			
    
			
			@
			test
			 
			
			all
			(
			
			map
			(
			
			titem
			 
			->
			
			 
			
	
			itemdata
			(
			titem
			)
			 
			==
			 
			
	
			itemdata
			(
			
			titems
			[
			1
			]
			)
			,
			 
			titems
			)
			)
			
			
    
			#
			
    
			
			@
			test_nowarn
			 
			
	
			apply
			(
			tfm
			,
			 
			
			(
			item
			,
			 
			item
			)
			)
			
			end
			
			
			
			
	
			testapply
			(
			
			tfm
			::
	
			Transform
			,
			 
			
			I
			::
			
			Type
			{
			
			<:
	
			AbstractItem
			}
			)
			 
			=
			 
			
	
			testapply
			(
			tfm
			,
			 
			
	
			testitem
			(
			I
			)
			)
			
			
			
	
			testapply
			(
			tfm
			,
			 
			
			items
			::
			Tuple
			)
			 
			=
			 
			
			foreach
			(
			
			i
			 
			->
			 
			
	
			testapply
			(
			tfm
			,
			 
			i
			)
			,
			 
			items
			)
			
			
			
			
			
			
			"""
			
			    testapply!(tfm, Items)
			    testapply!(tfm, Item)
			    testapply!(tfm, item1, item2)
			
			Test `apply!` invariants.
			
			1. With a constant `randstate` parameter, `apply!` should always return the
			    same result.
			2. Given a different item than was used to create the buffer, the buffer's data
			    should be modified.
			"""
			
			
			function
			 
			
			
	
			testapply!
			(
			
			tfm
			::
	
			Transform
			,
			 
			
			item1
			::
			I
			,
			 
			
			item2
			::
			I
			)
			 
			where
			 
			
			I
			<:
	
			AbstractItem
			
			
    
			# Invariant 1
			
    
			
			r
			 
			=
			 
			
	
			getrandstate
			(
			tfm
			)
			
    
			
			titems
			 
			=
			 
			
			[
			
			
	
			apply
			(
			tfm
			,
			 
			
			deepcopy
			(
			item1
			)
			
			;
			 
			
			randstate
			 
			=
			 
			r
			)
			 
			for
			
			 
			i
			 
			=
			
			 
			1
			:
			8
			]
			
    
			
			@
			test
			 
			
	
			allequal
			(
			
	
			itemdata
			.
			
			(
			titems
			)
			)
			
			
    
			# Invariant 2
			
    
			
			buf
			 
			=
			 
			
	
			makebuffer
			(
			tfm
			,
			 
			item1
			)
			
    
			
			cbuf
			 
			=
			 
			
			deepcopy
			(
			buf
			)
			
    
			
	
			apply!
			(
			buf
			,
			 
			tfm
			,
			 
			item2
			)
			
    
			
			if
			
			 
			buf
			 
			isa
			 
	
			AbstractItem
			
			
        
			
			@
			test
			
			 
			
	
			itemdata
			(
			buf
			)
			 
			!=
			 
			
	
			itemdata
			(
			cbuf
			)
			
    
			end
			
			end
			
			
			
			
	
			testapply!
			(
			
			tfm
			::
	
			Transform
			,
			 
			
			I
			::
			
			Type
			{
			
			<:
	
			AbstractItem
			}
			)
			 
			=
			 
			
	
			testapply!
			(
			tfm
			,
			 
			
	
			testitem
			(
			I
			)
			,
			 
			
	
			testitem
			(
			I
			)
			)
			
			
			
	
			testapply!
			(
			tfm
			,
			 
			
			items
			::
			Tuple
			)
			 
			=
			 
			
			foreach
			(
			
			i
			 
			->
			 
			
	
			testapply!
			(
			tfm
			,
			 
			i
			)
			,
			 
			items
			)
			
			
			
			
			
			
			
			"""
			
			    testprojective(tfm)
			
			Test invariants of a `ProjectiveTransform`.
			
			1. `getprojection` is defined, and, given a constant `randstate` parameter,
			    always returns the same result.
			2. It preserves the item type, i.e. `apply(tfm, ::I) -> I`.
			3. Applying it to multiple items with the same bounds results in the same bounds
			    for all items.
			"""
			
			
			function
			 
			
	
			testprojective
			(
			
			tfm
			::
	
			ProjectiveTransform
			,
			 
			
			items
			::
			Tuple
			)
			
			
    
			# All bounds must be equal for the test to work
			
    
			
			@
			assert
			 
			
	
			allequal
			(
			
	
			getbounds
			.
			
			(
			items
			)
			)
			
			
    
			# Invariant 1
			
    
			
			r
			 
			=
			 
			
	
			getrandstate
			(
			tfm
			)
			
    
			
			bs
			 
			=
			 
			
	
			getbounds
			(
			
			items
			[
			1
			]
			)
			
    
			
			Ps
			 
			=
			 
			
			[
			
			
	
			getprojection
			(
			tfm
			,
			 
			bs
			
			;
			 
			
			randstate
			 
			=
			 
			r
			)
			 
			for
			
			 
			i
			 
			=
			
			 
			1
			:
			10
			]
			
    
			
			@
			test
			 
			
	
			allequal
			(
			Ps
			)
			
			
    
			# Invariant 2
			
    
			
			titems
			 
			=
			 
			
	
			apply
			(
			tfm
			,
			 
			items
			)
			
    
			
			@
			test
			 
			
			all
			(
			
			
			typeof
			.
			
			(
			items
			)
			 
			.==
			 
			
			typeof
			.
			
			(
			titems
			)
			)
			
			
    
			# Invariant 3
			
    
			
			@
			test
			 
			
	
			allequal
			(
			
	
			getbounds
			.
			
			(
			titems
			)
			)
			
			end
			
			
			
			
			function
			 
			
			
	
			testprojective
			(
			
			tfm
			::
	
			ProjectiveTransform
			,
			 
			
			Is
			::
			
			NTuple
			{
			N
			,
			 
			
			<:
			Type
			}
			)
			 
			where
			 
			{
			N
			}
			
			
    
			
	
			testprojective
			(
			tfm
			,
			 
			
	
			testitem
			.
			
			(
			Is
			)
			)
			
			end
			
			
			
			
	
			testprojective
			(
			
			tfm
			::
	
			ProjectiveTransform
			)
			 
			=
			
    
			
	
			testprojective
			(
			tfm
			,
			 
			
			(
	
			Image
			,
			 
	
			MaskBinary
			,
			 
	
			MaskMulti
			,
			 
	
			Keypoints
			)
			)
			
			
			
			
			
			"""
			
			    testitem(TItem)
			
			Create an instance of an item with type `TItem`. If it has spatial bounds,
			should return an instance with bounds with ranges (1:16, 1:16).
			"""
			
			
			function
			 
	
			testitem
			 
			end
			
			
			
			
			
	
			testitem
			(
			
			::
			
			Type
			{
	
			ArrayItem
			}
			)
			 
			=
			 
			
	
			testitem
			(
			
	
			ArrayItem
			{
			2
			,
			 
			Float32
			}
			)
			
			
			
			
	
			testitem
			(
			
			::
			
			Type
			{
			
	
			ArrayItem
			{
			N
			,
			 
			T
			}
			}
			)
			 
			where
			 
			{
			N
			,
			 
			T
			}
			 
			=
			 
			
	
			ArrayItem
			(
			
			rand
			(
			T
			,
			 
			
			ntuple
			(
			
			i
			 
			->
			 
			16
			,
			 
			N
			)
			)
			)
			
			
			
			
	
			testitem
			(
			
			::
			
			Type
			{
	
			Image
			}
			)
			 
			=
			 
			
	
			testitem
			(
			
	
			Image
			{
			2
			,
			 
			
			RGB
			{
			N0f8
			}
			}
			)
			
			
			
			
	
			testitem
			(
			
			::
			
			Type
			{
			
	
			Image
			{
			N
			,
			 
			T
			}
			}
			)
			 
			where
			 
			{
			N
			,
			 
			T
			}
			 
			=
			 
			
	
			Image
			(
			
			rand
			(
			T
			,
			 
			
			ntuple
			(
			
			i
			 
			->
			 
			16
			,
			 
			N
			)
			)
			)
			
			
			
			
	
			testitem
			(
			
			::
			
			Type
			{
	
			MaskBinary
			}
			)
			 
			=
			 
			
	
			testitem
			(
			
	
			MaskBinary
			{
			2
			}
			)
			
			
			
			
	
			testitem
			(
			
			::
			
			Type
			{
			
	
			MaskBinary
			{
			N
			}
			}
			)
			 
			where
			 
			{
			N
			}
			 
			=
			 
			
	
			MaskBinary
			(
			
			rand
			(
			Bool
			,
			 
			
			ntuple
			(
			
			i
			 
			->
			 
			16
			,
			 
			N
			)
			)
			)
			
			
			
			
	
			testitem
			(
			
			::
			
			Type
			{
	
			MaskMulti
			}
			)
			 
			=
			 
			
	
			testitem
			(
			
	
			MaskMulti
			{
			2
			,
			 
			UInt8
			}
			)
			
			
			function
			 
			
			
	
			testitem
			(
			
			::
			
			Type
			{
			
	
			MaskMulti
			{
			N
			,
			 
			T
			}
			}
			)
			 
			where
			 
			{
			N
			,
			 
			T
			}
			
			
    
			
			n
			 
			=
			 
			
			rand
			(
			
			2
			:
			10
			)
			
    
			
			data
			 
			=
			 
			
			T
			.
			
			(
			
			rand
			(
			
			1
			:
			n
			,
			 
			
			ntuple
			(
			
			i
			 
			->
			 
			16
			,
			 
			N
			)
			)
			)
			
    
			
	
			MaskMulti
			(
			data
			,
			 
			
			1
			:
			n
			)
			
			end
			
			
			
			
			
	
			testitem
			(
			
			::
			
			Type
			{
	
			Keypoints
			}
			)
			 
			=
			 
			
	
			testitem
			(
			
	
			Keypoints
			{
			2
			,
			 
			Float32
			}
			)
			
			
			function
			 
			
			
	
			testitem
			(
			
			::
			
			Type
			{
			
	
			Keypoints
			{
			N
			,
			 
			T
			}
			}
			)
			 
			where
			 
			{
			N
			,
			 
			T
			}
			
			
    
			
			n
			 
			=
			 
			
			rand
			(
			
			8
			:
			16
			)
			
    
			
			data
			 
			=
			 
			
			map
			(
			
			v
			 
			->
			
			 
			v
			 
			.*
			 
			15
			,
			 
			
			rand
			(
			
			SVector
			{
			N
			,
			 
			T
			}
			,
			 
			n
			)
			)
			
    
			
			return
			 
			
	
			Keypoints
			(
			data
			,
			 
			
			(
			16
			,
			 
			16
			)
			)
			
			end
			
			
			
			
	
			testitem
			(
			
			::
			
			Type
			{
	
			Polygon
			}
			)
			 
			=
			 
			
	
			testitem
			(
			
	
			Polygon
			{
			2
			,
			 
			Float32
			}
			)
			
			
			function
			 
			
			
	
			testitem
			(
			
			::
			
			Type
			{
			
	
			Polygon
			{
			N
			,
			 
			T
			}
			}
			)
			 
			where
			 
			{
			N
			,
			 
			T
			}
			
			
    
			
			n
			 
			=
			 
			
			rand
			(
			
			8
			:
			16
			)
			
    
			
			data
			 
			=
			 
			
			map
			(
			
			v
			 
			->
			
			 
			v
			 
			.*
			 
			15
			,
			 
			
			rand
			(
			
			SVector
			{
			N
			,
			 
			T
			}
			,
			 
			n
			)
			)
			
    
			
			return
			 
			
	
			Polygon
			(
			
	
			Keypoints
			(
			data
			,
			 
			
			(
			16
			,
			 
			16
			)
			)
			)
			
			end
			
			
			
			
	
			testitem
			(
			
			::
			
			Type
			{
	
			BoundingBox
			}
			)
			 
			=
			 
			
	
			testitem
			(
			
	
			BoundingBox
			{
			2
			,
			 
			Float32
			}
			)
			
			
			function
			 
			
			
	
			testitem
			(
			
			::
			
			Type
			{
			
	
			BoundingBox
			{
			N
			,
			 
			T
			}
			}
			)
			 
			where
			 
			{
			N
			,
			 
			T
			}
			
			
    
			
			n
			 
			=
			 
			2
			
    
			
			data
			 
			=
			 
			
			map
			(
			
			v
			 
			->
			
			 
			v
			 
			.*
			 
			15
			,
			 
			
			rand
			(
			
			SVector
			{
			N
			,
			 
			T
			}
			,
			 
			n
			)
			)
			
    
			
			return
			 
			
	
			BoundingBox
			(
			
	
			Keypoints
			(
			data
			,
			 
			
			(
			16
			,
			 
			16
			)
			)
			)
			
			end
			
			
			
			
	
			allequal
			(
			xs
			)
			 
			=
			 
			
			all
			(
			
			
			x
			 
			==
			 
			
			xs
			[
			1
			]
			 
			for
			
			 
			x
			 
			in
			 
			xs
			)