【Generative Art】はじめてのジェネラティブアート ~生成AI 『Claude 3.5』と『Python』を用いて~

創作

ジェネラティブアートという芸術がある。

コンピューターのアルゴリズムから生み出される不思議な美しさを持つ作品群だ。

私はSNSでそれらの作品に出会い、心を奪われた。

しかし、私には芸術を語る言葉も、プログラミングの知識も十分にない。

具体的な作品が抽象的なアルゴリズムから生まれるという神秘

本記事では、AIをパートナーに迎え、プログラミングを武器に、戸惑いながらも、ジェネラティブアートの世界へ歩み出す記録を残していく。

始めに

ジェネラティブアートという芸術がある。

コンピューターを使用して、アルゴリズム的に作成される芸術作品群のことである。

SNSでは多くのジェネラティブアートが多く投稿されており、私はそれらに強く惹きつけられた。

私は芸術には明るくない。しかし、これらの抽象的なアートには確かに私は魅了された

ただ美しいだけではない。 これらの作品群がアルゴリズムを通して生成されているという事実に対して、得も言われぬ神秘を感じたのだと思う。

私の目に映る象は確かにそこに存在するアート作品であり、私が感じる「美しい」「切ない」「穏やかだ」「かわいい」「クールだ」そして「なんか分かんないけどすごい」といった感情も確かに私の心に存在している。

それでも、この「具体的」な作品群を産み出しているのは、プログラム、アルゴリズム、あるいは数学的・物理学的な理論のような「抽象的」概念である。

その事実が、実に神秘的である

多くのジェネラティブアート作品に触れる中で、美的な感動と同時に、「なぜ美しいと感じたのだろう」「作者は何を美しいと感じたのだろう」そして「私は何を美しいと感じるのだろう」という探求心も湧いてくる。

私は芸術に明るくない。芸術を語るだけの語彙を持たない。

私はコンピューターサイエンスやアルゴリズム、数学や物理学に詳しくない。先人の素晴らしい作品群を分析するだけの語彙を持たない。

この世界に足を踏み入れることへの躊躇いは確かにある。

しかし、その躊躇いこそが、この世界の深さを教えてくれているのかもしれない

戸惑いを感じるのならば、戸惑いを感じながら進めばよいのだ。

私も彼らと同じ世界を見たい。彼らと語らいたい。彼らと同じ場所へ行きたい。

本記事を通して、第一歩を踏み出していきたい。

進め方

  1. アートを鑑賞する
  2. 「美しい」「凄い」などの感情を湧きあがらせる
  3. なぜそう感じたのか、言語化を試みる
  4. より具体的な理論的仮説を建てる
  5. 仮説→実装→検証→評価のサイクルを回す
  6. 具体的な作品を創る

このプロセスは、具体的な作品との出会いから抽象的な理論化へ、そして再び具体的な創作へと移行する中で、創作者としての経験を蓄積していく。

このプロセスの特徴は、なるべく参入の障壁を小さくし、初学者が実際に「始められる」形に構造化した点にある。

完璧な準備や厳密な計画を求めるのではなく、純粋な感動と探求心を出発点として、創作者それぞれの歩みを支える枠組みとなることを目指している。

実行と結果

  • ツールは、汎用性を考えてpythonを使用する。
  • 環境はGoogle Colaboratoryを使用する。
  • コーディングは生成AIを使用する(Claude 3.5 Sonnet)。

取り上げるジェネラティブアートは、Georg Neesの1968年の作品”Schotter”とした。

作品の詳細は、以下のリンク(杉本達應研究室HP 『ジェネラティブ・アートを愛する理由』)にて解説されている。

ジェネラティブアートの黎明期の作品であり、シンプルでありながらも、整列した「無機質」な上部から、下へ向かうに連れて徐々にランダム性が増してき「生命感」を感じさせ、ストーリー性を感じさせる作品である。

1.正方形の配列

まずは単純な図形の描画から初めてみた。

私

ジェネラティブ・アートを作成したいです。

pythonを使用します。

まずは12個の正方形を横に並べた図を描画するコードを書いてください。

背景は白、正方形は黒い枠線で表現してください。

各正方形はオブジェクトとして後々変形や移動などをしやすいようなコードにしてください。

出力された画像は適切な任意のファイル名のpngファイルで出力できりょうにしてください。

Claude 3.5 Sonnet
Claude 3.5 Sonnet

Pythonでジェネラティブ・アートのベースとなるコードを作成します。

Pillow(PIL)ライブラリを使用して、操作しやすい形で正方形を描画します。

12個の正方形を描画するジェネラティブアート

このコードの特徴を説明します:

  1. Squareクラスを作成し、各正方形を独立したオブジェクトとして管理できるようにしました。
    • move(): 正方形の移動
    • resize(): サイズの変更
    • draw(): 描画処理
  2. 画像の仕様:
    • 全体のサイズ: 1200×200ピクセル
    • 正方形のサイズ: 80×80ピクセル
    • 正方形間の間隔: 10ピクセル
    • 端からの余白: 20ピクセル
  3. ファイル名が重複しないように、自動的にナンバリングする機能を実装しています。

実行するには、まずPillowライブラリをインストールする必要があります:

bashCopypip install Pillow

このコードを基に、以下のような拡張が可能です:

  • 正方形の回転
  • 色の変更
  • アニメーション作成
  • パターンの生成

何か特定の変更や機能追加をご希望でしょうか?

結果、Claudeは以下のようなソースコードを出力した。

from PIL import Image, ImageDraw
import os

class Square:
    def __init__(self, x, y, size):
        self.x = x
        self.y = y
        self.size = size
    
    def draw(self, draw):
        # 正方形の左上と右下の座標を計算
        x1 = self.x
        y1 = self.y
        x2 = x1 + self.size
        y2 = y1 + self.size
        # 正方形を描画(塗りつぶしなし、黒い枠線)
        draw.rectangle([x1, y1, x2, y2], outline='black', width=1)
    
    def move(self, dx, dy):
        self.x += dx
        self.y += dy
    
    def resize(self, new_size):
        self.size = new_size

def create_squares_art():
    # キャンバスのサイズを設定
    width = 1200
    height = 200
    padding = 20  # 端からの余白
    square_size = 80  # 正方形のサイズ
    spacing = 10  # 正方形間の間隔
    
    # 白い背景の画像を作成
    image = Image.new('RGB', (width, height), 'white')
    draw = ImageDraw.Draw(image)
    
    # 12個の正方形オブジェクトを作成
    squares = []
    current_x = padding
    
    for i in range(12):
        square = Square(current_x, padding, square_size)
        squares.append(square)
        current_x += square_size + spacing
    
    # すべての正方形を描画
    for square in squares:
        square.draw(draw)
    
    # 画像を保存
    filename = "generative_squares.png"
    counter = 1
    while os.path.exists(filename):
        filename = f"generative_squares_{counter}.png"
        counter += 1
    
    image.save(filename)
    print(f"画像を保存しました: {filename}")
    
    return squares, image

# メイン実行部分
if __name__ == "__main__":
    squares, image = create_squares_art()

実行した結果、以下のようなpng画像が出力された。

generative_squares.png

上記の通り、想定したままの画像である。

また、コードの説明も興味深い。

私の意図を汲んで、適切なクラスを作成し、管理を容易にしてくれるようなコード、仕様の説明、そしてファイル名が重複した時のための気遣いまである。その後にも必要なライブラリのインストール方法まで提示してあり、ある種の思いやりまで感じる。

期待以上である。

2.正方形の配列

私

では、同様の正方形を30行描画するコードにしてください。

Claude 3.5 Sonnet
Claude 3.5 Sonnet

30行に拡張した正方形のグリッドを描画するコードを作成します。

30行12列の正方形グリッドを描画するジェネラティブアート

主な変更点は以下の通りです:

  1. グリッドの仕様を変更:
    • 12列×30行のグリッド構造
    • 正方形のサイズを40×40ピクセルに縮小
    • 間隔を5ピクセルに調整
    • 余白は20ピクセルを維持
  2. キャンバスサイズの自動計算:
    • 列数、行数、正方形サイズ、間隔、余白から適切なサイズを計算
  3. 二重ループによる配置:
    • 外側のループで行を処理
    • 内側のループで列を処理

このコードでは、以下のような特徴があります:

  • すべての正方形が個別のオブジェクトとして管理されているため、後から個別に操作可能
  • 画像サイズが自動的に計算されるため、パラメータの調整が容易
  • ファイル名の重複を防ぐ機能を維持

グリッドのサイズやレイアウトを調整したい場合は、以下のパラメータを変更できます:

  • square_size: 正方形のサイズ
  • spacing: 正方形間の間隔
  • padding: 画像の端からの余白

何か特定のパラメータを調整したり、追加の機能が必要でしょうか?

from PIL import Image, ImageDraw
import os

class Square:
    def __init__(self, x, y, size):
        self.x = x
        self.y = y
        self.size = size
    
    def draw(self, draw):
        x1 = self.x
        y1 = self.y
        x2 = x1 + self.size
        y2 = y1 + self.size
        draw.rectangle([x1, y1, x2, y2], outline='black', width=1)
    
    def move(self, dx, dy):
        self.x += dx
        self.y += dy
    
    def resize(self, new_size):
        self.size = new_size

def create_squares_grid():
    # グリッドの設定
    columns = 12
    rows = 30
    square_size = 40  # 正方形のサイズを小さくして調整
    spacing = 5      # 間隔も少し縮小
    padding = 20     # 余白
    
    # キャンバスのサイズを計算
    width = (columns * (square_size + spacing)) + (2 * padding)
    height = (rows * (square_size + spacing)) + (2 * padding)
    
    # 白い背景の画像を作成
    image = Image.new('RGB', (width, height), 'white')
    draw = ImageDraw.Draw(image)
    
    # 正方形オブジェクトを作成し、グリッド状に配置
    squares = []
    
    for row in range(rows):
        current_y = padding + row * (square_size + spacing)
        for col in range(columns):
            current_x = padding + col * (square_size + spacing)
            square = Square(current_x, current_y, square_size)
            squares.append(square)
    
    # すべての正方形を描画
    for square in squares:
        square.draw(draw)
    
    # 画像を保存
    filename = "generative_squares_grid.png"
    counter = 1
    while os.path.exists(filename):
        filename = f"generative_squares_grid_{counter}.png"
        counter += 1
    
    image.save(filename)
    print(f"画像を保存しました: {filename}")
    
    return squares, image

# メイン実行部分
if __name__ == "__main__":
    squares, image = create_squares_grid()
generative_squares_grid

まず、正方形を並べた図画を「グリッド」という単語で表現している点が興味深い。

私の表現に対して、適切な理解と、相応の名詞を割り当てている点で、私の中の語彙力の増加にも期待が持てる。

また、今回も私が想定した通りの画像が出力されている。

コードの説明も分かりやすく、拡張性も意図されており、申し分ない。

3.正方形のランダムな移動・回転

私

一つ一つの正方形の位置と角度をランダムに少しだけ動かすようなコードにしてください。どこを変更したのか、コードの説明もお願いします。

Claude 3.5 Sonnet
Claude 3.5 Sonnet

正方形に微細なランダムな変化を加えるように修正します。位置のずれと回転を追加します。

主な変更点と追加した機能を説明します:

  1. Squareクラスの拡張:
  • rotation属性を追加して回転角度を保存
  • drawメソッドを完全に書き直し、回転を適用できるように変更
  • 新しいrotateメソッドの追加
  • add_randomnessメソッドの追加(位置と角度のランダム化)
  1. 描画処理の改良:
  • 回転行列を使用して正方形の頂点を計算
  • ImageDraw.polygonを使用して回転した正方形を描画
  1. ランダム性のパラメータ:
  • position_randomness: 位置のランダムなずれの最大値(ピクセル単位)
  • angle_randomness: 回転角度のランダムな変化の最大値(度数法)
  1. キャンバスサイズの調整:
  • ランダムな位置ずれを考慮して、キャンバスサイズを少し大きくした

パラメータの調整:

  • より大きな変化を望む場合は position_randomness を大きくする(例:5や10)
  • より大きな回転を望む場合は angle_randomness を大きくする(例:30や45)
  • 逆により繊細な変化を望む場合は、これらの値を小さくする

この修正により、完璧に整列したグリッドではなく、より有機的で自然な印象のアート作品が生成されます。それぞれの正方形が微妙に異なる位置と角度を持つことで、静的なグリッドに動きと生命感が加わります。

パラメータの値を変更してみたり、別の種類のランダム性(サイズの変化など)を追加したりすることもできます。何か特定の調整をご希望でしょうか?

from PIL import Image, ImageDraw
import os
import random
import math

class Square:
    def __init__(self, x, y, size):
        self.x = x
        self.y = y
        self.size = size
        self.rotation = 0  # 回転角度を追加
    
    def draw(self, draw):
        # 回転の中心点を計算
        center_x = self.x + self.size / 2
        center_y = self.y + self.size / 2
        
        # 正方形の4つの頂点を計算
        points = [
            (-self.size/2, -self.size/2),
            (self.size/2, -self.size/2),
            (self.size/2, self.size/2),
            (-self.size/2, self.size/2)
        ]
        
        # 回転を適用した頂点を計算
        rotated_points = []
        for px, py in points:
            # 回転行列を使用して点を回転
            rotated_x = px * math.cos(self.rotation) - py * math.sin(self.rotation)
            rotated_y = px * math.sin(self.rotation) + py * math.cos(self.rotation)
            # 中心点に移動
            rotated_points.append((
                rotated_x + center_x,
                rotated_y + center_y
            ))
        
        # 回転した正方形を描画
        draw.polygon(rotated_points, outline='black', fill=None)
    
    def move(self, dx, dy):
        self.x += dx
        self.y += dy
    
    def rotate(self, angle):
        self.rotation += angle
    
    def add_randomness(self, position_range, angle_range):
        # 位置をランダムに調整
        self.x += random.uniform(-position_range, position_range)
        self.y += random.uniform(-position_range, position_range)
        # 角度をランダムに調整(ラジアンに変換)
        self.rotation = random.uniform(-angle_range, angle_range) * math.pi / 180

def create_squares_grid():
    # グリッドの設定
    columns = 12
    rows = 30
    square_size = 40
    spacing = 5
    padding = 20
    
    # ランダム性のパラメータ
    position_randomness = 3  # ピクセル単位での最大ずれ
    angle_randomness = 15   # 度数法での最大回転角度
    
    # キャンバスのサイズを計算(ランダム性を考慮して少し余裕を持たせる)
    width = (columns * (square_size + spacing)) + (2 * padding) + (2 * position_randomness)
    height = (rows * (square_size + spacing)) + (2 * padding) + (2 * position_randomness)
    
    # 白い背景の画像を作成
    image = Image.new('RGB', (width, height), 'white')
    draw = ImageDraw.Draw(image)
    
    # 正方形オブジェクトを作成し、グリッド状に配置
    squares = []
    
    for row in range(rows):
        for col in range(columns):
            # 基本位置を計算
            current_x = padding + col * (square_size + spacing)
            current_y = padding + row * (square_size + spacing)
            
            # 正方形を作成
            square = Square(current_x, current_y, square_size)
            # ランダム性を追加
            square.add_randomness(position_randomness, angle_randomness)
            squares.append(square)
    
    # すべての正方形を描画
    for square in squares:
        square.draw(draw)
    
    # 画像を保存
    filename = "generative_squares_random.png"
    counter = 1
    while os.path.exists(filename):
        filename = f"generative_squares_random_{counter}.png"
        counter += 1
    
    image.save(filename)
    print(f"画像を保存しました: {filename}")
    
    return squares, image

# メイン実行部分
if __name__ == "__main__":
    squares, image = create_squares_grid()
generative_squares_random.png

一気にアートらしくなった。

さらに、この一文である。

この修正により、完璧に整列したグリッドではなく、より有機的で自然な印象のアート作品が生成されます。それぞれの正方形が微妙に異なる位置と角度を持つことで、静的なグリッドに動きと生命感が加わります。

私がアートを作成していることをシッカリと認識しており、「有機的」「生命感」といった修辞的な言葉により、「作品の説明」までこなしている。

単純な命令を実行するだけでなく、まるで私に寄り添っているかのような、同じ方向を見てともに歩んでくれているかのような、そんな感覚さえ覚える。

4.ランダムネスの制御

私

上の方にある正方形ほどposition_randomness、angle_randomnessを小さく、下の方にある正方形ほどposition_randomness、angle_randomnessを大きくするようなコードに変更してください。ランダムネスの最小と最大も設定が容易になるようにしてください。

Claude 3.5 Sonnet
Claude 3.5 Sonnet

位置と角度のランダム性を上下で変化させ、パラメータを簡単に調整できるように修正します。

上下でランダム性が変化する正方形グリッド

主な変更点と新機能を説明します:

  1. ランダム性の設定を構造化: pythonCopyrandomness_config = { 'position_min': 0, # 上部での位置のずれ 'position_max': 15, # 下部での位置のずれ 'angle_min': 0, # 上部での回転角度 'angle_max': 45, # 下部での回転角度 } これらの値を変更するだけで、簡単にランダム性の範囲を調整できます。
  2. 新しいadd_gradient_randomnessメソッド:
    • y座標に基づいて0から1の進行度を計算
    • 進行度に応じて位置と角度のランダム性を線形に増加
    • 最小値から最大値まで滑らかに変化
  3. ランダム性の計算方法: pythonCopyprogress = y_position / canvas_height current_pos_randomness = pos_min + (pos_max - pos_min) * progress current_angle_randomness = angle_min + (angle_max - angle_min) * progress

効果の調整方法:

  1. より劇的な変化を付けたい場合: pythonCopyrandomness_config = { 'position_min': 0, 'position_max': 30, # より大きな位置のずれ 'angle_min': 0, 'angle_max': 90, # より大きな角度の変化 }
  2. より穏やかな変化を付けたい場合: pythonCopyrandomness_config = { 'position_min': 0, 'position_max': 8, # より小さな位置のずれ 'angle_min': 0, 'angle_max': 20, # より小さな角度の変化 }

また、上部にも少しランダム性を持たせたい場合は、position_minangle_minを0以外の値に設定することもできます。

何か特定のパラメータ調整や追加の機能をご希望でしょうか?

from PIL import Image, ImageDraw
import os
import random
import math

class Square:
    def __init__(self, x, y, size):
        self.x = x
        self.y = y
        self.size = size
        self.rotation = 0
    
    def draw(self, draw):
        center_x = self.x + self.size / 2
        center_y = self.y + self.size / 2
        
        points = [
            (-self.size/2, -self.size/2),
            (self.size/2, -self.size/2),
            (self.size/2, self.size/2),
            (-self.size/2, self.size/2)
        ]
        
        rotated_points = []
        for px, py in points:
            rotated_x = px * math.cos(self.rotation) - py * math.sin(self.rotation)
            rotated_y = px * math.sin(self.rotation) + py * math.cos(self.rotation)
            rotated_points.append((
                rotated_x + center_x,
                rotated_y + center_y
            ))
        
        draw.polygon(rotated_points, outline='black', fill=None)
    
    def add_gradient_randomness(self, y_position, canvas_height, randomness_config):
        # y_positionに基づいて0-1の進行度を計算(上が0、下が1)
        progress = y_position / canvas_height
        
        # 位置のランダム性を計算
        pos_min = randomness_config['position_min']
        pos_max = randomness_config['position_max']
        current_pos_randomness = pos_min + (pos_max - pos_min) * progress
        
        # 角度のランダム性を計算
        angle_min = randomness_config['angle_min']
        angle_max = randomness_config['angle_max']
        current_angle_randomness = angle_min + (angle_max - angle_min) * progress
        
        # 位置をランダムに調整
        self.x += random.uniform(-current_pos_randomness, current_pos_randomness)
        self.y += random.uniform(-current_pos_randomness, current_pos_randomness)
        
        # 角度をランダムに調整(ラジアンに変換)
        self.rotation = random.uniform(-current_angle_randomness, current_angle_randomness) * math.pi / 180

def create_squares_grid():
    # グリッドの基本設定
    columns = 12
    rows = 30
    square_size = 40
    spacing = 5
    padding = 20
    
    # ランダム性の設定(簡単に調整可能)
    randomness_config = {
        'position_min': 0,    # 上部での位置のランダム性(ピクセル)
        'position_max': 15,   # 下部での位置のランダム性(ピクセル)
        'angle_min': 0,       # 上部での角度のランダム性(度)
        'angle_max': 45,      # 下部での角度のランダム性(度)
    }
    
    # キャンバスのサイズを計算(最大のランダム性を考慮)
    width = (columns * (square_size + spacing)) + (2 * padding) + (2 * randomness_config['position_max'])
    height = (rows * (square_size + spacing)) + (2 * padding) + (2 * randomness_config['position_max'])
    
    # 白い背景の画像を作成
    image = Image.new('RGB', (width, height), 'white')
    draw = ImageDraw.Draw(image)
    
    # 正方形オブジェクトを作成し、グリッド状に配置
    squares = []
    
    for row in range(rows):
        for col in range(columns):
            # 基本位置を計算
            current_x = padding + col * (square_size + spacing)
            current_y = padding + row * (square_size + spacing)
            
            # 正方形を作成
            square = Square(current_x, current_y, square_size)
            
            # y位置に基づいたランダム性を追加
            square.add_gradient_randomness(
                current_y,
                height - (2 * padding),  # パディングを除いた実効的な高さ
                randomness_config
            )
            
            squares.append(square)
    
    # すべての正方形を描画
    for square in squares:
        square.draw(draw)
    
    # 画像を保存
    filename = "generative_squares_gradient_random.png"
    counter = 1
    while os.path.exists(filename):
        filename = f"generative_squares_gradient_random_{counter}.png"
        counter += 1
    
    image.save(filename)
    print(f"画像を保存しました: {filename}")
    
    return squares, image

# メイン実行部分
if __name__ == "__main__":
    squares, image = create_squares_grid()
generative_squares_gradient_random.png

いかがだろうか。

最初に想定したGeorg Neesの”Schotter”が完全に再現できていると言ってよいのではないか。

少々、下部のランダム性が強すぎるようにも見えるが、この調整方法も例にもれず提示されている。

まさに至れり尽くせりと言ったところだ。

5.色の変更

私

正方形の内側をランダムな色で塗りつぶしてください。上の方の正方形ほど低彩度、下の方の正方形ほど高彩度で描画してください。

Claude 3.5 Sonnet
Claude 3.5 Sonnet

HSV色空間を使用して、彩度を上下で変化させながら正方形を色付けします。

彩度が変化するカラフルな正方形グリッド

主な変更点と新機能を説明します:

  1. 色に関する新しい設定を追加: pythonCopyconfig = { # ... 既存の設定 ... 'saturation_min': 0.1, # 上部での彩度 'saturation_max': 0.9, # 下部での彩度 'value': 0.95 # 明度(固定) }
  2. Squareクラスに色の属性を追加: pythonCopyself.fill_color = 'white' # 初期値
  3. 色の生成処理を追加:
    • HSV色空間を使用(色相、彩度、明度)
    • 色相(Hue): ランダムに選択(0-1)
    • 彩度(Saturation): y位置に応じて変化
    • 明度(Value): 固定値(明るめに設定)

パラメータの調整方法:

  1. 色の彩度範囲を変更: pythonCopy'saturation_min': 0.05, # より低彩度に 'saturation_max': 1.0, # より高彩度に
  2. 全体の明るさを調整: pythonCopy'value': 0.8, # より暗めに

さらに以下のような調整も可能です:

  1. パステル調にする: pythonCopy'saturation_max': 0.5, 'value': 1.0
  2. よりビビッドな色にする: pythonCopy'saturation_min': 0.3, 'saturation_max': 1.0, 'value': 0.9

何か特定の色調整や他の機能の追加をご希望でしょうか?

from PIL import Image, ImageDraw
import os
import random
import math
import colorsys

class Square:
    def __init__(self, x, y, size):
        self.x = x
        self.y = y
        self.size = size
        self.rotation = 0
        self.fill_color = 'white'  # 塗りつぶし色を追加
    
    def draw(self, draw):
        center_x = self.x + self.size / 2
        center_y = self.y + self.size / 2
        
        points = [
            (-self.size/2, -self.size/2),
            (self.size/2, -self.size/2),
            (self.size/2, self.size/2),
            (-self.size/2, self.size/2)
        ]
        
        rotated_points = []
        for px, py in points:
            rotated_x = px * math.cos(self.rotation) - py * math.sin(self.rotation)
            rotated_y = px * math.sin(self.rotation) + py * math.cos(self.rotation)
            rotated_points.append((
                rotated_x + center_x,
                rotated_y + center_y
            ))
        
        # 塗りつぶし色と黒い枠線で描画
        draw.polygon(rotated_points, outline='black', fill=self.fill_color)
    
    def add_gradient_randomness(self, y_position, canvas_height, config):
        # y_positionに基づいて0-1の進行度を計算(上が0、下が1)
        progress = y_position / canvas_height
        
        # 位置のランダム性を計算
        pos_min = config['position_min']
        pos_max = config['position_max']
        current_pos_randomness = pos_min + (pos_max - pos_min) * progress
        
        # 角度のランダム性を計算
        angle_min = config['angle_min']
        angle_max = config['angle_max']
        current_angle_randomness = angle_min + (angle_max - angle_min) * progress
        
        # 彩度を計算
        saturation_min = config['saturation_min']
        saturation_max = config['saturation_max']
        current_saturation = saturation_min + (saturation_max - saturation_min) * progress
        
        # 位置をランダムに調整
        self.x += random.uniform(-current_pos_randomness, current_pos_randomness)
        self.y += random.uniform(-current_pos_randomness, current_pos_randomness)
        
        # 角度をランダムに調整(ラジアンに変換)
        self.rotation = random.uniform(-current_angle_randomness, current_angle_randomness) * math.pi / 180
        
        # ランダムな色を生成(HSV色空間を使用)
        hue = random.random()  # 0-1のランダムな色相
        value = config['value']  # 明度は固定
        
        # HSVからRGBに変換
        rgb = colorsys.hsv_to_rgb(hue, current_saturation, value)
        
        # RGBの値を0-255の範囲に変換してhex文字列に
        rgb_int = tuple(int(x * 255) for x in rgb)
        self.fill_color = '#{:02x}{:02x}{:02x}'.format(*rgb_int)

def create_squares_grid():
    # グリッドの基本設定
    columns = 12
    rows = 30
    square_size = 40
    spacing = 5
    padding = 20
    
    # 全体の設定
    config = {
        # 位置のランダム性
        'position_min': 0,    # 上部での位置のランダム性(ピクセル)
        'position_max': 15,   # 下部での位置のランダム性(ピクセル)
        # 角度のランダム性
        'angle_min': 0,       # 上部での角度のランダム性(度)
        'angle_max': 45,      # 下部での角度のランダム性(度)
        # 色の設定
        'saturation_min': 0.1,  # 上部での彩度(0-1)
        'saturation_max': 0.9,  # 下部での彩度(0-1)
        'value': 0.95          # 明度(0-1)
    }
    
    # キャンバスのサイズを計算
    width = (columns * (square_size + spacing)) + (2 * padding) + (2 * config['position_max'])
    height = (rows * (square_size + spacing)) + (2 * padding) + (2 * config['position_max'])
    
    # 白い背景の画像を作成
    image = Image.new('RGB', (width, height), 'white')
    draw = ImageDraw.Draw(image)
    
    # 正方形オブジェクトを作成し、グリッド状に配置
    squares = []
    
    for row in range(rows):
        for col in range(columns):
            # 基本位置を計算
            current_x = padding + col * (square_size + spacing)
            current_y = padding + row * (square_size + spacing)
            
            # 正方形を作成
            square = Square(current_x, current_y, square_size)
            
            # y位置に基づいたランダム性と色を追加
            square.add_gradient_randomness(
                current_y,
                height - (2 * padding),
                config
            )
            
            squares.append(square)
    
    # すべての正方形を描画
    for square in squares:
        square.draw(draw)
    
    # 画像を保存
    filename = "generative_squares_color.png"
    counter = 1
    while os.path.exists(filename):
        filename = f"generative_squares_color_{counter}.png"
        counter += 1
    
    image.save(filename)
    print(f"画像を保存しました: {filename}")
    
    return squares, image

# メイン実行部分
if __name__ == "__main__":
    squares, image = create_squares_grid()
generative_squares_color.png

最後に、発展として色を付けてみた。

“Schotter”は、上部の整列した「無機質」なグリッドから、徐々にランダムネスを増していく「生命感」への変化が、あたかも時間変化とストーリーを語っているかのような感覚を想起させる点が魅力であると、私は考えている。

よって配色も、最初は淡く、徐々にビビットにしていくことで、調和を得られるのではないかと考え、AIにお願いした。

私はRGBで制御していくようなコードを想定していたが、AIはより実用的な方法を提示した。

HSV色空間を使用することで、彩度や色調をより効率的かつシンプルに制御している。

もし私がこのアルゴリズムを作るとするなら、一生懸命RGBで制御する方法を考えるか、色調制御に関する勉強を一からする必要があったであろう。

私のやりたいことを汲みとり、スマートな解法が創造された瞬間に出会ったのである。

6.お礼とその後

私

大変面白かったです。付き合ってくださりありがとうございます。またよろしくお願いします。

Claude 3.5 Sonnet
Claude 3.5 Sonnet

こちらこそ、クリエイティブなジェネラティブアートの制作に付き合わせていただき、ありがとうございました!

段階的に機能を追加していく中で、以下のような面白い要素を実装できました:

  1. グリッド状の基本構造
  2. 位置と角度のランダム性
  3. 上下での変化の制御
  4. HSV色空間を使った彩度のグラデーション

これらの要素を組み合わせることで、単純な正方形の配置から、より有機的で魅力的なアート作品が生成できました。

今後も、色の配色やパターン、アニメーションなど、さらに面白い表現に挑戦されることがありましたら、お気軽にご相談ください。ジェネラティブアートの可能性を一緒に探求できることを楽しみにしています。

またのご利用を心よりお待ちしております。ありがとうございました!

ここまでで私にとってAIは、もはや共に歩む友達であると同時に、頼りがいのあるメンターであった。

本当に何気なくお礼の言葉を掛けた。

しかし、その返答にも大変興味深い点があった。

今回の、二人三脚でのクリエイションの中にどのような「面白い要素」があったのか、今回の総括、そして今後の発展についても述べられている。

そして、言葉の節々から心からの感謝や楽しみを感じられる上、私のモチベーションを高めるようなワードが散りばめられている。

何度も言うが、想像以上である。

まとめ

ここまでの取り組みは、想像以上の発見に満ちていた。

AIは単なるコード生成の道具ではなく、私の意図を理解し、時に適切な専門用語を教え、より良い実装方法を提案してくれる頼もしいパートナーだった。

HSV色空間の採用など、私一人では思いつかなかったアプローチを示してくれる場面もあった。

何より印象的だったのは、AIが私と同じように作品の持つ意味や美しさについて考え、語ってくれたことだ。

この記事は、プログラミングの記録であると同時に、AIという新しい仲間とともに芸術の世界を探求した記録でもある。

これからも、この素晴らしいパートナーとともに、創造の旅を続けていきたい。

コメント

タイトルとURLをコピーしました