├── .gitignore ├── image_process ├── 06_final.png ├── 07_mapped.png ├── 01_original.png ├── 02_cropped.png ├── 03_cleaned.png ├── 04_removed_line.png └── 05_reconstructed_points.png ├── README.md ├── points.csv ├── LICENSE.md └── extractor.py /.gitignore: -------------------------------------------------------------------------------- 1 | ._* 2 | 3 | .DS_Store 4 | .Trashes -------------------------------------------------------------------------------- /image_process/06_final.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kamik423/xkcd-2048/HEAD/image_process/06_final.png -------------------------------------------------------------------------------- /image_process/07_mapped.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kamik423/xkcd-2048/HEAD/image_process/07_mapped.png -------------------------------------------------------------------------------- /image_process/01_original.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kamik423/xkcd-2048/HEAD/image_process/01_original.png -------------------------------------------------------------------------------- /image_process/02_cropped.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kamik423/xkcd-2048/HEAD/image_process/02_cropped.png -------------------------------------------------------------------------------- /image_process/03_cleaned.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kamik423/xkcd-2048/HEAD/image_process/03_cleaned.png -------------------------------------------------------------------------------- /image_process/04_removed_line.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kamik423/xkcd-2048/HEAD/image_process/04_removed_line.png -------------------------------------------------------------------------------- /image_process/05_reconstructed_points.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kamik423/xkcd-2048/HEAD/image_process/05_reconstructed_points.png -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # xkcd #2048 2 | 3 | A Python script to extract the point coordinates from the comic. 4 | 5 | ![comic](https://imgs.xkcd.com/comics/curve_fitting.png) 6 | ![extracted](image_process/07_mapped.png) 7 | 8 | Licensed under the [MIT license](LICENSE.md). 9 | -------------------------------------------------------------------------------- /points.csv: -------------------------------------------------------------------------------- 1 | 227.1,258.4 2 | 245.7,221.2 3 | 279.8,220.3 4 | 273.5,203.0 5 | 271.3,190.2 6 | 256.2,184.4 7 | 284.3,184.2 8 | 64.2,181.5 9 | 267.0,164.6 10 | 227.0,151.5 11 | 121.9,143.3 12 | 211.2,142.5 13 | 231.2,138.5 14 | 227.6,120.9 15 | 267.9,120.3 16 | 53.2,117.4 17 | 196.7,102.9 18 | 116.0,102.2 19 | 91.2,84.4 20 | 77.6,82.4 21 | 132.1,81.1 22 | 83.3,73.1 23 | 141.5,72.6 24 | 56.8,62.7 25 | 134.1,62.3 26 | 143.1,58.1 27 | 72.1,52.1 28 | 186.8,49.7 29 | 162.1,45.1 30 | 27.5,44.8 31 | 262.0,20.9 32 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | # The MIT License (MIT) 2 | 3 | Copyright © 2018 Hans Schülein 4 | 5 | Permission is hereby granted, free of charge, to any person 6 | obtaining a copy of this software and associated documentation 7 | files (the “Software”), to deal in the Software without 8 | restriction, including without limitation the rights to use, 9 | copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the 11 | Software is furnished to do so, subject to the following 12 | conditions: 13 | 14 | The above copyright notice and this permission notice shall be 15 | included in all copies or substantial portions of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, 18 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 19 | OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 20 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 21 | HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 22 | WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 23 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 24 | OTHER DEALINGS IN THE SOFTWARE. 25 | -------------------------------------------------------------------------------- /extractor.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | """A Python script to extract the point coordinates from the comic. 3 | """ 4 | 5 | import numpy as np 6 | 7 | from matplotlib import pyplot as plt 8 | from scipy import ndimage 9 | 10 | def main(): 11 | """Do the main processing. 12 | """ 13 | # Load image 14 | image_raw = plt.imread('image_process/06_final.png') 15 | # Convert to binary to make finding easier - via alpha threshold 16 | image_bin = np.array([np.array( 17 | [1 if pixel[-1] > 0.5 else 0 for pixel in row]) 18 | for row in image_raw]) 19 | # Initialize variables 20 | height = len(image_bin) 21 | points = [] 22 | # Filter grouped pixels 23 | structure = np.ones((3, 3), dtype=np.int) 24 | labeled, component_count = ndimage.measurements.label(image_bin, structure) 25 | # Compute center of mass 26 | for label in range(1, component_count + 1): 27 | matches = np.argwhere(labeled == label) 28 | center_of_mass = np.sum(matches, axis=0) / len(matches) 29 | points.append(center_of_mass) 30 | # Generate CSV 31 | csv = '' 32 | for line in points: 33 | x = line[1] 34 | # Mirror the coordinates so it is not counting from the top left but 35 | # The bottom left as in the comic 36 | y = height - line[0] 37 | csv += f'{x:.1f},{y:.1f}\n' 38 | # Plot 39 | plt.imshow(labeled) 40 | plt.scatter(*reversed(list(zip(*points))), 4, '0') 41 | # Output 42 | plt.savefig('image_process/07_mapped.png') 43 | with open('points.csv', 'w') as f: 44 | f.write(csv) 45 | plt.show() 46 | 47 | if __name__ == '__main__': 48 | main() 49 | --------------------------------------------------------------------------------