Coverage for lib/lottie/parsers/raster.py: 0%

122 statements  

« prev     ^ index     » next       coverage.py v7.2.2, created at 2023-03-20 16:17 +0100

1from PIL import Image 

2import glaxnimate 

3from . import glaxnimate_helpers 

4import enum 

5from .. import objects 

6from ..nvector import NVector 

7from .pixel import _vectorizing_func 

8 

9 

10class QuantizationMode(enum.Enum): 

11 Nearest = 1 

12 Exact = 2 

13 

14 

15class PaletteAlgorithm: 

16 def get_colors(self, image, n_colors): 

17 pass 

18 

19 

20class KMeansPalette(PaletteAlgorithm): 

21 def __init__(self, iterations=100, match=glaxnimate.utils.quantize.MatchType.MostFrequent): 

22 self.iterations = iterations 

23 self.match = match 

24 

25 def get_colors(self, image, n_colors): 

26 return glaxnimate.utils.quantize.k_means(image, n_colors, self.iterations, self.match) 

27 

28 

29class OctreePalette(PaletteAlgorithm): 

30 def get_colors(self, image, n_colors): 

31 return glaxnimate.utils.quantize.octree(image, n_colors) 

32 

33 

34class KModesPalette(PaletteAlgorithm): 

35 def get_colors(self, image, n_colors): 

36 return glaxnimate.utils.quantize.k_modes(image, n_colors) 

37 

38 

39class EdgeExclusionModesPalette(PaletteAlgorithm): 

40 def __init__(self, min_frequency=0.0005): 

41 self.min_frequency = min_frequency 

42 

43 def get_colors(self, image, n_colors): 

44 return glaxnimate.utils.quantize.edge_exclusion_modes(image, n_colors, self.min_frequency) 

45 

46 

47class TraceOptions: 

48 def __init__( 

49 self, 

50 color_mode=QuantizationMode.Nearest, 

51 palette_algorithm=OctreePalette(), 

52 tolerance=100, 

53 stroke_width=1, 

54 smoothness=0.75, 

55 min_area=16 

56 ): 

57 self.trace_options = glaxnimate.utils.trace.TraceOptions() 

58 self.palette_algorithm = palette_algorithm 

59 self.color_mode = color_mode 

60 self.tolerance = tolerance 

61 self.stroke_width = stroke_width 

62 self.min_area = min_area 

63 self.smoothness = smoothness 

64 

65 @property 

66 def smoothness(self): 

67 return self.trace_options.smoothness 

68 

69 @smoothness.setter 

70 def smoothness(self, value): 

71 self.trace_options.smoothness = value 

72 

73 @property 

74 def min_area(self): 

75 return self.trace_options.min_area 

76 

77 @min_area.setter 

78 def min_area(self, value): 

79 self.trace_options.min_area = value 

80 

81 def quantize(self, image, n_colors): 

82 """! 

83 Returns a list of RGB values 

84 """ 

85 return self.palette_algorithm.get_colors(image, n_colors) 

86 

87 def trace(self, image, codebook): 

88 """! 

89 Returns a list of tuple [color, data] where for each color in codebook 

90 data is a list of bezier 

91 

92 You can get codebook from quantize() 

93 """ 

94 

95 if codebook is None or len(codebook) == 0: 

96 tracer = glaxnimate.utils.trace.Tracer(image, self.trace_options) 

97 tracer.set_target_alpha(128, False) 

98 return [glaxnimate.utils.Color(0, 0, 0), tracer.trace()] 

99 

100 if self.color_mode == QuantizationMode.Nearest: 

101 return list(zip(codebook, glaxnimate.utils.trace.quantize_and_trace(image, self.trace_options, codebook))) 

102 

103 mono_data = [] 

104 tracer = glaxnimate.utils.trace.Tracer(image, self.trace_options) 

105 for color in codebook: 

106 tracer.set_target_color(color, self.tolerance) 

107 mono_data.append((color, tracer.trace())) 

108 

109 return mono_data 

110 

111 

112class Vectorizer: 

113 def __init__(self, trace_options: TraceOptions): 

114 self.palette = None 

115 self.layers = {} 

116 self.trace_options = trace_options 

117 

118 def _create_layer(self, animation, layer_name): 

119 layer = animation.add_layer(objects.ShapeLayer()) 

120 if layer_name: 

121 self.layers[layer_name] = layer 

122 layer.name = layer_name 

123 return layer 

124 

125 def prepare_layer(self, animation, layer_name=None): 

126 layer = self._create_layer(animation, layer_name) 

127 layer._max_verts = {} 

128 for color in self.palette: 

129 group = layer.add_shape(objects.Group()) 

130 group.name = "color_%s" % color.name 

131 layer._max_verts[group.name] = 0 

132 fcol = glaxnimate_helpers.color_from_glaxnimate(color) 

133 group.add_shape(objects.Fill(NVector(*fcol))) 

134 if self.trace_options.stroke_width > 0: 

135 group.add_shape(objects.Stroke(NVector(*fcol), self.trace_options.stroke_width)) 

136 return layer 

137 

138 def raster_to_layer(self, animation, raster, layer_name=None): 

139 layer = self.prepare_layer(animation, layer_name) 

140 mono_data = self.trace_options.trace(raster, self.palette) 

141 for (color, beziers), group in zip(mono_data, layer.shapes): 

142 self.traced_to_shapes(group, beziers) 

143 return layer 

144 

145 def traced_to_shapes(self, group, beziers): 

146 shapes = [] 

147 for bezier in beziers: 

148 shape = group.insert_shape(0, objects.Path()) 

149 shapes.append(shape) 

150 shape.shape.value = self.traced_to_bezier(bezier) 

151 return shapes 

152 

153 def traced_to_bezier(self, path): 

154 bezier = objects.Bezier() 

155 for point in path: 

156 pos = glaxnimate_helpers.point_from_glaxnimate(point.pos) 

157 tan_in = glaxnimate_helpers.point_from_glaxnimate(point.tan_in - point.pos) 

158 tan_out = glaxnimate_helpers.point_from_glaxnimate(point.tan_out - point.pos) 

159 bezier.add_point(pos, tan_in, tan_out) 

160 if path.closed: 

161 bezier.closed = True 

162 return bezier 

163 

164 

165def raster_to_animation( 

166 filenames, 

167 n_colors=1, 

168 frame_delay=1, 

169 framerate=60, 

170 palette=[], 

171 trace_options=TraceOptions() 

172): 

173 vc = Vectorizer(trace_options) 

174 

175 def callback(animation, raster, frame, time, duration): 

176 if vc.palette is None: 

177 if palette: 

178 vc.palette = [glaxnimate_helpers.color_to_glaxnimate(c) for c in palette] 

179 elif n_colors > 1: 

180 vc.palette = trace_options.quantize(raster, n_colors) 

181 else: 

182 vc.palette = [glaxnimate.utils.Color(0, 0, 0, 255)] 

183 layer = vc.raster_to_layer(animation, raster, "frame_%s" % frame) 

184 layer.in_point = time 

185 layer.out_point = layer.in_point + duration 

186 

187 animation = _vectorizing_func(filenames, frame_delay, framerate, callback) 

188 

189 return animation