ابنِ مولّد صور بالذكاء الاصطناعي بلغة Python (خطوة بخطوة)

اكتب سكربت Python يرسل أمرًا نصيًا إلى نموذج صور، وينتظر اكتمال المهمة، ثم يحفظ الصورة. ابنِه في أربع خطوات، ثم أضف واجهة سطر أوامر، ودفعات متعددة الخيوط، ومسارًا عبر Flask، وحلولًا للأخطاء التي ستصادفها.

ابنِ مولّد صور بالذكاء الاصطناعي بلغة Python (خطوة بخطوة)
Cristian Da Conceicao
مؤسس Picasso IA

معظم الشروحات عن الصور بالذكاء الاصطناعي تتوقف عند "الصق أمرًا نصيًا في موقع". هذا يعمل مع صورة واحدة، لكنه ينهار حين تحتاج إلى خمسين صورة لمنتجات، أو دفعة ليلية من صور مصغّرة للمدونة، أو تطبيق صغير يكتب فيه الزوار جملة ويحصلون على صورة فوتوغرافية. لذلك تحتاج إلى كود، والخبر الجيد أن مولّد صور يعمل بلغة Python يتسع تقريبًا في 60 سطرًا.

يبني هذا الشرح المولّد في أربع خطوات صغيرة: إرسال أمر نصي إلى نموذج مستضاف، ثم انتظار اكتمال المهمة، ثم تنزيل الملف، وأخيرًا تغليف كل شيء في أداة سطر أوامر تشغّل الدفعات. تنتهي كل خطوة بفحص سريع، فتعرف أنها تعمل قبل الانتقال إلى التالية.

💡 الإجابة السريعة: مولّد الصور بالذكاء الاصطناعي في Python هو سكربت يرسل أمرًا نصيًا إلى نموذج عبر HTTP، ثم يستطلع الحالة حتى تصبح الصورة جاهزة، ثم يحفظ الملف. يعمل النموذج على وحدة GPU تابعة لشخص آخر. يحتاج الكود إلى requests، ورمز سري، وحلقة تكرار.

قبل أن تكتب أي سطر

يدا مطوّر تكتبان كود Python على حاسوب محمول فوق مكتب خشبي

تحتاج إلى ثلاثة أشياء: Python 3.10 أو أحدث، ونافذة طرفية، ورمز سري لخدمة الصور. يتصل الكود أدناه عبر PicassoIA API عبر HTTP عادي، لذلك لا يلزمك تثبيت أي تعريف لوحة رسومات ولا تنزيل ملف نموذج.

إعداد المشروع

أنشئ مجلدًا وبيئة افتراضية، ثم ثبّت حزمة واحدة:

mkdir ai-image-generator && cd ai-image-generator
python -m venv .venv
source .venv/bin/activate   # Windows: .venv\Scripts\activate
pip install requests

بعد ذلك، أنشئ رمزًا من صفحة API واحفظه في متغير بيئة بدلًا من ملف المصدر. يمكن للحساب أن يحمل رمزين كحد أقصى في الوقت نفسه.

export PICASSOIA_API_TOKEN="pia_sk_your_token_here"

في PowerShell على Windows يكون السطر نفسه $env:PICASSOIA_API_TOKEN = "pia_sk_your_token_here".

⚠️ لا تلصق الرمز داخل السكربت أبدًا. أي شيء يُرفع إلى git يبقى في السجل حتى بعد حذفه. متغير البيئة يُبقي السر خارج المستودع.

اختر النموذج أولًا

يحدد النموذج شكل صورك وسرعة وصولها. يستدعي السكربت في هذا الشرح picassoia/picassoia-image عبر API. النماذج في الجدول أدناه متاحة كلها في المتصفح على PicassoIA، ما يجعلها مفيدة لاختبار الأوامر النصية قبل ربط أي شيء بالكود.

النموذجأفضل استخدام
P Imageمسودات واقعية سريعة، نحو ثانية لكل صورة
Flux 2 Proصور مفصّلة من النص أو من صور مرجعية
Flux Devمشاهد واقعية وصور شخصية
Stable Diffusion 3.5 Largeصور عالية الوضوح بتفاصيل قوية
Imagen 4 Fastنتائج سريعة خلال ثوانٍ قليلة
Seedream 4.5مخرجات بدقة عالية تصل إلى 4K
SDXL Lightning 4Stepمعاينات سريعة جدًا

كيف يتحول النص إلى صورة

يبدأ نموذج الانتشار من ضوضاء عشوائية ويزيلها على مراحل صغيرة كثيرة، ويوجّهه أمرك النصي في كل مرحلة. كل مرحلة حساب ثقيل على شبكة عصبية كبيرة. لهذا يحتاج توليد الصور إلى وحدة GPU قوية، ولهذا يكون أول قرار تصميمي في مشروع Python هو مكان وجود وحدة GPU هذه.

صفوف من خزائن خوادم سوداء في ممر هادئ داخل مركز بيانات

وحدة GPU محلية أو API مستضافة. تشغيل النموذج على جهازك يمنحك تحكمًا كاملًا، لكنه يعني أيضًا مجموعة من التعريفات، وعشرات الغيغابايتات من الأوزان، وبطاقة ذات ذاكرة وفيرة. واجهة API المستضافة تقايض جزءًا من ذلك التحكم بإعداد يُقاس بالدقائق.

لقطة مقرّبة لبطاقة رسوميات مكتبية مع أنابيب نحاسية للتبريد داخل هيكل حاسوب مفتوح

العاملوحدة GPU محليةAPI مستضاف
وقت الإعدادساعاتدقائق
العتادبطاقة رسوميات حديثة بذاكرة 12 غيغابايت أو أكثرأي حاسوب محمول
اختيار النموذجما تنزّلهما تعرضه الخدمة
التوسعشراء بطاقة أخرىرفع عدد الطلبات المتزامنة
اعتماديات Pythonثقيلةrequests فقط

لماذا تكون المهام غير متزامنة. يستغرق توليد الصورة من أقل من ثانية إلى نحو دقيقة، بحسب النموذج. إبقاء اتصال HTTP مفتوحًا كل هذه المدة هش، لذلك تعمل واجهة API في ثلاث حركات: إنشاء مهمة (prediction)، ثم استطلاع حالته، ثم جلب المخرجات حين تصبح الحالة succeeded. يكرر كودك هذه الحركات الثلاث في القسم التالي.

خطوة بخطوة: السكربت الأول

ضع كل شيء في ملف واحد اسمه generate.py. تبني الخطوات الأربع أدناه الملف من أعلاه إلى أسفله.

الخطوة 1: إعداد الطلب

واجهة API من نوع Replicate: عنوان أساسي، واسم النموذج في المسار، ورمز Bearer في الترويسة.

import os
import time
from pathlib import Path

import requests

API_BASE = "https://api.picassoia.com/v1"
MODEL = "picassoia/picassoia-image"
TOKEN = os.environ["PICASSOIA_API_TOKEN"]
HEADERS = {
    "Authorization": f"Bearer {TOKEN}",
    "Content-Type": "application/json",
}

إذا كان المتغير مفقودًا، يتوقف Python بخطأ هنا مباشرة، وهذا ما تريده تمامًا. الفشل الواضح في أعلى الملف أفضل من خطأ 401 مربك لاحقًا.

الخطوة 2: إرسال الأمر النصي

def create_prediction(prompt: str) -> str:
    url = f"{API_BASE}/models/{MODEL}/predictions"
    payload = {"input": {"prompt": prompt}}
    response = requests.post(url, headers=HEADERS, json=payload, timeout=30)
    response.raise_for_status()
    return response.json()["id"]

لا تُرجع الدالة صورة، بل معرّف مهمة (job ID). يمكن أن تصل الأوامر النصية إلى 4,000 حرف، وهذا أكثر بكثير مما ستحتاجه.

الخطوة 3: الاستطلاع حتى الانتهاء

def wait_for_result(job_id: str, poll_every: float = 2.0, limit: float = 300.0) -> str:
    url = f"{API_BASE}/predictions/{job_id}"
    deadline = time.time() + limit
    while time.time() < deadline:
        response = requests.get(url, headers=HEADERS, timeout=30)
        response.raise_for_status()
        data = response.json()
        status = data["status"]
        if status == "succeeded":
            output = data["output"]
            return output[0] if isinstance(output, list) else output
        if status in ("failed", "canceled"):
            raise RuntimeError(f"Job {job_id} ended as {status}: {data.get('error')}")
        time.sleep(poll_every)
    raise TimeoutError(f"Job {job_id} did not finish within {limit} seconds")

💡 نصيحة: تتبع أسماء الحقول صيغة Replicate. إذا بدت استجابة ما مختلفة يومًا، اطبع response.json() مرة واحدة، ثم عدّل السطرين اللذين يقرآن status وoutput. كما تحدد الحلقة موعدًا نهائيًا خاصًا بها، فلن يتجمد سكربتك بسبب مهمة عالقة.

الخطوة 4: حفظ الملف

def download(image_url: str, folder: str = "output") -> Path:
    Path(folder).mkdir(exist_ok=True)
    name = image_url.split("/")[-1].split("?")[0] or f"image-{int(time.time())}.png"
    target = Path(folder) / name
    response = requests.get(image_url, timeout=60)
    response.raise_for_status()
    target.write_bytes(response.content)
    return target


if __name__ == "__main__":
    prompt = "A misty mountain lake at sunrise, 35mm photograph, soft natural light, fine film grain"
    job_id = create_prediction(prompt)
    image_url = wait_for_result(job_id)
    print("Saved", download(image_url))

لقطة من فوق الكتف لنافذة طرفية بجانب صورة شاطئ مضاءة بالساعة الذهبية

شغّل python generate.py. بعد بضع ثوانٍ يجب أن ترى Saved output/... وملفًا جديدًا في مجلد output. نقطة تحقق: إذا استطعت فتح تلك الصورة، فالخطوات من 1 إلى 4 تعمل جميعها، وكل ما بعد هذه النقطة هو تغليف فقط.

حوّل السكربت إلى أداة

أضف واجهة سطر أوامر

استبدل نهاية الملف بنقطة دخول argparse كي تمرر الأوامر النصية من الطرفية:

import argparse


def main() -> None:
    parser = argparse.ArgumentParser(description="Generate an image from a text prompt")
    parser.add_argument("prompt", help="what the image should show")
    parser.add_argument("--out", default="output", help="folder for saved files")
    args = parser.parse_args()

    image_url = wait_for_result(create_prediction(args.prompt))
    print(download(image_url, args.out))


if __name__ == "__main__":
    main()

الآن ينجز python generate.py "a red bicycle leaning on a brick wall, 50mm photo" --out bikes المهمة كاملة في سطر واحد.

شغّل الدفعات ضمن الحدود

حلقة تنتظر كل صورة بالترتيب بطيئة. الخيوط (threads) تحل ذلك، لأن السكربت يقضي معظم وقته في انتظار الشبكة. تسمح واجهة API بحد أقصى 5 مهام متزامنة لكل حساب، مشتركة بين رموزك وأي تطبيقات متصلة، لذلك يكون خمسة عمّال هو الحد الأقصى.

from multiprocessing.pool import ThreadPool


def generate(prompt: str) -> Path:
    return download(wait_for_result(create_prediction(prompt)))


def run_batch(prompts: list[str]) -> list[Path]:
    with ThreadPool(5) as pool:
        return pool.map(generate, prompts)

مصوّر يفحص ورقة اتصال مطبوعة تضم إطارات صغيرة لمناظر طبيعية وصور شخصية

إذا كنت تستخدم الموقع أيضًا أثناء تشغيل دفعة، فاخفض عدد العمّال إلى ثلاثة حتى لا يتنازع الاثنان على الفتحات الخمس نفسها. يُطلق أي أمر نصي فاشل خطأً داخل pool.map، لذلك لفّ generate في كتلة try تسجّل الأمر النصي وتُرجع None حين تشغّل مئات الصور دفعة واحدة.

احتفظ بسجل. أضف كل أمر نصي واسم ملف إلى ملف log.jsonl، كي تتمكن من إرجاع أي صورة إلى الصياغة التي أنتجتها. استدعِ هذه الدالة المساعدة داخل generate مباشرة بعد download:

import json


def log_result(prompt: str, path: Path) -> None:
    with open("log.jsonl", "a", encoding="utf-8") as log:
        log.write(json.dumps({"prompt": prompt, "file": str(path)}) + "\n")

بعد ستة أسابيع، حين يسأل أحدهم أي أمر نصي أنتج صورة شروق الشمس، سيجيب ذلك الملف خلال ثوانٍ.

اعرضه كمسار ويب

نادرًا ما يريد الفريق الطرفية. يتيح تطبيق Flask صغير لأي شخص إرسال أمر نصي من صفحة. لاحظ أن المسار يُرجع معرّف المهمة فورًا، وأن مسارًا ثانيًا يُبلغ عن التقدم، فلا يتعطل أي طلب ويب لدقيقة كاملة.

from flask import Flask, jsonify, request

app = Flask(__name__)


@app.post("/generate")
def start():
    prompt = request.get_json()["prompt"]
    return jsonify(id=create_prediction(prompt))


@app.get("/status/<job_id>")
def status(job_id):
    url = f"{API_BASE}/predictions/{job_id}"
    data = requests.get(url, headers=HEADERS, timeout=30).json()
    return jsonify(status=data["status"], output=data.get("output"))

ثلاثة زملاء متحلقون حول شاشة واحدة تعرض شبكة من الصور المصغّرة الفوتوغرافية

تستدعي واجهتك /generate، ثم تسأل /status/<id> كل ثانيتين حتى تصبح status succeeded. يبقى الرمز على الخادم، ولا يظهر أبدًا في كود المتصفح.

اكتب أوامر نصية تعمل بانتظام

منظر علوي لمكتب فيه حاسوب محمول ورسومات في دفتر وصور فوتوغرافية مطبوعة

ابنِ الأوامر من أجزاء

الأوامر الغامضة تعطي صورًا غامضة. الأمر الذي يحتوي على موضوع ومكان ومصدر ضوء وعدسة يعطيك نتيجة قابلة للتكرار. ضع هذه البنية في دالة كي تتبعها كل صورة في الدفعة:

def build_prompt(subject: str, setting: str, light: str, lens: str = "50mm f/1.8") -> str:
    return (
        f"{subject}, {setting}, {light}, shot on a {lens} lens, "
        "natural skin and surface texture, fine film grain, photorealistic"
    )


print(build_prompt("a ceramic mug of black coffee", "on an oak desk", "soft window light from the left"))

ثلاث عادات تجعل النتائج أكثر قابلية للتوقع:

  • مشهد واحد لكل أمر نصي. شخصان يتنازعان الانتباه ينتجان صورًا مشوشة.
  • سمِّ الضوء. عبارة "ضوء صباحي حجمي من اليسار" أفضل من "إضاءة جميلة".
  • سمِّ العدسة. كلمات مثل 85mm وf/1.8 تدفع النموذج نحو عمق ميداني ضحل.

استلهم الأفكار من نموذج لغوي

كتابة 50 أمرًا نصيًا يدويًا تصبح مملة عند الأمر الثاني عشر. يستطيع نموذج لغوي أن يكتبها لك. يتعامل Claude Sonnet 5 مع القوائم المنظّمة بكفاءة، ويُرجع Gemini 3.5 Flash مسودات سريعة حين تحتاج إلى الكمية فقط. اطلب 20 أمرًا نصيًا تتبع بنية الموضوع والمكان والضوء والعدسة، سطرًا لكل أمر، واحفظ الرد كملف prompts.txt. ثم مرر الملف إلى مشغّل الدفعات:

prompts = [line.strip() for line in Path("prompts.txt").read_text().splitlines() if line.strip()]
run_batch(prompts)

كيف تستخدم P Image على PicassoIA

قبل أن تقضي وقتًا في الكود، اختبر صياغتك في المتصفح. يُجيب P Image خلال ثانية تقريبًا، فيمكنك تجربة عشرة تنويعات في الوقت الذي يستغرقه نموذج أبطأ لعرض نتيجة واحدة.

  1. افتح صفحة النموذج وابحث عن مربع الأمر النصي.
  2. الصق أمرًا نصيًا مبنيًا من الموضوع والمكان والضوء وبنية العدسة من القسم السابق.
  3. اختر نسبة 16:9 لصور المقالات، أو 1:1 للصور المصغّرة.
  4. ولّد ثلاثة تنويعات وغيّر تفصيلًا واحدًا فقط بينها. بذلك تعرف أي كلمة قامت بالعمل.
  5. انسخ الصياغة الناجحة إلى prompts.txt أو إلى استدعاء build_prompt الخاص بك.

💡 إذا توفّر حقل للبذرة (seed)، فثبّتها أثناء مقارنة الصياغات. مع تثبيت البذرة، يأتي أي تغيير في النتيجة من أمرك النصي وحده ولا شيء غيره.

حين يمنحك P Image الكادر الصحيح لكن ليس اللمسة النهائية الصحيحة، شغّل الأمر النصي نفسه عبر Flux 2 Pro أو Seedream 4.5، وقارن النتائج جنبًا إلى جنب. لكل نموذج عاداته، وعشر دقائق من الاختبار توفّر ساعات من إعادة الكتابة لاحقًا.

أصلح الأخطاء التي ستواجهها

أخطاء المصادقة

يعني خطأ 401 أو 403 غالبًا أن الرمز لم يصل إلى الطلب أبدًا. تحقّق من أن PICASSOIA_API_TOKEN مضبوط في الطرفية نفسها التي تشغّل Python فيها، لأن المتغير المصدَّر في نافذة لا يوجد في نافذة أخرى. تحقّق أيضًا من علامات اقتباس زائدة أو مسافة في نهاية القيمة.

الحدود والمهام العالقة

العَرَضالسبب المرجّحالإصلاح
استجابة 429أكثر من 5 مهام تعمل في وقت واحداخفض ThreadPool إلى 3 أو 4 وأعد المحاولة بعد انتظار قصير
استجابة 400الأمر النصي يتجاوز 4,000 حرف، أو اسم حقل خاطئاقتطع الأمر النصي واطبع نص الخطأ
المهمة لا تنتهي أبدًاطابور مزدحم أو مهمة متوقفةأبقِ موعد limit النهائي وأعد المحاولة مرة واحدة
حالة failedرفض النموذج الأمر النصيبسّطه وأزل الرموز غير المعتادة

في المحاولات المتكررة، انتظر مدة أطول بعد كل فشل (2 ثم 4 ثم 8 ثوانٍ) واستسلم بعد ثلاث محاولات. قصف واجهة API بمحاولات فورية لا يفعل سوى إبقائك فوق الحد.

نتائج ناعمة أو مموّهة

امرأة تفحص صورة طبيعة كبيرة مطبوعة على طاولة رسم

إذا كان الكادر صحيحًا لكن التفاصيل ناعمة، فالسبب غالبًا هو الدقة لا الأمر النصي. اختر نموذجًا يُخرج دقة عالية، مثل Seedream 4.5، أو شغّل الصورة النهائية عبر نموذج Super Resolution على PicassoIA لرفعها بمقدار 2x إلى 4x. تحقّق أيضًا من أن الأمر النصي يذكر كاميرا وعدسة، لأن كلمة "photograph" وحدها تترك مساحة واسعة من الاحتمالات.

أنشئ أول صورة لك اليوم

رجل يرتدي سترة فحمية يحمل جهازًا لوحيًا فيه صورة لممر غابة مضاء بالشمس داخل مقهى

صار لديك الآن سكربت يحوّل جملة إلى صورة فوتوغرافية، ومشغّل دفعات يحترم حد الخمس مهام، وقالب أوامر نصية يمكنك إعادة استخدامه في كل مشروع. أسرع خطوة تالية صغيرة: افتح P Image في متصفحك، واختبر ثلاثة أوامر نصية، والصق أفضل صياغة في prompts.txt، ثم شغّل الدفعة.

حين تريد التوسع، تسرد صفحة PicassoIA API نقاط النهاية، ويعرض قائمة النماذج الكاملة نماذج تحويل النص إلى صورة والفيديو والكلام التي يمكنك تجربتها بعد ذلك. تعمل حلقة الإنشاء والاستطلاع والتنزيل نفسها مع مهام الفيديو، لذلك سيكون السكربت الذي كتبته اليوم أساسًا لمولّد مقاطع الغد. اختر أمرًا نصيًا، شغّل السكربت، وشاهد ما يُحفظ في مجلد المخرجات لديك.

شارك هذا المقال

اختر لغتك

مقالات ذات صلة