qgif 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
qgif-0.1.0/PKG-INFO ADDED
@@ -0,0 +1,13 @@
1
+ Metadata-Version: 2.1
2
+ Name: qgif
3
+ Version: 0.1.0
4
+ Summary: Convert GIF to QGIF, or decode QGIF.
5
+ Author: Hangzhou Nationalchip Inc.
6
+ Author-email: zhengdi@nationalchip.com
7
+ License: MIT Licence
8
+ Keywords: qgif nationalchip
9
+ Classifier: Development Status :: 3 - Alpha
10
+ Classifier: Intended Audience :: Developers
11
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
12
+ Classifier: License :: OSI Approved :: MIT License
13
+ Classifier: Programming Language :: Python :: 3
@@ -0,0 +1 @@
1
+ VERSION = "0.1.0"
@@ -0,0 +1,61 @@
1
+ #!/usr/bin/env python
2
+
3
+ import sys
4
+ import os
5
+ import argparse
6
+
7
+ from qgif import VERSION
8
+ from qgif.qgif import convert_gif_to_qgif, decode_qgif
9
+
10
+ def check_range(param_name, t):
11
+ def checker(value):
12
+ value = t(value)
13
+ if value < 0 or value > 10:
14
+ raise argparse.ArgumentTypeError(f"Argument '{param_name}' must be between 0 and 10, but got {value}")
15
+ return value
16
+ return checker
17
+
18
+ if __name__ == "__main__":
19
+ parser = argparse.ArgumentParser(prog="qgif", description="QGIF generator and decoder")
20
+ subparsers = parser.add_subparsers(dest="command")
21
+
22
+ parser_convert = subparsers.add_parser("convert", help="Convert GIF to QGIF.")
23
+ parser_convert.add_argument("-f", "--format", required=True, choices=["gx64", "gx96"], help="The format of generated QGIF frames.")
24
+ parser_convert.add_argument("-i", "--input", required=True, help="The input GIF file name.")
25
+ parser_convert.add_argument("-o", "--output", required=True, help="Generated QGIF file name.")
26
+ parser_convert.add_argument("-fr", "--framerate", type=int, default=0, help="The frame rate of QGIF file. (default: same as GIF)")
27
+ parser_convert.add_argument("-l", "--lvgl", action="store_true", help="Generate LVGL C file.")
28
+ parser_convert.add_argument("--crop-size", type=int, nargs=2, help="Size to crop the image (width height)")
29
+ parser_convert.add_argument("--scale-size", type=int, nargs=2, help="Size to scale the image (width height)")
30
+ parser_convert.add_argument("--bilateral-strength", default=0, type=check_range("--bilateral-strength", float),
31
+ help="Strength of bilateral filter (0-10, can be a float)")
32
+ parser_convert.add_argument("--median-strength", default=0, type=check_range("--median-strength", int),
33
+ help="Strength of spatial median filter (0-10)")
34
+ parser_convert.add_argument("--interframe-threshold", default=0, type=check_range("--interframe-threshold", int),
35
+ help="Threshold for interframe median filtering (0-10)")
36
+ parser_convert.add_argument("--temporal-threshold", default=0, type=check_range("--temporal-threshold", int),
37
+ help="Threshold for temporal denoise (0-10)")
38
+
39
+ parser_decode = subparsers.add_parser("decode", help="Decode QGIF to frames.")
40
+ parser_decode.add_argument("-i", "--input", required=True, help="The input QGIF file name.")
41
+ parser_decode.add_argument("-o", "--output", required=True, help="The directory of output frames and pngs.")
42
+ parser_decode.add_argument("-c", "--colordepth", type=int, required=True, choices=[16, 32], help="The color depth of decoded frames.")
43
+
44
+ parser.add_argument("-V", "--version", action="version", version="qgif %s" % VERSION)
45
+
46
+ args = parser.parse_args()
47
+ if args.command == "convert":
48
+ convert_gif_to_qgif(args.input,
49
+ args.output,
50
+ args.format,
51
+ args.lvgl,
52
+ args.framerate,
53
+ crop_size=args.crop_size,
54
+ scale_size=args.scale_size,
55
+ bilateral_strength=args.bilateral_strength,
56
+ median_strength=args.median_strength,
57
+ interframe_threshold=args.interframe_threshold,
58
+ temporal_threshold=args.temporal_threshold)
59
+ elif args.command == "decode":
60
+ decode_qgif(args.input, args.output, args.colordepth)
61
+
@@ -0,0 +1,84 @@
1
+ import os
2
+ import re
3
+
4
+ def sanitize_filename(filename):
5
+ # 移除不合法的字符,只保留字母、数字和下划线
6
+ sanitized = re.sub(r'[^a-zA-Z0-9_]', '_', filename)
7
+
8
+ # 如果第一个字符是数字,则在前面加上一个下划线
9
+ if sanitized and sanitized[0].isdigit():
10
+ sanitized = '_' + sanitized
11
+
12
+ # 检查是否是C语言的关键字
13
+ c_keywords = {
14
+ "auto", "break", "case", "char", "const", "continue", "default", "do", "double", "else", "enum", "extern",
15
+ "float", "for", "goto", "if", "inline", "int", "long", "register", "restrict", "return", "short", "signed",
16
+ "sizeof", "static", "struct", "switch", "typedef", "union", "unsigned", "void", "volatile", "while", "_Alignas",
17
+ "_Alignof", "_Atomic", "_Bool", "_Complex", "_Generic", "_Imaginary", "_Noreturn", "_Static_assert", "_Thread_local"
18
+ }
19
+
20
+ if sanitized in c_keywords:
21
+ sanitized = '_' + sanitized
22
+
23
+ return sanitized
24
+
25
+ def gen_lvgl_file(bin_file, lvgl_file, image_width, image_height):
26
+ image_name_with_extension = os.path.basename(bin_file)
27
+ image_name, _ = os.path.splitext(image_name_with_extension)
28
+ image_name = sanitize_filename(image_name)
29
+
30
+ # 读取二进制文件
31
+ with open(bin_file, 'rb') as bin_file:
32
+ bin_data = bin_file.read()
33
+
34
+ # 生成C文件内容
35
+ c_file_content = f"""#ifdef __has_include
36
+ #if __has_include("lvgl.h")
37
+ #ifndef LV_LVGL_H_INCLUDE_SIMPLE
38
+ #define LV_LVGL_H_INCLUDE_SIMPLE
39
+ #endif
40
+ #endif
41
+ #endif
42
+
43
+ #if defined(LV_LVGL_H_INCLUDE_SIMPLE)
44
+ #include "lvgl.h"
45
+ #else
46
+ #include "lvgl/lvgl.h"
47
+ #endif
48
+
49
+ #ifndef LV_ATTRIBUTE_MEM_ALIGN
50
+ #define LV_ATTRIBUTE_MEM_ALIGN
51
+ #endif
52
+
53
+ #ifndef LV_ATTRIBUTE_IMG_{image_name.upper()}
54
+ #define LV_ATTRIBUTE_IMG_{image_name.upper()}
55
+ #endif
56
+
57
+ const LV_ATTRIBUTE_MEM_ALIGN LV_ATTRIBUTE_LARGE_CONST LV_ATTRIBUTE_IMG_{image_name.upper()} uint8_t {image_name}_bin[] = {{
58
+ """
59
+
60
+ # 将二进制数据转换为C数组格式
61
+ for i, byte in enumerate(bin_data):
62
+ if i % 12 == 0:
63
+ c_file_content += '\n '
64
+ c_file_content += f'0x{byte:02x}, '
65
+
66
+ c_file_content = c_file_content.rstrip(', ') # 移除最后一个逗号和空格
67
+ c_file_content += '\n};\n\n'
68
+
69
+ # 添加图像描述符
70
+ c_file_content += f"""const lv_img_dsc_t {image_name} = {{
71
+ .header.cf = LV_IMG_CF_RAW_CHROMA_KEYED,
72
+ .header.always_zero = 0,
73
+ .header.reserved = 0,
74
+ .header.w = {image_width},
75
+ .header.h = {image_height},
76
+ .data_size = {len(bin_data)},
77
+ .data = {image_name}_bin,
78
+ }};
79
+ """
80
+
81
+ # 写入C文件
82
+ with open(lvgl_file, 'w') as c_file:
83
+ c_file.write(c_file_content)
84
+
@@ -0,0 +1,135 @@
1
+ # coding: utf-8
2
+ import sys
3
+ import os
4
+ from PIL import Image, ImageFilter
5
+ import cv2
6
+ import numpy as np
7
+ from tqdm import tqdm
8
+
9
+
10
+ def map_strength_to_parameters(strength):
11
+ if strength < 0 or strength > 10:
12
+ raise ValueError("强度值应在0到10之间")
13
+
14
+ # 非线性映射,使用指数函数控制强度
15
+ min_sigma = 0.00001 # 极低时接近不处理
16
+ max_sigma = 25.0 # 高强度时的最大滤波参数
17
+
18
+ sigma = min_sigma + (max_sigma - min_sigma) * (strength / 10.0)
19
+ return sigma
20
+
21
+ def apply_bilateral_filter(image, strength):
22
+ image_cv = np.array(image)
23
+ image_cv = cv2.cvtColor(image_cv, cv2.COLOR_RGB2BGR)
24
+ sigma = map_strength_to_parameters(strength)
25
+ filtered_image_cv = cv2.bilateralFilter(image_cv, d=5, sigmaColor=sigma, sigmaSpace=sigma)
26
+ filtered_image_cv = cv2.cvtColor(filtered_image_cv, cv2.COLOR_BGR2RGB)
27
+ return Image.fromarray(filtered_image_cv)
28
+
29
+ def reduce_quantization_noise(image, strength=10):
30
+ image_cv = np.array(image)
31
+ image_lab = cv2.cvtColor(image_cv, cv2.COLOR_RGB2Lab)
32
+ L, a, b = cv2.split(image_lab)
33
+ L_filtered = cv2.bilateralFilter(L, d=9, sigmaColor=strength, sigmaSpace=strength)
34
+ image_filtered = cv2.merge((L_filtered, a, b))
35
+ image_rgb = cv2.cvtColor(image_filtered, cv2.COLOR_Lab2RGB)
36
+ return Image.fromarray(image_rgb)
37
+
38
+ def apply_interframe_median_filter(prev_frame, curr_frame, next_frame, threshold):
39
+ prev_frame_np = np.array(prev_frame)
40
+ curr_frame_np = np.array(curr_frame)
41
+ next_frame_np = np.array(next_frame)
42
+ mask = (np.abs(curr_frame_np - prev_frame_np) < threshold) & (prev_frame_np == next_frame_np)
43
+ curr_frame_np[mask] = prev_frame_np[mask]
44
+ return Image.fromarray(curr_frame_np)
45
+
46
+ def apply_temporal_denoise(display_frame, curr_frame, threshold):
47
+ display_frame_np = np.array(display_frame)
48
+ curr_frame_np = np.array(curr_frame)
49
+ mask = np.abs(curr_frame_np - display_frame_np) < threshold
50
+ curr_frame_np[mask] = display_frame_np[mask]
51
+ return Image.fromarray(curr_frame_np)
52
+
53
+ def crop_and_resize(image, crop_size=None, scale_size=None):
54
+ if crop_size:
55
+ width, height = image.size
56
+ crop_width, crop_height = crop_size
57
+ left = (width - crop_width) / 2
58
+ top = (height - crop_height) / 2
59
+ right = (width + crop_width) / 2
60
+ bottom = (height + crop_height) / 2
61
+ image = image.crop((left, top, right, bottom))
62
+
63
+ if scale_size:
64
+ image = image.resize(scale_size, Image.BILINEAR)
65
+
66
+ return image
67
+
68
+ def extract_frames(gif_path, output_dir, crop_size=None, scale_size=None,
69
+ bilateral_strength=0, median_strength=0, interframe_threshold=0, temporal_threshold=0):
70
+ gif = Image.open(gif_path)
71
+
72
+ gif_info = {}
73
+ gif_info["frames"] = gif.n_frames
74
+ gif_info["duration"] = gif.info["duration"] # 获取第一帧的持续时间
75
+
76
+ width, height = gif.size
77
+ if crop_size is not None:
78
+ width, height = crop_size
79
+ if scale_size is not None:
80
+ width, height = scale_size
81
+ gif_info["width"], gif_info["height"] = width, height
82
+
83
+ if gif_info["width"] % 4 != 0 or gif_info["height"] % 4 != 0:
84
+ print(f"img width {gif_info['width']} or height {gif_info['height']} is incorrect, must be aligned to 4")
85
+ sys.exit(1)
86
+
87
+ if not os.path.exists(output_dir):
88
+ os.makedirs(output_dir)
89
+
90
+ frames = []
91
+ display_frame = None
92
+ frame_count = 0
93
+ try:
94
+ while True:
95
+ frame = gif.convert("RGB")
96
+
97
+ # 裁剪和缩放处理
98
+ frame = crop_and_resize(frame, crop_size=crop_size, scale_size=scale_size)
99
+
100
+ # 双边滤波处理
101
+ if bilateral_strength > 0:
102
+ frame = apply_bilateral_filter(frame, bilateral_strength)
103
+
104
+ # 空域中值滤波处理
105
+ if median_strength > 0:
106
+ frame = frame.filter(ImageFilter.MedianFilter(size=int(median_strength * 2 + 1)))
107
+
108
+ frames.append(frame)
109
+ frame_count += 1
110
+ gif.seek(gif.tell() + 1)
111
+ except EOFError:
112
+ pass
113
+
114
+ # 时域中值滤波
115
+ if len(frames) > 1 and interframe_threshold > 0:
116
+ prev_frame = frames[-1]
117
+ for frame_index in tqdm(range(len(frames)), desc="Applying interframe median filter"):
118
+ next_frame = frames[(frame_index + 1) % len(frames)]
119
+ frames[frame_index] = apply_interframe_median_filter(prev_frame, frames[frame_index], next_frame, interframe_threshold)
120
+ prev_frame = frames[frame_index]
121
+
122
+ # 时域不更新算法
123
+ if temporal_threshold > 0:
124
+ for frame_index in tqdm(range(len(frames)), desc="Applying temporal denoise"):
125
+ if display_frame is not None:
126
+ frames[frame_index] = apply_temporal_denoise(display_frame, frames[frame_index], temporal_threshold)
127
+ display_frame = frames[frame_index]
128
+
129
+ for frame_index, frame in tqdm(enumerate(frames), total=frame_count, desc="Saving frames"):
130
+ png_filename = f"{os.path.splitext(os.path.basename(gif_path))[0]}_{frame_index}.png"
131
+ png_path = os.path.join(output_dir, png_filename)
132
+ frame.save(png_path, "PNG")
133
+
134
+ return gif_info
135
+
qgif-0.1.0/qgif/qgif ADDED
@@ -0,0 +1,61 @@
1
+ #!/usr/bin/env python
2
+
3
+ import sys
4
+ import os
5
+ import argparse
6
+
7
+ from qgif import VERSION
8
+ from qgif.qgif import convert_gif_to_qgif, decode_qgif
9
+
10
+ def check_range(param_name, t):
11
+ def checker(value):
12
+ value = t(value)
13
+ if value < 0 or value > 10:
14
+ raise argparse.ArgumentTypeError(f"Argument '{param_name}' must be between 0 and 10, but got {value}")
15
+ return value
16
+ return checker
17
+
18
+ if __name__ == "__main__":
19
+ parser = argparse.ArgumentParser(prog="qgif", description="QGIF generator and decoder")
20
+ subparsers = parser.add_subparsers(dest="command")
21
+
22
+ parser_convert = subparsers.add_parser("convert", help="Convert GIF to QGIF.")
23
+ parser_convert.add_argument("-f", "--format", required=True, choices=["gx64", "gx96"], help="The format of generated QGIF frames.")
24
+ parser_convert.add_argument("-i", "--input", required=True, help="The input GIF file name.")
25
+ parser_convert.add_argument("-o", "--output", required=True, help="Generated QGIF file name.")
26
+ parser_convert.add_argument("-fr", "--framerate", type=int, default=0, help="The frame rate of QGIF file. (default: same as GIF)")
27
+ parser_convert.add_argument("-l", "--lvgl", action="store_true", help="Generate LVGL C file.")
28
+ parser_convert.add_argument("--crop-size", type=int, nargs=2, help="Size to crop the image (width height)")
29
+ parser_convert.add_argument("--scale-size", type=int, nargs=2, help="Size to scale the image (width height)")
30
+ parser_convert.add_argument("--bilateral-strength", default=0, type=check_range("--bilateral-strength", float),
31
+ help="Strength of bilateral filter (0-10, can be a float)")
32
+ parser_convert.add_argument("--median-strength", default=0, type=check_range("--median-strength", int),
33
+ help="Strength of spatial median filter (0-10)")
34
+ parser_convert.add_argument("--interframe-threshold", default=0, type=check_range("--interframe-threshold", int),
35
+ help="Threshold for interframe median filtering (0-10)")
36
+ parser_convert.add_argument("--temporal-threshold", default=0, type=check_range("--temporal-threshold", int),
37
+ help="Threshold for temporal denoise (0-10)")
38
+
39
+ parser_decode = subparsers.add_parser("decode", help="Decode QGIF to frames.")
40
+ parser_decode.add_argument("-i", "--input", required=True, help="The input QGIF file name.")
41
+ parser_decode.add_argument("-o", "--output", required=True, help="The directory of output frames and pngs.")
42
+ parser_decode.add_argument("-c", "--colordepth", type=int, required=True, choices=[16, 32], help="The color depth of decoded frames.")
43
+
44
+ parser.add_argument("-V", "--version", action="version", version="qgif %s" % VERSION)
45
+
46
+ args = parser.parse_args()
47
+ if args.command == "convert":
48
+ convert_gif_to_qgif(args.input,
49
+ args.output,
50
+ args.format,
51
+ args.lvgl,
52
+ args.framerate,
53
+ crop_size=args.crop_size,
54
+ scale_size=args.scale_size,
55
+ bilateral_strength=args.bilateral_strength,
56
+ median_strength=args.median_strength,
57
+ interframe_threshold=args.interframe_threshold,
58
+ temporal_threshold=args.temporal_threshold)
59
+ elif args.command == "decode":
60
+ decode_qgif(args.input, args.output, args.colordepth)
61
+
@@ -0,0 +1,182 @@
1
+ # coding: utf-8
2
+ import os
3
+ import sys
4
+ import signal
5
+ import ctypes
6
+ from ctypes import CFUNCTYPE, c_int
7
+ import numpy as np
8
+ import time
9
+ from PIL import Image
10
+ from tqdm import tqdm
11
+
12
+ from qgif.gif2png import extract_frames
13
+ from qgif.gen_lvgl_file import gen_lvgl_file
14
+
15
+ qgif_lib_path = os.path.join(os.path.dirname(__file__), "./qgif_C/libqgif.so")
16
+ qgif_lib = ctypes.CDLL(qgif_lib_path)
17
+
18
+ exit_flag = False
19
+ def signal_handler(sig, frame):
20
+ global exit_flag
21
+ exit_flag = True
22
+ sys.exit(1)
23
+
24
+ signal.signal(signal.SIGINT, signal_handler)
25
+
26
+ def get_filename_without_extension(file_path):
27
+ file_name_with_extension = os.path.basename(file_path)
28
+ file_name, _ = os.path.splitext(file_name_with_extension)
29
+ return file_name
30
+
31
+ def replace_extension(file_path, extension):
32
+ base = os.path.splitext(file_path)[0]
33
+ return base + extension
34
+
35
+ def get_compress_type_value(compress_type):
36
+ # 0: gx64
37
+ # 1: gx64a
38
+ # 2: gx96
39
+ # 3: gx96a
40
+ compress_types = {
41
+ "gx64" : 0,
42
+ "gx96" : 2,
43
+ }
44
+ return compress_types[compress_type]
45
+
46
+ """
47
+ void compress_video(const char *image_path, const char *output_path, uint8_t compress_type, uint8_t frame_rate, int (*progress_callback)(int));
48
+ """
49
+ qgif_lib.compress_video.argtypes = [ctypes.c_char_p, ctypes.c_char_p, ctypes.c_uint8, ctypes.c_uint8, CFUNCTYPE(c_int, c_int)]
50
+ qgif_lib.compress_video.restype = None
51
+
52
+
53
+ """
54
+ void decode_qgif(const char *input_path, const char *output_path, unsigned char color_depth, int *width, int *height);
55
+ """
56
+ qgif_lib.decode_qgif.argtypes = [ctypes.c_char_p, ctypes.c_char_p, ctypes.c_uint8,
57
+ ctypes.POINTER(ctypes.c_int), ctypes.POINTER(ctypes.c_int)]
58
+ qgif_lib.decode_qgif.restype = None
59
+
60
+
61
+ def convert_gif_to_qgif(input_path, output_path, qgif_format, need_output_lvgl, frame_rate=0,\
62
+ crop_size=None, scale_size=None, bilateral_strength=0, median_strength=0,\
63
+ interframe_threshold=0, temporal_threshold=0):
64
+ png_path = "_tmp"
65
+ if not os.path.exists(png_path):
66
+ os.makedirs(png_path)
67
+ input_name = get_filename_without_extension(input_path)
68
+ input_png_path = os.path.join(png_path, f"{input_name}_X.png")
69
+
70
+ # gif to png
71
+ print("Convert gif to png ...")
72
+ gif_info = extract_frames(input_path, png_path, crop_size, scale_size, bilateral_strength, median_strength,
73
+ interframe_threshold, temporal_threshold)
74
+ print("Convert gif to png finish.")
75
+
76
+ # png to qgif
77
+ print("Convert png to qgif ...")
78
+ progress_bar = tqdm(total=gif_info["frames"])
79
+
80
+ if frame_rate < 1 or frame_rate > 64:
81
+ frame_rate = int(1000. / gif_info["duration"])
82
+
83
+ global exit_flag
84
+ def progress_callback(frame_index):
85
+ progress_bar.n = frame_index
86
+ progress_bar.refresh()
87
+ if exit_flag:
88
+ return True
89
+ else:
90
+ return False
91
+
92
+ PROGRESS_CALLBACK = CFUNCTYPE(c_int, c_int)
93
+ c_progress_callback = PROGRESS_CALLBACK(progress_callback)
94
+
95
+ compress_type_value = get_compress_type_value(qgif_format)
96
+ qgif_lib.compress_video(input_png_path.encode("utf-8"), output_path.encode("utf-8"), compress_type_value,
97
+ frame_rate, c_progress_callback)
98
+ os.system(f"rm -r {png_path}")
99
+ progress_bar.close()
100
+ if exit_flag:
101
+ sys.exit(1)
102
+ print(f"Convert png to qgif {output_path} finish.")
103
+
104
+ if need_output_lvgl:
105
+ print("Generate LVGL C file ...")
106
+ bin_path = output_path
107
+ lvgl_path = replace_extension(bin_path, ".c")
108
+ gen_lvgl_file(bin_path, lvgl_path, gif_info["width"], gif_info["height"])
109
+ print(f"Generate LVGL C file {lvgl_path} finish.")
110
+
111
+ print(f"============================")
112
+ print(f"QGIF {output_path}:")
113
+ print(f"frames: {gif_info['frames']}")
114
+ print(f"framerate: {frame_rate}")
115
+ print(f"img width: {gif_info['width']}, height: {gif_info['height']}")
116
+ print(f"============================")
117
+
118
+ def decode_qgif(input_path, output_path, color_depth):
119
+ if not os.path.exists(output_path):
120
+ os.makedirs(output_path)
121
+ output_frame_path = os.path.join(output_path, "frames")
122
+ if not os.path.exists(output_frame_path):
123
+ os.makedirs(output_frame_path)
124
+ output_png_path = os.path.join(output_path, "pngs")
125
+ if not os.path.exists(output_png_path):
126
+ os.makedirs(output_png_path)
127
+
128
+ print("Decode qgif to frames ...")
129
+ width = ctypes.c_int()
130
+ height = ctypes.c_int()
131
+ qgif_lib.decode_qgif(input_path.encode("utf-8"), output_frame_path.encode("utf-8"), color_depth,
132
+ ctypes.byref(width), ctypes.byref(height))
133
+ print("Decode qgif to frames finish.")
134
+
135
+ print("Save frames to pngs ...")
136
+ display_images(output_frame_path, output_png_path, width.value, height.value, color_depth)
137
+ print("Save frames to pngs finish.")
138
+
139
+
140
+ def rgb565_to_rgb888(rgb565):
141
+ rgb565 = ((rgb565 >> 8) & 0xff) | ((rgb565 & 0xff) << 8)
142
+ r = ((rgb565 >> 11) & 0x1F) << 3
143
+ g = ((rgb565 >> 5) & 0x3F) << 2
144
+ b = (rgb565 & 0x1F) << 3
145
+ return (r, g, b)
146
+
147
+ def convert_rgb888_to_image(data, width, height):
148
+ img = Image.new("RGB", (width, height))
149
+ pixels = img.load()
150
+ for y in range(height):
151
+ for x in range(width):
152
+ index = y * width + x
153
+ pixels[x, y] = (data[index] & 0xff, (data[index] >> 8) & 0xff, (data[index] >> 16) & 0xff)
154
+ return img
155
+
156
+ def convert_rgb565_to_image(data, width, height):
157
+ img = Image.new("RGB", (width, height))
158
+ pixels = img.load()
159
+ for y in range(height):
160
+ for x in range(width):
161
+ index = y * width + x
162
+ rgb565 = data[index]
163
+ pixels[x, y] = rgb565_to_rgb888(rgb565)
164
+ return img
165
+
166
+ def display_images(frame_folder, png_folder, width, height, color_depth):
167
+ files = [f for f in os.listdir(frame_folder) if f.endswith(".rgb")]
168
+ files = [f for f in files if f.split(".")[0].isdigit()]
169
+ sorted_files = sorted(files, key=lambda x: int(x.split(".")[0]))
170
+
171
+ for image_file in tqdm(sorted_files):
172
+ with open(os.path.join(frame_folder, image_file), "rb") as f:
173
+ rgb_image = f.read()
174
+ if color_depth == 32:
175
+ rgb888_data = np.frombuffer(rgb_image, dtype=np.uint32)
176
+ img = convert_rgb888_to_image(rgb888_data, width, height)
177
+ elif color_depth == 16:
178
+ rgb565_data = np.frombuffer(rgb_image, dtype=np.uint16)
179
+ img = convert_rgb565_to_image(rgb565_data, width, height)
180
+ image_name = get_filename_without_extension(image_file)
181
+ img.save(os.path.join(png_folder, image_name + ".png"))
182
+
Binary file
@@ -0,0 +1,13 @@
1
+ Metadata-Version: 2.1
2
+ Name: qgif
3
+ Version: 0.1.0
4
+ Summary: Convert GIF to QGIF, or decode QGIF.
5
+ Author: Hangzhou Nationalchip Inc.
6
+ Author-email: zhengdi@nationalchip.com
7
+ License: MIT Licence
8
+ Keywords: qgif nationalchip
9
+ Classifier: Development Status :: 3 - Alpha
10
+ Classifier: Intended Audience :: Developers
11
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
12
+ Classifier: License :: OSI Approved :: MIT License
13
+ Classifier: Programming Language :: Python :: 3
@@ -0,0 +1,13 @@
1
+ setup.py
2
+ qgif/__init__.py
3
+ qgif/__main__.py
4
+ qgif/gen_lvgl_file.py
5
+ qgif/gif2png.py
6
+ qgif/qgif
7
+ qgif/qgif.py
8
+ qgif.egg-info/PKG-INFO
9
+ qgif.egg-info/SOURCES.txt
10
+ qgif.egg-info/dependency_links.txt
11
+ qgif.egg-info/requires.txt
12
+ qgif.egg-info/top_level.txt
13
+ qgif/qgif_C/libqgif.so
@@ -0,0 +1,4 @@
1
+ numpy
2
+ pillow
3
+ tqdm
4
+ opencv-python
@@ -0,0 +1 @@
1
+ qgif
qgif-0.1.0/setup.cfg ADDED
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
qgif-0.1.0/setup.py ADDED
@@ -0,0 +1,54 @@
1
+ # Copyright 2024 The QGIF Authors. All Rights Reserved.
2
+ #
3
+ # Licensed under the Proprietary License;
4
+ # you may not use this file except in compliance with the License.
5
+
6
+ # Unless required by applicable law or agreed to in writing, software
7
+ # distributed under the License is distributed on an "AS IS" BASIS,
8
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
9
+ # See the License for the specific language governing permissions and
10
+ # limitations under the License.
11
+ # ==============================================================================
12
+ from setuptools import find_packages, setup
13
+ from setuptools.command.build_py import build_py
14
+ import os
15
+
16
+ from qgif import VERSION
17
+
18
+ REQUIRED_PACKAGES = [
19
+ 'numpy',
20
+ 'pillow',
21
+ 'tqdm',
22
+ 'opencv-python'
23
+ ]
24
+
25
+ setup(
26
+ name='qgif',
27
+ version=VERSION,
28
+ description='Convert GIF to QGIF, or decode QGIF.',
29
+ author='Hangzhou Nationalchip Inc.',
30
+ author_email='zhengdi@nationalchip.com',
31
+ license='MIT Licence',
32
+
33
+ # PyPI package information.
34
+ classifiers=[
35
+ 'Development Status :: 3 - Alpha',
36
+ 'Intended Audience :: Developers',
37
+ 'Topic :: Software Development :: Libraries :: Python Modules',
38
+ 'License :: OSI Approved :: MIT License',
39
+ 'Programming Language :: Python :: 3',
40
+ ],
41
+
42
+ keywords='qgif nationalchip',
43
+
44
+ # Contained modules and scripts.
45
+ packages=find_packages(),
46
+ install_requires=REQUIRED_PACKAGES,
47
+
48
+ package_data={
49
+ 'qgif': ['qgif_C/libqgif.so']
50
+ },
51
+ scripts=['qgif/qgif'],
52
+ entry_points={
53
+ },
54
+ )