DataAugmentation
			
			
			
			
			function
			 
			
			
	
			scaleprojection
			(
			
			ratios
			::
			
			NTuple
			{
			N
			}
			,
			 
			
			T
			 
			=
			 
			Float32
			)
			 
			where
			 
			N
			
			
    
			
			a
			 
			=
			 
			
			zeros
			(
			Float32
			,
			 
			N
			,
			 
			N
			)
			
    
			
			
			a
			[
			
			I
			(
			N
			)
			]
			 
			=
			 
			
			
			SVector
			{
			N
			}
			(
			ratios
			)
			
    
			
			return
			 
			
			LinearMap
			(
			
			
			SArray
			{
			
			Tuple
			{
			N
			,
			 
			N
			}
			}
			(
			a
			)
			)
			
			end
			
			
			
			
			
			struct
			
			 
			
	
			ScaleRatio
			{
			N
			}
			 
			<:
			 
	
			ProjectiveTransform
			
			
    
			
			ratios
			::
			
			NTuple
			{
			N
			}
			
			end
			
			
			
			
			function
			 
			
	
			getprojection
			(
			
			scale
			::
	
			ScaleRatio
			,
			 
			bounds
			
			;
			 
			
			randstate
			 
			=
			 
			nothing
			)
			
			
    
			
			return
			 
			
	
			scaleprojection
			(
			
			scale
			.
			
			ratios
			)
			
			end
			
			
			
			
			
			"""
			
			    ScaleKeepAspect(minlengths) <: ProjectiveTransform
			
			Scales the shortest side of `item` to `minlengths`, keeping the
			original aspect ratio.
			
			## Examples
			
			{cell=ScaleKeepAspect}
			```julia
			using DataAugmentation, TestImages
			image = testimage("lighthouse")
			tfm = ScaleKeepAspect((200, 200))
			apply(tfm, Image(image)) |> showitems
			```
			"""
			
			
			
			struct
			
			 
			
	
			ScaleKeepAspect
			{
			N
			}
			 
			<:
			 
	
			ProjectiveTransform
			
			
    
			
			minlengths
			::
			
			NTuple
			{
			N
			,
			 
			Int
			}
			
			end
			
			
			
			
			function
			 
			
			
	
			getprojection
			(
			
			scale
			::
			
	
			ScaleKeepAspect
			{
			N
			}
			,
			 
			bounds
			
			;
			 
			
			randstate
			 
			=
			 
			nothing
			)
			 
			where
			 
			N
			
			
    
			# If no scaling needs to be done, return a noop transform
			
    
			
			
			
			scale
			.
			
			minlengths
			 
			==
			 
			
			length
			.
			
			(
			
			bounds
			.
			
			rs
			)
			 
			&&
			 
			
			return
			 
			
			IdentityTransformation
			(
			)
			
			
    
			# Offset `minlengths` by 1 to avoid black border on one side
			
    
			
			ratio
			 
			=
			 
			
			maximum
			(
			
			(
			
			
			scale
			.
			
			minlengths
			 
			.+
			 
			1
			)
			 
			./
			 
			
			length
			.
			
			(
			
			bounds
			.
			
			rs
			)
			)
			
    
			
			upperleft
			 
			=
			
			 
			
			
			SVector
			{
			N
			,
			 
			Float32
			}
			(
			
			minimum
			.
			
			(
			
			bounds
			.
			
			rs
			)
			)
			 
			.-
			 
			0.5
			
    
			
			P
			 
			=
			 
			
	
			scaleprojection
			(
			
			Tuple
			(
			
			ratio
			 
			for
			
			 
			_
			 
			in
			
			 
			1
			:
			N
			)
			)
			
    
			
			if
			
			 
			upperleft
			 
			!=
			 
			
			SVector
			(
			0
			,
			 
			0
			)
			
			
        
			
			P
			 
			=
			
			 
			P
			 
			∘
			 
			
			Translation
			(
			(
			
			
			Float32
			.
			
			(
			
			P
			(
			upperleft
			)
			)
			 
			.+
			 
			0.5f0
			)
			)
			
    
			end
			
    
			
			return
			 
			P
			
			end
			
			
			
			function
			 
			
			
	
			projectionbounds
			(
			
			tfm
			::
			
	
			ScaleKeepAspect
			{
			N
			}
			,
			 
			P
			,
			 
			
			bounds
			::
			
	
			Bounds
			{
			N
			}
			
			;
			 
			
			randstate
			 
			=
			 
			nothing
			)
			 
			where
			 
			N
			
			
    
			
			origsz
			 
			=
			 
			
			length
			.
			
			(
			
			bounds
			.
			
			rs
			)
			
    
			
			ratio
			 
			=
			 
			
			maximum
			(
			
			(
			
			tfm
			.
			
			minlengths
			)
			 
			./
			 
			origsz
			)
			
    
			
			sz
			 
			=
			 
			
			floor
			.
			
			(
			Int
			,
			
			ratio
			 
			.*
			 
			origsz
			)
			
    
			
			bounds_
			 
			=
			 
			
	
			transformbounds
			(
			bounds
			,
			 
			P
			)
			
    
			
			bs_
			 
			=
			 
			
	
			offsetcropbounds
			(
			sz
			,
			 
			bounds_
			,
			 
			
			ntuple
			(
			
			_
			 
			->
			 
			0.5
			,
			 
			N
			)
			)
			
    
			
			return
			 
			bs_
			
			end
			
			
			
			
			
			"""
			
			    ScaleFixed(sizes)
			
			Projective transformation that scales sides to `sizes`, disregarding
			aspect ratio.
			
			See also [`ScaleKeepAspect`](#).
			"""
			
			
			
			struct
			
			 
			
	
			ScaleFixed
			{
			N
			}
			 
			<:
			 
	
			ProjectiveTransform
			
			
    
			
			sizes
			::
			
			NTuple
			{
			N
			,
			 
			Int
			}
			
			end
			
			
			
			
			function
			 
			
	
			getprojection
			(
			
			scale
			::
	
			ScaleFixed
			,
			 
			bounds
			
			;
			 
			
			randstate
			 
			=
			 
			nothing
			)
			
			
    
			
			ratios
			 
			=
			
			 
			(
			
			
			scale
			.
			
			sizes
			 
			.+
			 
			1
			)
			 
			./
			 
			
			length
			.
			
			(
			
			bounds
			.
			
			rs
			)
			
    
			
			upperleft
			 
			=
			
			 
			
			
			SVector
			{
			2
			,
			 
			Float32
			}
			(
			
			minimum
			.
			
			(
			
			bounds
			.
			
			rs
			)
			)
			 
			.-
			 
			1
			
    
			
			P
			 
			=
			 
			
	
			scaleprojection
			(
			ratios
			)
			
    
			
			if
			
			 
			upperleft
			 
			!=
			 
			
			SVector
			(
			0
			,
			 
			0
			)
			
			
        
			
			P
			 
			=
			
			 
			P
			  
			∘
			 
			
			Translation
			(
			
			-
			upperleft
			)
			
    
			end
			
    
			
			return
			 
			P
			
			end
			
			
			
			
			function
			 
			
			
	
			projectionbounds
			(
			
			tfm
			::
			
	
			ScaleFixed
			{
			N
			}
			,
			 
			P
			,
			 
			
			bounds
			::
			
	
			Bounds
			{
			N
			}
			
			;
			 
			
			randstate
			 
			=
			 
			nothing
			)
			 
			where
			 
			N
			
			
    
			
			bounds_
			 
			=
			 
			
	
			transformbounds
			(
			bounds
			,
			 
			P
			)
			
    
			
			return
			 
			
	
			offsetcropbounds
			(
			
			tfm
			.
			
			sizes
			,
			 
			bounds_
			,
			 
			
			(
			1.
			,
			 
			1.
			)
			)
			
			end
			
			
			
			
			
			"""
			
			    Zoom(scales = (1, 1.2)) <: ProjectiveTransform
			    Zoom(distribution)
			
			Zoom into an item by a factor chosen from the interval `scales`
			or `distribution`.
			"""
			
			
			
			struct
			
			 
			
	
			Zoom
			{
			
			D
			<:
			Sampleable
			}
			 
			<:
			 
	
			ProjectiveTransform
			
			
    
			
			dist
			::
			D
			
			end
			
			
			
			
			
	
			Zoom
			(
			
			
			scales
			::
			
			NTuple
			{
			2
			,
			 
			T
			}
			 
			=
			 
			
			(
			1.
			,
			 
			1.2
			)
			)
			 
			where
			 
			T
			 
			=
			 
			
	
			Zoom
			(
			
			Uniform
			(
			
			scales
			[
			1
			]
			,
			 
			
			scales
			[
			2
			]
			)
			)
			
			
			
			
	
			getrandstate
			(
			
			tfm
			::
	
			Zoom
			)
			 
			=
			 
			
			rand
			(
			
			tfm
			.
			
			dist
			)
			
			
			
			function
			 
			
			
	
			getprojection
			(
			
			tfm
			::
	
			Zoom
			,
			 
			
			bounds
			::
			
	
			Bounds
			{
			N
			}
			
			;
			 
			
			randstate
			 
			=
			 
			
	
			getrandstate
			(
			tfm
			)
			)
			 
			where
			 
			N
			
			
    
			
			ratio
			 
			=
			 
			randstate
			
    
			
			return
			 
			
	
			scaleprojection
			(
			
			ntuple
			(
			
			_
			 
			->
			 
			ratio
			,
			 
			N
			)
			)
			
			end
			
			
			
			
			
			"""
			
			    Rotate(γ)
			    Rotate(γs)
			
			Rotate 2D spatial data around the center by an angle chosen at
			uniformly from [-γ, γ], an angle given in degrees.
			
			You can also pass any `Distributions.Sampleable` from which the
			angle is selected.
			
			## Examples
			
			```julia
			tfm = Rotate(10)
			```
			
			"""
			
			
			
			struct
			
			 
			
	
			Rotate
			{
			
			S
			<:
			Sampleable
			}
			 
			<:
			 
	
			ProjectiveTransform
			
			
    
			
			dist
			::
			S
			
			end
			
			
			
	
			Rotate
			(
			γ
			)
			 
			=
			 
			
	
			Rotate
			(
			
			Uniform
			(
			
			-
			
			abs
			(
			γ
			)
			,
			 
			
			abs
			(
			γ
			)
			)
			)
			
			
			
			
	
			getrandstate
			(
			
			tfm
			::
	
			Rotate
			)
			 
			=
			 
			
			rand
			(
			
			tfm
			.
			
			dist
			)
			
			
			
			function
			 
			
	
			getprojection
			(
			
        
			
			tfm
			::
	
			Rotate
			,
			
        
			
			bounds
			::
			
	
			Bounds
			{
			2
			}
			
			;
			
        
			
			randstate
			 
			=
			 
			
	
			getrandstate
			(
			tfm
			)
			)
			
			
    
			
			γ
			 
			=
			 
			randstate
			
    
			
			middlepoint
			 
			=
			 
			
			
			SVector
			{
			2
			,
			 
			Float32
			}
			(
			
			mean
			.
			
			(
			
			bounds
			.
			
			rs
			)
			)
			
    
			
			r
			 
			=
			
			
			 
			γ
			 
			/
			 
			360
			 
			*
			
			 
			2
			
			pi
			
    
			
			return
			 
			
			recenter
			(
			
			RotMatrix
			(
			
			convert
			(
			Float32
			,
			 
			r
			)
			)
			,
			 
			middlepoint
			)
			
			end
			
			
			
			
			
			
			"""
			
			    Reflect(γ)
			    Reflect(distribution)
			
			Reflect 2D spatial data around the center by an angle chosen at
			uniformly from [-γ, γ], an angle given in degrees.
			
			You can also pass any `Distributions.Sampleable` from which the
			angle is selected.
			
			## Examples
			
			```julia
			tfm = Reflect(10)
			```
			"""
			
			
			
			struct
			
			 
	
			Reflect
			 
			<:
			 
	
			ProjectiveTransform
			
			
    
			γ
			
			end
			
			
			
			
			function
			 
			
	
			getprojection
			(
			
			tfm
			::
	
			Reflect
			,
			 
			bounds
			
			;
			 
			
			randstate
			 
			=
			 
			
	
			getrandstate
			(
			tfm
			)
			)
			
			
    
			
			r
			 
			=
			
			
			 
			
			tfm
			.
			
			γ
			 
			/
			 
			360
			 
			*
			
			 
			2
			
			pi
			
    
			
			return
			 
			
	
			centered
			(
			
			LinearMap
			(
			
	
			reflectionmatrix
			(
			r
			)
			)
			,
			 
			bounds
			)
			
			end
			
			
			
			
			
			"""
			
			    centered(P, bounds)
			
			Transform `P` so that is applied around the center of `bounds`
			instead of the origin
			"""
			
			
			function
			 
			
	
			centered
			(
			P
			,
			 
			
			bounds
			::
			
	
			Bounds
			{
			2
			}
			)
			
			
    
			
			upperleft
			 
			=
			 
			
			minimum
			.
			
			(
			
			bounds
			.
			
			rs
			)
			
    
			
			bottomright
			 
			=
			 
			
			maximum
			.
			
			(
			
			bounds
			.
			
			rs
			)
			
			
    
			
			midpoint
			 
			=
			
			 
			
			
			SVector
			{
			2
			,
			 
			Float32
			}
			(
			
			(
			
			bottomright
			 
			.-
			 
			upperleft
			)
			 
			./
			 
			2
			)
			 
			.+
			 
			
			
			SVector
			{
			2
			,
			 
			Float32
			}
			(
			.5
			,
			 
			.5
			)
			
    
			
			return
			 
			
			recenter
			(
			P
			,
			 
			midpoint
			)
			
			end
			
			
			
			
			
	
			FlipX
			(
			)
			 
			=
			 
			
	
			Reflect
			(
			180
			)
			
			
			
	
			FlipY
			(
			)
			 
			=
			 
			
	
			Reflect
			(
			90
			)
			
			
			
			function
			 
			
	
			reflectionmatrix
			(
			r
			)
			
			
    
			
			A
			 
			=
			 
			
			
			SMatrix
			{
			2
			,
			 
			2
			,
			 
			Float32
			}
			(
			
			cos
			(
			
			2
			
			r
			)
			,
			 
			
			sin
			(
			
			2
			
			r
			)
			,
			 
			
			sin
			(
			
			2
			
			r
			)
			,
			 
			
			-
			
			cos
			(
			
			2
			
			r
			)
			)
			
    
			
			return
			 
			
			round
			.
			
			(
			A
			
			;
			 
			
			digits
			 
			=
			 
			12
			)
			
			end
			
			
			
			
			
			
			"""
			
			    PinOrigin()
			
			Projective transformation that translates the data so that
			the upper left bounding corner is at the origin `(0, 0)` (or
			the multidimensional equivalent).
			
			Projective transformations on images return `OffsetArray`s,
			but not on keypoints. Hardware like GPUs do not support OffsetArrays,
			so they will be unwrapped and no longer match up with the keypoints.
			
			Pinning the data to the origin makes sure that the resulting
			`OffsetArray` has the same indices as a regular array, starting
			at one.
			"""
			
			
			
			struct
			
			 
	
			PinOrigin
			 
			<:
			 
	
			ProjectiveTransform
			
			 
			end
			
			
			
			function
			 
			
	
			getprojection
			(
			
			::
	
			PinOrigin
			,
			 
			bounds
			
			;
			 
			
			randstate
			 
			=
			 
			nothing
			)
			
			
    
			
			p
			 
			=
			
			 
			(
			
			-
			
			
			SVector
			{
			2
			,
			 
			Float32
			}
			(
			
			minimum
			.
			
			(
			
			bounds
			.
			
			rs
			)
			)
			)
			 
			.+
			 
			1
			
    
			
			P
			 
			=
			 
			
			Translation
			(
			p
			)
			
    
			
			return
			 
			P
			
			end
			
			
			
			function
			 
			
	
			apply
			(
			
			::
	
			PinOrigin
			,
			 
			
			item
			::
			
			Union
			{
			
			<:
	
			Image
			,
			 
			
			<:
	
			MaskMulti
			,
			 
			
			<:
	
			MaskBinary
			}
			
			;
			 
			
			randstate
			 
			=
			 
			nothing
			)
			
			
    
			
			item
			 
			=
			 
			
			@
			set
			
			 
			
			item
			.
			
			data
			 
			=
			 
			
			parent
			(
			
	
			itemdata
			(
			item
			)
			)
			
    
			
			item
			 
			=
			 
			
			@
			set
			
			 
			
			item
			.
			
			bounds
			 
			=
			 
			
	
			Bounds
			(
			
			size
			(
			
	
			itemdata
			(
			item
			)
			)
			)
			
    
			
			return
			 
			item
			
			end
			
			
			
			function
			 
			
	
			apply!
			(
			
			buf
			::
	
			AbstractItem
			,
			 
			
			::
	
			PinOrigin
			,
			 
			
			item
			::
			
			Union
			{
	
			Image
			,
			 
	
			MaskMulti
			,
			 
	
			MaskBinary
			}
			
			;
			 
			
			randstate
			 
			=
			 
			nothing
			)
			
			
    
			
			item
			 
			=
			 
			
			@
			set
			
			 
			
			item
			.
			
			data
			 
			=
			 
			
			parent
			(
			
	
			itemdata
			(
			item
			)
			)
			
    
			
	
			copyitemdata!
			(
			buf
			,
			 
			item
			)
			
    
			
			return
			 
			buf
			
			end
			
			PinOrigin should not compose with a cropped transform otherwise the pinning won't work. This overwrites the default composition.
			
			
			
			
	
			compose
			(
			
			cropped
			::
	
			CroppedProjectiveTransform
			,
			 
			
			pin
			::
	
			PinOrigin
			)
			 
			=
			 
			
	
			Sequence
			(
			cropped
			,
			 
			pin
			)
			
			
			
	
			compose
			(
			
			cropped
			::
	
			ComposedProjectiveTransform
			,
			 
			
			pin
			::
	
			PinOrigin
			)
			 
			=
			 
			
	
			Sequence
			(
			cropped
			,
			 
			pin
			)
			
			
			
	
			compose
			(
			
			cropped
			::
	
			ProjectiveTransform
			,
			 
			
			pin
			::
	
			PinOrigin
			)
			 
			=
			 
			
	
			Sequence
			(
			cropped
			,
			 
			pin
			)
			
			
			
			
	
			RandomResizeCrop
			(
			sz
			)
			 
			=
			
			
			 
			
	
			ScaleKeepAspect
			(
			sz
			)
			 
			|>
			 
			
	
			RandomCrop
			(
			sz
			)
			 
			|>
			 
			
	
			PinOrigin
			(
			)
			
			
			
	
			CenterResizeCrop
			(
			sz
			)
			 
			=
			
			
			 
			
	
			ScaleKeepAspect
			(
			sz
			)
			 
			|>
			 
			
	
			CenterCrop
			(
			sz
			)
			 
			|>
			 
			
	
			PinOrigin
			(
			)
			
			
			
			
	
			ResizePadDivisible
			(
			sz
			,
			 
			by
			)
			 
			=
			
			
			 
			
	
			ScaleKeepAspect
			(
			sz
			)
			 
			|>
			 
			
	
			PadDivisible
			(
			by
			)
			 
			|>
			 
			
	
			PinOrigin
			(
			)