image2prime.pl 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196
  1. #!/usr/bin/perl
  2. # Author: Trizen
  3. # Date: 29 April 2022
  4. # https://github.com/trizen
  5. # Generate an ASCII representation for an image, using only digits, such that the number is a prime.
  6. # Inspired by the following Matt Parker video:
  7. # https://yewtu.be/watch?v=dET2l8l3upU
  8. # See also:
  9. # https://github.com/TotalTechGeek/pictoprime
  10. use 5.010;
  11. use strict;
  12. use autodie;
  13. use warnings;
  14. use GD qw();
  15. use List::Util qw(max);
  16. use Getopt::Long qw(GetOptions);
  17. use Math::Prime::Util::GMP qw(is_prob_prime);
  18. use constant {
  19. GENERATE_PRIME => 1, # true to generate primes (slow)
  20. };
  21. GD::Image->trueColor(1);
  22. my $size = 80;
  23. sub help {
  24. my ($code) = @_;
  25. print <<"HELP";
  26. usage: $0 [options] [files]
  27. options:
  28. -w --width=i : width size of the ASCII image (default: $size)
  29. example:
  30. perl $0 --width 200 image.png
  31. HELP
  32. exit($code);
  33. }
  34. GetOptions('w|width=s' => \$size,
  35. 'h|help' => sub { help(0) },)
  36. or die "Error in command-line arguments!";
  37. sub map_value {
  38. my ($value, $in_min, $in_max, $out_min, $out_max) = @_;
  39. ($value - $in_min) * ($out_max - $out_min) / ($in_max - $in_min) + $out_min;
  40. }
  41. my @digits = split(//, "7772299408");
  42. #my @digits = 0..9;
  43. # The ways that we allow the algorithm to substitute a character.
  44. # Like 0 can become 8 or 9, so on and so forth.
  45. my %substitutions = (
  46. '0' => ['8', '9'],
  47. '1' => ['7'],
  48. '7' => ['1'],
  49. '8' => ['0', '9'],
  50. '9' => ['4'],
  51. '4' => ['9'],
  52. );
  53. # These are used to swap out the last digit if necessary.
  54. my %edge_digit_substitutions = (
  55. '0' => '3',
  56. '2' => '3',
  57. '4' => '9',
  58. '6' => '9',
  59. '8' => '9',
  60. '5' => '3'
  61. );
  62. sub create_prime {
  63. my ($pixels) = @_;
  64. GENERATE_PRIME || return $pixels;
  65. if (substr($pixels, 0, 1) == 0) {
  66. substr($pixels, 0, 1, $edge_digit_substitutions{0});
  67. }
  68. if (exists($edge_digit_substitutions{substr($pixels, -1)})) {
  69. my $digit = chop $pixels;
  70. $pixels .= $edge_digit_substitutions{$digit};
  71. }
  72. my $count = 0;
  73. my $copy = $pixels;
  74. my $length = length($pixels);
  75. my @substitution_indices = grep { exists $substitutions{substr($pixels, $_, 1)} } 0 .. $length - 1;
  76. while (1) {
  77. if (is_prob_prime($pixels)) {
  78. return $pixels;
  79. }
  80. if (++$count > 5) {
  81. $pixels = $copy;
  82. $count = 0;
  83. }
  84. my $rand = $substitution_indices[int rand scalar @substitution_indices];
  85. my $digit = substr($pixels, $rand, 1);
  86. my $alt = $substitutions{$digit};
  87. substr($pixels, $rand, 1, $alt->[int rand scalar @$alt]);
  88. }
  89. }
  90. sub img2prime {
  91. my ($image) = @_;
  92. my $img = GD::Image->new($image) // return;
  93. my ($width, $height) = $img->getBounds;
  94. if ($size != 0) {
  95. my $scale_width = $size;
  96. my $scale_height = int($height / ($width / ($size / 2)));
  97. my $resized = GD::Image->new($scale_width, $scale_height);
  98. $resized->copyResampled($img, 0, 0, 0, 0, $scale_width, $scale_height, $width, $height);
  99. ($width, $height) = ($scale_width, $scale_height);
  100. $img = $resized;
  101. }
  102. my $avg = 0;
  103. my @averages;
  104. foreach my $y (0 .. $height - 1) {
  105. foreach my $x (0 .. $width - 1) {
  106. my $index = $img->getPixel($x, $y);
  107. my ($r, $g, $b) = $img->rgb($index);
  108. my $value = max($r, $g, $b);
  109. push @averages, $digits[map_value($value, 0, 255, 0, $#digits)];
  110. }
  111. }
  112. my $prime = create_prime(join('', @averages));
  113. unpack("(A$width)*", $prime);
  114. }
  115. say for img2prime($ARGV[0] // help(1));
  116. __END__
  117. 30000000000000000000000000000000000000000000000000000000000000000000000000000000
  118. 00000000000000000000000000000000000000000000000000000000000000000000000000000000
  119. 00000000000000000000000000000000000000000000000000000000000000000000000000000000
  120. 00000000000000000000000000000000000000000000000000000000000000000000000000000000
  121. 00000000000000000000000000000000000000000000000000000000000000000000000000000000
  122. 00000000000000000000001002222333222221100000000000000000000000000000000000000000
  123. 00000000000000000001101343344444444333332211000000000000000000000000000000000000
  124. 00000000000000000000355455555555556666666665544332100000000000000000000000000000
  125. 00000000000000003543245666666666777777777666665544321000000000000000000000000000
  126. 00000000000000001256666666677777777777777776666655432100000000000000000000000000
  127. 00000000011144433225666678888888876555556655543433322100000000000000000000000000
  128. 00000000012234667777767888888753135554300134310000000000000000000000000000000000
  129. 00000000001233356777888888884013366653000000000000000000000110000000000000000000
  130. 00000000000013345678888888711335667631000000000000000000000000010000000000000000
  131. 00001124556666656888888888123456776520000000000000111111100000011110000000000000
  132. 00033334567777788888888885245667776651000000000010000000000000001110001000000000
  133. 00021034355667788888888884456778877766520000000100000000022000000100000100000000
  134. 00000013044455888888888887788888888875300000000110000000132032111000000110000000
  135. 00000000012053888888888888888888888888400000000111001110220301211000001110000000
  136. 00000000000020788888888888888888888888830000001000000022021013133211001100000000
  137. 00000000000020788888888888888888888888830000010000000001221121121011000001000000
  138. 00000000013054888888888888888888888888400000001000001222222122231000000001000000
  139. 00000013044455888888888887778888888885400000000001100000001221021000000010000000
  140. 00022034355677788888888884456668877766420000000001110000011000000001111000000000
  141. 00033335567777788888888885235667776651000000000000100000000000000001100000000000
  142. 00000124455666655888888888123456777520000800000000011000011110000011000000000000
  143. 00000000000023345678888888711334667641000000000000000110000011111100000000000000
  144. 00000000001233356777888888884113366653000000000000000000000000000000000000000000
  145. 00000000012234667777767888888753135554300134310000000000000000000000000000000000
  146. 00000000011134332225666678888888876555556655544433332100000000000000000000000000
  147. 00000000000000001256666666667777777777777776666655432100000000000000000000000000
  148. 00000000000000013443245666666666777777777666665544321000000000000000000000000000
  149. 00000000000000000000354455555555555666665555543321100000000000000000000000000000
  150. 00000000000000000001101343344344434343332210000000000000000000000000000000000000
  151. 00000000000000000000001002222223222221100000000000000000000000000000000000000000
  152. 00000000000000000000000000000000000000000000000000000000000000000000000000000000
  153. 00000000000000000000000000000000000000000000000000000000000000000000000000000000
  154. 00000000000000000000000000000000000000000000000000000000000000000000000000000000
  155. 00000000000000000000000000000000000000000000000000000000000000000000000000000000
  156. 00000000000000000000000000000000000000000000000000000000000000000000000000000003