Прочитав свежую статью про обои в разных окружениях, начиная с Microsoft Windows 11 и методах их смены, я заинтересовался — нет ли приложения, которое может выставлять не готовые обои из папки или подкачивать из ресурса, а создавать их периодически, используя любой доступный AI сервис.
Гугление ничего не дало. С ходу я не нашел ни одного сервиса, который бы создавал обои от любого AI сервиса на лету. В сущности, это очень простая операция — почему бы не набросать свой скрипт в свободный день?
Я уже писал плагин для Visual Code для проверки правописания и переводов русский-английский, так что OpenAI ключ у меня есть. Будем пробовать DALL-E 3, к которому этот ключ подходит. На Reddit упоминали, что DALL-E умеет создавать бесшовные плиточные изображения, что будет здорово для генерации обоев. Это впоследствии не подтвердилось, как я ни старался. Так что окончательный вариант работает с полным форматом изображения.
Самым простым вариантом мне показалось написать PowerShell скрипт и посадить его на расписание (task scheduler), чтобы он запускался сам каждый день и менял обои.
Отправной точкой является документ, описывающий современный API DALL-E 3.
Вот, собственно, и весь код получения изображения от DALL-E на PowerShell:
$body = @{
"model" = "dall-e-3"
"prompt" = "$prompt --ar 16:9"
"size" = "1792x1024"
"style" = "vivid"
} | ConvertTo-Json
$response = Invoke-RestMethod -Uri "https://api.openai.com/v1/images/generations" `
-Method Post `
-Headers @{ "Authorization" = "Bearer $apiKey"; "Content-Type" = "application/json" } `
-Body $body
$imageUrl = $response.data[0].url
В переменную $prompt мы положим наш промпт. О разрешении хотелось бы сказать особо. Максимальное разрешение, поддерживаемое на данный момент DALL-E, есть 1792x1024. Но с ним возникают проблемы — через раз создается квадратное изображение, с полями по бокам. Проблема известная, описана тут. Помогает совет дополнить промпт указанием "--ar 16:9" в конце.
Хорошей идеей является не перезаписывать предыдущее полученное изображение, а создавать новое, со случайным именем. Во-первых, через некоторое время у нас появятся «любимчики», а во-вторых можно будет на разные мониторы назначать разные обои из уже имеющихся.
$chars = "abcdefghijklmnopqrstuvwxyz0123456789"
$randomString = -Join ((1..8) | ForEach-Object { $chars[(Get-Random -Maximum $chars.Length)] })
$outputFile = "$outputDir\dalle_generated_$randomString.jpg"
Ну, и осталось полученное изображение установить на десктоп:
$code = @"
[DllImport("user32.dll", CharSet = CharSet.Auto)]
public static extern int SystemParametersInfo(int uAction, int uParam, string lpvParam, int fuWinIni);
"@
$type = Add-Type -MemberDefinition $code -Name "Wallpaper" -Namespace "Win32" -PassThru
$SPI_SETDESKWALLPAPER = 0x0014
$SPIF_UPDATEINIFILE = 0x01
$SPIF_SENDCHANGE = 0x02
$type::SystemParametersInfo($SPI_SETDESKWALLPAPER, 0, $outputFile, $SPIF_UPDATEINIFILE -bor $SPIF_SENDCHANGE)
Я выбрал простой метод API SystemParametersInfo, но он не позволяет произвольно выставить, на какой монитор из имеющихся поставить обои — он ставит на все одновременно. Более сложный метод, который позволяет работать с произвольными мониторами и менять способы отображения обоев, описан у Pete Hinchley. У него создается полная обертка для COM на .NET. Для моих целей простого метода достаточно, не хочу слишком усложнять код.
Мой промпт — вкусовщина. Люблю обои в буквальном смысле, с текстурой бумаги и низким контрастом, чтобы не отвлекало и не мешало разглядеть иконки. "A full-sized wallpaper with flowers and birds and leaves. Muted dark colors, with the texture of rough paper."
Вот так выглядит основной экран:

Скрипт заработал, генерируя каждый раз новое изображение (это занимает до десяти секунд). Осталось только запускать его каждый день автоматически.
Запускаем скрипт в task scheduler (внимание на командную строку):

Вот ссылка на гитхаб с исходником скрипта. Нужен только один файл, .ps1
Что в планах: работать со всеми мониторами независимо, используя обертку Windows API от Pete Hinchley (ссылка выше) и назначать на разные мониторы разные изображения автоматически. Еще надо в свободный день развернуть Stable Diffusion и поиграться с ним из PowerShell. Но я с самого начала не рассчитывал на Stable Diffusion — он требует серьезного GPU и много памяти. Предвижу много возни с настройкой, да и не на каждом десктопе или лаптопе найдется подходящая конфигурация. Но было бы интересно попробовать.
Добавлено 2024/10/15
Чтобы избежать нежелательных полей в отдельных случаях, я проверяю десять контрольных точек по бокам изображения. Если их зеленая компонента почти одинакова (зеленая самая чувствительная визуально) — изображение отбраковывается и запрашивается снова.

Код проверки очень простой, без сторонних утилит и модулей. Порог чувствительности 24 определен экспериментально, на нескольких отбракованных изображениях.
$image = [System.Drawing.Image]::FromFile($fullName)
$points = @(
[System.Drawing.Point]::new(3, 3),
[System.Drawing.Point]::new(3, 257),
[System.Drawing.Point]::new(3, 512),
[System.Drawing.Point]::new(3, 766),
[System.Drawing.Point]::new(3, 1020),
[System.Drawing.Point]::new(1788, 5),
[System.Drawing.Point]::new(1788, 257),
[System.Drawing.Point]::new(1788, 512),
[System.Drawing.Point]::new(1788, 766),
[System.Drawing.Point]::new(1788, 1020)
)
$greenValues = $points | ForEach-Object { $image.GetPixel($_.X, $_.Y).G }
$maxDifference = 0
for ($i = 0; $i -lt $greenValues.Length - 1; $i++) {
for ($j = $i + 1; $j -lt $greenValues.Length; $j++) {
$difference = [Math]::Abs($greenValues[$i] - $greenValues[$j])
if ($difference -gt $maxDifference) {
$maxDifference = $difference
}
}
}
return $maxDifference -ge 24
Меня просили создать побольше примеров природы, чтобы оценить качество генерации. Я начал с леса, постепенно усложняя промпт и пытаясь добиться фотореалистичности. Признаюсь — это затягивает!

Кто захочет оценить возможности DALL‑E 3 — полсотни лесных пейзажей в разрешении 1792×1024 в репозитарии, в папке «nature». Использованный промпт (с незначительными вариациями) — в самом скрипте.
Удачи в изучении PowerShell и творчестве!