├── LICENCE ├── README.md ├── _tweet.png ├── catgirltogds.py └── requirements.txt /LICENCE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2020 the6p4c 2 | 3 | Permission to use, copy, modify, and/or distribute this software for any 4 | purpose with or without fee is hereby granted. 5 | 6 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH 7 | REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 8 | AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, 9 | INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM 10 | LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR 11 | OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 12 | PERFORMANCE OF THIS SOFTWARE. 13 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | catgirltogds 2 | ============ 3 | converts catgirls to gds files 4 | 5 | ![](_tweet.png) 6 | 7 | ``` 8 | usage: catgirltogds.py [-h] [--rects] img [gds] [cell_name] 9 | 10 | convert images (catgirls) to gds 11 | 12 | positional arguments: 13 | img image to convert 14 | gds gds to export, default to image filename with gds extension 15 | cell_name name of the cell, default to image filename 16 | 17 | optional arguments: 18 | -h, --help show this help message and exit 19 | --rects try to use filling rects (vv slow) 20 | ``` 21 | -------------------------------------------------------------------------------- /_tweet.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/the6p4c/catgirltogds/0901492813b123e20a06114a1dcc17b58d05038d/_tweet.png -------------------------------------------------------------------------------- /catgirltogds.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | import argparse 3 | import gdspy 4 | from PIL import Image 5 | 6 | def parse_args(): 7 | parser = argparse.ArgumentParser(description='convert images (catgirls) to gds') 8 | parser.add_argument('img', help='image to convert') 9 | parser.add_argument('gds', nargs='?', help='gds to export, default to image filename with gds extension') 10 | parser.add_argument('cell_name', nargs='?', help='name of the cell, default to image filename') 11 | parser.add_argument('--rects', action='store_const', const=True, default=False, help='try to use filling rects (vv slow)') 12 | return parser.parse_args() 13 | 14 | def is_set(p): 15 | return p == 255 16 | 17 | def rectangles(img_data, img_width, img_height): 18 | rects = [] 19 | 20 | while True: 21 | left = [0] * img_width 22 | right = [img_width] * img_width 23 | height = [0] * img_width 24 | 25 | rect = ((0, 0, 0, 0), 0) 26 | for y in range(img_height): 27 | cur_left, cur_right = 0, img_width 28 | for x in range(img_width): 29 | if is_set(img_data[x, y]): 30 | height[x] += 1 31 | else: 32 | height[x] = 0 33 | for x in range(img_width): 34 | if is_set(img_data[x, y]): 35 | left[x] = max(left[x], cur_left) 36 | else: 37 | left[x] = 0 38 | cur_left = x + 1 39 | for x in range(img_width - 1, -1, -1): 40 | if is_set(img_data[x, y]): 41 | right[x] = min(right[x], cur_right) 42 | else: 43 | right[x] = img_width 44 | cur_right = x 45 | for x in range(img_width): 46 | rect2 = ((left[x], y - height[x] + 1, right[x], y + 1), height[x] * (right[x] - left[x])) 47 | rect = max(rect, rect2, key=lambda p: p[1]) 48 | if rect[1] == 0: 49 | return rects 50 | else: 51 | rects.append(rect[0]) 52 | 53 | x1, y1, x2, y2 = rect[0] 54 | w, h = x2 - x1, y2 - y1 55 | 56 | if w <= 5 and h <= 5: 57 | for y in range(img_height): 58 | for x in range(img_width): 59 | if is_set(img_data[x, y]): 60 | rects.append((x, y, x + 1, y + 1)) 61 | return rects 62 | 63 | for dy in range(h): 64 | for dx in range(w): 65 | img_data[x1 + dx, y1 + dy] = 0 66 | 67 | def main(): 68 | args = parse_args() 69 | 70 | img_path = args.img 71 | img_filename = img_path.split('.')[0] 72 | gds_path = args.gds if args.gds is not None else f'{img_filename}.gds' 73 | cell_name = args.cell_name if args.cell_name is not None else f'{img_filename}' 74 | rects = args.rects 75 | 76 | img = Image.open(img_path) 77 | width, height = img.size 78 | 79 | channels = img.split()[:3] 80 | channels = [channel.convert('1') for channel in channels] 81 | 82 | lib = gdspy.GdsLibrary() 83 | cell = lib.new_cell(cell_name) 84 | 85 | for i, img in enumerate(channels): 86 | img_data = img.load() 87 | if rects: 88 | for x1, y1, x2, y2 in rectangles(img_data, width, height): 89 | cell.add(gdspy.Rectangle((x1, height - y1 - 1), (x2, height - y2 - 1), layer=i)) 90 | else: 91 | for y in range(height): 92 | for x in range(width): 93 | if is_set(img_data[x, y]): 94 | cell.add(gdspy.Rectangle((x, height - y - 1), (x + 1, height - y), layer=i)) 95 | 96 | lib.write_gds(gds_path) 97 | 98 | if __name__ == '__main__': 99 | main() 100 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | gdspy==1.6.4 2 | numpy==1.20.3 3 | Pillow==8.2.0 4 | --------------------------------------------------------------------------------