ACMX 2.139.0
Dual-Backend Real-Time GPU Video Synthesis
Loading...
Searching...
No Matches
deep-dream-settings.cpp
Go to the documentation of this file.
2
3#include "custom_style.hpp"
4
5#include <QCheckBox>
6#include <QComboBox>
7#include <QDialogButtonBox>
8#include <QDoubleSpinBox>
9#include <QFile>
10#include <QFileDialog>
11#include <QFileInfo>
12#include <QFormLayout>
13#include <QGroupBox>
14#include <QHBoxLayout>
15#include <QJsonArray>
16#include <QJsonDocument>
17#include <QJsonObject>
18#include <QJsonParseError>
19#include <QLabel>
20#include <QLineEdit>
21#include <QMessageBox>
22#include <QPushButton>
23#include <QRandomGenerator>
24#include <QRegularExpression>
25#include <QScrollArea>
26#include <QSettings>
27#include <QSpinBox>
28#include <QStringList>
29#include <QVBoxLayout>
30
31#include <array>
32
33namespace {
34 class DreamChannelSpinBox final : public QSpinBox {
35 public:
36 explicit DreamChannelSpinBox(QWidget *parent = nullptr) : QSpinBox(parent) {}
37
38 protected:
39 QValidator::State validate(QString &text, int &position) const override {
40 const QString value = text.trimmed();
41 if (value.compare("A", Qt::CaseInsensitive) == 0 || value.compare("All", Qt::CaseInsensitive) == 0 || value.compare("All channels", Qt::CaseInsensitive) == 0) {
42 return QValidator::Acceptable;
43 }
44 if (QStringLiteral("All").startsWith(value, Qt::CaseInsensitive)) {
45 return QValidator::Intermediate;
46 }
47 return QSpinBox::validate(text, position);
48 }
49
50 int valueFromText(const QString &text) const override {
51 const QString value = text.trimmed();
52 if (value.compare("A", Qt::CaseInsensitive) == 0 || value.compare("All", Qt::CaseInsensitive) == 0 || value.compare("All channels", Qt::CaseInsensitive) == 0) {
53 return minimum();
54 }
55 return QSpinBox::valueFromText(text);
56 }
57 };
58} // namespace
59
60DeepDreamSettingsDialog::DeepDreamSettingsDialog(bool gpu_filter_enabled, QWidget *parent) : QDialog(parent), gpu_filter_available(gpu_filter_enabled) {
61 setWindowTitle("Deep Dream Settings");
62 setMinimumSize(560, 620);
63
64 enable_check_box = new QCheckBox("Enable Deep Dream", this);
65 model_file_edit = new QLineEdit(this);
66 model_file_edit->setReadOnly(true);
67 model_file_edit->setPlaceholderText("Select a TorchScript .pt model...");
68 browse_model_button = new QPushButton("Browse...", this);
69 model_metadata_label = new QLabel(this);
70 model_metadata_label->setTextFormat(Qt::PlainText);
71 model_metadata_label->setWordWrap(true);
72
73 layer_combo_box = new QComboBox(this);
74 layer_combo_box->setEditable(true);
75 layer_combo_box->addItems({"relu1_1", "relu1_2", "relu2_1", "relu2_2", "relu3_1", "relu3_2", "relu3_3", "relu4_1", "relu4_2", "relu4_3", "relu5_1", "relu5_2", "relu5_3"});
76
77 iterations_spin_box = new QSpinBox(this);
78 iterations_spin_box->setRange(1, 100);
79 strength_spin_box = new QDoubleSpinBox(this);
80 strength_spin_box->setRange(0.0001, 10.0);
81 strength_spin_box->setDecimals(4);
82 strength_spin_box->setSingleStep(0.01);
83 feedback_spin_box = new QDoubleSpinBox(this);
84 feedback_spin_box->setRange(0.0, 0.99);
85 feedback_spin_box->setDecimals(3);
86 feedback_spin_box->setSingleStep(0.05);
87 zoom_spin_box = new QDoubleSpinBox(this);
88 zoom_spin_box->setRange(0.9, 1.1);
89 zoom_spin_box->setDecimals(4);
90 zoom_spin_box->setSingleStep(0.005);
91 rotation_spin_box = new QDoubleSpinBox(this);
92 rotation_spin_box->setRange(-5.0, 5.0);
93 rotation_spin_box->setDecimals(3);
94 rotation_spin_box->setSingleStep(0.1);
95
96 native_size_check_box = new QCheckBox("Use native input size", this);
97 maximum_dimension_spin_box = new QSpinBox(this);
98 maximum_dimension_spin_box->setRange(64, 4096);
99 maximum_dimension_spin_box->setSingleStep(64);
100 maximum_dimension_spin_box->setSuffix(" px");
101 fp16_check_box = new QCheckBox("Use FP16", this);
102 fp16_check_box->setToolTip("Use half-precision model and working tensors to reduce CUDA memory "
103 "and improve performance.");
105 channel_spin_box->setRange(-1, 65535);
106 channel_spin_box->setSpecialValueText("All channels");
107 channel_spin_box->setToolTip("Enter a channel number, A, or All to select every channel.");
108 octaves_spin_box = new QSpinBox(this);
109 octaves_spin_box->setRange(1, 8);
110 octave_scale_spin_box = new QDoubleSpinBox(this);
111 octave_scale_spin_box->setRange(1.1, 3.0);
112 octave_scale_spin_box->setDecimals(2);
113 octave_scale_spin_box->setSingleStep(0.1);
114 jitter_spin_box = new QSpinBox(this);
115 jitter_spin_box->setRange(0, 64);
116 jitter_spin_box->setSuffix(" px");
117 smoothing_spin_box = new QSpinBox(this);
118 smoothing_spin_box->setRange(0, 16);
119 smoothing_spin_box->setSuffix(" px");
120 gpu_filter_first_check_box = new QCheckBox("Run acidcam-gpu filters before Deep Dream", this);
121 gpu_filter_first_check_box->setToolTip(gpu_filter_available ? "Transform the CUDA source with acidcam-gpu before LibTorch." : "Enable an acidcam-gpu filter chain first.");
122 deep_original_check_box = new QCheckBox("Independent-frame preview (--deep-orig)", this);
123 deep_original_check_box->setToolTip("Process each video frame independently without temporal feedback, "
124 "zoom, or rotation. Fully applies on the next launch.");
125
126 auto *model_group = new QGroupBox("Model", this);
127 auto *model_layout = new QFormLayout(model_group);
128 auto *model_row = new QHBoxLayout;
129 model_row->addWidget(model_file_edit, 1);
130 model_row->addWidget(browse_model_button);
131 model_layout->addRow("TorchScript model:", model_row);
132 model_layout->addRow("Model information:", model_metadata_label);
133 model_layout->addRow("Feature layer:", layer_combo_box);
134
135 auto *dream_group = new QGroupBox("Gradient Ascent", this);
136 auto *dream_layout = new QFormLayout(dream_group);
137 dream_layout->addRow("Iterations:", iterations_spin_box);
138 dream_layout->addRow("Strength:", strength_spin_box);
139 dream_layout->addRow("Target channel:", channel_spin_box);
140 dream_layout->addRow("Octaves:", octaves_spin_box);
141 dream_layout->addRow("Octave scale:", octave_scale_spin_box);
142 dream_layout->addRow("Spatial jitter:", jitter_spin_box);
143 dream_layout->addRow("Gradient smoothing:", smoothing_spin_box);
144
145 auto *feedback_group = new QGroupBox("Temporal Feedback", this);
146 auto *feedback_layout = new QFormLayout(feedback_group);
147 feedback_layout->addRow("Previous-frame blend:", feedback_spin_box);
148 feedback_layout->addRow("Feedback zoom:", zoom_spin_box);
149 feedback_layout->addRow("Feedback rotation:", rotation_spin_box);
150
151 auto *performance_group = new QGroupBox("Performance and Pipeline", this);
152 auto *performance_layout = new QFormLayout(performance_group);
153 performance_layout->addRow(native_size_check_box);
154 performance_layout->addRow("Maximum dimension:", maximum_dimension_spin_box);
155 performance_layout->addRow(fp16_check_box);
156 performance_layout->addRow(deep_original_check_box);
157
158 auto *contents = new QWidget(this);
159 auto *contents_layout = new QVBoxLayout(contents);
160 contents_layout->addWidget(enable_check_box);
161 contents_layout->addWidget(gpu_filter_first_check_box);
162 contents_layout->addWidget(model_group);
163 contents_layout->addWidget(dream_group);
164 contents_layout->addWidget(feedback_group);
165 contents_layout->addWidget(performance_group);
166 contents_layout->addStretch();
167
168 auto *scroll_area = new QScrollArea(this);
169 scroll_area->setWidgetResizable(true);
170 scroll_area->setWidget(contents);
171
172 auto *buttons = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Apply | QDialogButtonBox::Cancel, this);
173 QPushButton *randomize_button = buttons->addButton("Randomize", QDialogButtonBox::ActionRole);
174 randomize_button->setToolTip("Generate and immediately apply a random Deep Dream preset.");
175 auto *layout = new QVBoxLayout(this);
176 layout->addWidget(scroll_area, 1);
177 layout->addWidget(buttons);
178
179 connect(enable_check_box, &QCheckBox::toggled, this, [this](bool) { update_enabled_state(); });
180 connect(native_size_check_box, &QCheckBox::toggled, this, [this](bool) { update_enabled_state(); });
181 connect(deep_original_check_box, &QCheckBox::toggled, this, [this](bool) { update_enabled_state(); });
182 connect(browse_model_button, &QPushButton::clicked, this, &DeepDreamSettingsDialog::browse_model);
183 connect(buttons, &QDialogButtonBox::accepted, this, &DeepDreamSettingsDialog::accept_settings);
184 connect(buttons->button(QDialogButtonBox::Apply), &QPushButton::clicked, this, &DeepDreamSettingsDialog::apply_settings);
185 connect(randomize_button, &QPushButton::clicked, this, &DeepDreamSettingsDialog::randomize_settings);
186 connect(buttons, &QDialogButtonBox::rejected, this, &QDialog::reject);
187
192}
193
196 result.enabled = enable_check_box->isChecked();
197 result.model_file = model_file_edit->text().trimmed();
198 result.layer = layer_combo_box->currentText().trimmed();
199 result.iterations = iterations_spin_box->value();
200 result.strength = strength_spin_box->value();
201 result.deep_original = deep_original_check_box->isChecked();
202 result.feedback = result.deep_original ? 0.0 : feedback_spin_box->value();
203 result.zoom = result.deep_original ? 1.0 : zoom_spin_box->value();
204 result.rotation = result.deep_original ? 0.0 : rotation_spin_box->value();
205 result.maximum_dimension = native_size_check_box->isChecked() ? 0 : maximum_dimension_spin_box->value();
206 result.fp16 = fp16_check_box->isChecked();
207 result.channel = channel_spin_box->value();
208 result.octaves = octaves_spin_box->value();
209 result.octave_scale = octave_scale_spin_box->value();
210 result.jitter = jitter_spin_box->value();
211 result.smoothing = smoothing_spin_box->value();
212 result.gpu_filter_first = gpu_filter_first_check_box->isChecked();
213 return result;
214}
215
217 QSettings settings("LostSideDead", "acmx2");
218 QString directory = settings.value("deep_dream/last_model_directory").toString();
219 if (directory.isEmpty()) {
220 directory = QFileInfo(model_file_edit->text()).absolutePath();
221 }
222 const QString filename = QFileDialog::getOpenFileName(this, "Select Deep Dream TorchScript Model", directory, "TorchScript Models (*.pt *.pth);;All Files (*)");
223 if (filename.isEmpty()) {
224 return;
225 }
226 model_file_edit->setText(filename);
228 settings.setValue("deep_dream/last_model_directory", QFileInfo(filename).absolutePath());
229}
230
232 const QString model_filename = model_file_edit->text().trimmed();
233 if (model_filename.isEmpty()) {
234 model_metadata_label->setText("Select a model to inspect its layers.");
235 return;
236 }
237
238 const QString metadata_filename = model_filename + ".json";
239 QFile metadata_file(metadata_filename);
240 const QFileInfo metadata_info(metadata_file);
241 QString error;
242 if (!metadata_info.isFile()) {
243 model_metadata_label->setText("No .pt.json sidecar found; enter a feature layer manually.");
244 return;
245 }
246 constexpr qint64 MAXIMUM_METADATA_BYTES = 1024 * 1024;
247 if (metadata_info.size() <= 0 || metadata_info.size() > MAXIMUM_METADATA_BYTES) {
248 error = "The model metadata sidecar has an invalid size.";
249 } else if (!metadata_file.open(QIODevice::ReadOnly)) {
250 error = "The model metadata sidecar could not be opened.";
251 }
252
253 QJsonDocument document;
254 if (error.isEmpty()) {
255 QJsonParseError parse_error;
256 document = QJsonDocument::fromJson(metadata_file.readAll(), &parse_error);
257 if (parse_error.error != QJsonParseError::NoError || !document.isObject()) {
258 error = "The model metadata sidecar is not valid JSON.";
259 }
260 }
261
262 QString architecture;
263 QString default_layer;
264 QStringList layers;
265 int minimum_size = 0;
266 if (error.isEmpty()) {
267 const QJsonObject root = document.object();
268 if (root.value("format").toString() != "acmxvk-deep-dream" || root.value("version").toInt(-1) != 1) {
269 error = "The sidecar is not supported ACMXVK Deep Dream metadata.";
270 } else {
271 architecture = root.value("architecture").toString().trimmed();
272 default_layer = root.value("default_layer").toString().trimmed();
273 const QJsonArray layer_array = root.value("layers").toArray();
274 static const QRegularExpression name_pattern(QStringLiteral("^[A-Za-z0-9_.-]{1,64}$"));
275 if (!name_pattern.match(architecture).hasMatch() || layer_array.isEmpty() || layer_array.size() > 256) {
276 error = "The model metadata fields are invalid.";
277 } else {
278 for (const QJsonValue value : layer_array) {
279 const QString name = value.toObject().value("name").toString().trimmed();
280 if (!name_pattern.match(name).hasMatch() || layers.contains(name)) {
281 error = "The model metadata contains an invalid or duplicate layer.";
282 break;
283 }
284 layers.append(name);
285 }
286 }
287 const QJsonObject input = root.value("input").toObject();
288 minimum_size = input.value("minimum_size").toInt(0);
289 if (error.isEmpty() && (!layers.contains(default_layer) || minimum_size < 1 || minimum_size > 4096)) {
290 error = "The model default layer or input size is invalid.";
291 }
292 }
293 }
294
295 if (!error.isEmpty()) {
296 model_metadata_label->setText(error + " Enter a feature layer manually.");
297 model_metadata_label->setToolTip(metadata_filename);
298 if (report_error) {
299 QMessageBox::warning(this, "Invalid Deep Dream Metadata", error + "\n\n" + metadata_filename);
300 }
301 return;
302 }
303
304 const QString selected_layer = layer_combo_box->currentText().trimmed();
305 layer_combo_box->clear();
306 layer_combo_box->addItems(layers);
307 layer_combo_box->setCurrentText(layers.contains(selected_layer) ? selected_layer : default_layer);
308 model_metadata_label->setText(QString("%1, %2 feature layers, minimum input %3 px").arg(architecture).arg(layers.size()).arg(minimum_size));
309 model_metadata_label->setToolTip(metadata_filename);
310}
311
313 if (enable_check_box->isChecked()) {
314 const QFileInfo model(model_file_edit->text().trimmed());
315 if (!model.isFile()) {
316 QMessageBox::warning(this, "Deep Dream Model Required", "Select an existing TorchScript model file.");
317 model_file_edit->setFocus();
318 return false;
319 }
320 static const QRegularExpression layer_pattern(QStringLiteral("^[A-Za-z0-9_.-]+$"));
321 if (!layer_pattern.match(layer_combo_box->currentText().trimmed()).hasMatch()) {
322 QMessageBox::warning(this, "Invalid Deep Dream Layer", "Enter a named layer such as relu4_2 or a numeric layer index.");
323 layer_combo_box->setFocus();
324 return false;
325 }
327 QMessageBox::warning(this,
328 "GPU Filter Required",
329 "Configure and enable an acidcam-gpu filter chain before "
330 "selecting GPU filters before Deep Dream.");
331 return false;
332 }
333 }
334 return true;
335}
336
338 iterations_spin_box->interpretText();
339 strength_spin_box->interpretText();
340 feedback_spin_box->interpretText();
341 zoom_spin_box->interpretText();
342 rotation_spin_box->interpretText();
343 maximum_dimension_spin_box->interpretText();
344 channel_spin_box->interpretText();
345 octaves_spin_box->interpretText();
346 octave_scale_spin_box->interpretText();
347 jitter_spin_box->interpretText();
348 smoothing_spin_box->interpretText();
349}
350
352 if (!QFileInfo(model_file_edit->text().trimmed()).isFile()) {
353 QMessageBox::warning(this, "Deep Dream Model Required", "Select a TorchScript model before randomizing.");
354 model_file_edit->setFocus();
355 return;
356 }
357
358 QRandomGenerator *random = QRandomGenerator::global();
359 enable_check_box->setChecked(true);
360 if (layer_combo_box->count() > 0) {
361 layer_combo_box->setCurrentIndex(random->bounded(layer_combo_box->count()));
362 }
363 iterations_spin_box->setValue(1);
364 strength_spin_box->setValue(0.01 + random->generateDouble() * 0.04);
365 channel_spin_box->setValue(-1);
366 octaves_spin_box->setValue(random->bounded(1, 3));
367 octave_scale_spin_box->setValue(1.2 + random->generateDouble() * 0.4);
368 jitter_spin_box->setValue(random->bounded(0, 5));
369 smoothing_spin_box->setValue(random->bounded(1, 4));
370 feedback_spin_box->setValue(0.55 + random->generateDouble() * 0.35);
371 zoom_spin_box->setValue(0.985 + random->generateDouble() * 0.03);
372 const double rotation_magnitude = 0.5 + random->generateDouble() * 2.5;
373 rotation_spin_box->setValue(random->bounded(2) == 0 ? -rotation_magnitude : rotation_magnitude);
374 static constexpr std::array<int, 4> RANDOM_DIMENSIONS = {256, 384, 512, 640};
375 native_size_check_box->setChecked(false);
376 maximum_dimension_spin_box->setValue(RANDOM_DIMENSIONS[static_cast<std::size_t>(random->bounded(static_cast<int>(RANDOM_DIMENSIONS.size())))]);
378}
379
382 if (!validate_settings()) {
383 return;
384 }
386 emit settingsApplied();
387}
388
391 if (!validate_settings()) {
392 return;
393 }
395 emit settingsApplied();
396 accept();
397}
398
400 QSettings settings("LostSideDead", "acmx2");
401 enable_check_box->setChecked(settings.value("deep_dream/enabled", false).toBool());
402 model_file_edit->setText(settings.value("deep_dream/model_file", QString()).toString());
403 layer_combo_box->setCurrentText(settings.value("deep_dream/layer", "relu4_2").toString());
404 iterations_spin_box->setValue(settings.value("deep_dream/iterations", 1).toInt());
405 strength_spin_box->setValue(settings.value("deep_dream/strength", 0.05).toDouble());
406 feedback_spin_box->setValue(settings.value("deep_dream/feedback", 0.9).toDouble());
407 zoom_spin_box->setValue(settings.value("deep_dream/zoom", 1.01).toDouble());
408 rotation_spin_box->setValue(settings.value("deep_dream/rotation", 0.1).toDouble());
409 const int maximum_dimension = settings.value("deep_dream/maximum_dimension", 512).toInt();
410 native_size_check_box->setChecked(maximum_dimension == 0);
411 maximum_dimension_spin_box->setValue(maximum_dimension == 0 ? 512 : maximum_dimension);
412 fp16_check_box->setChecked(settings.value("deep_dream/fp16", false).toBool());
413 channel_spin_box->setValue(settings.value("deep_dream/channel", -1).toInt());
414 octaves_spin_box->setValue(settings.value("deep_dream/octaves", 1).toInt());
415 octave_scale_spin_box->setValue(settings.value("deep_dream/octave_scale", 1.4).toDouble());
416 jitter_spin_box->setValue(settings.value("deep_dream/jitter", 0).toInt());
417 smoothing_spin_box->setValue(settings.value("deep_dream/smoothing", 0).toInt());
418 gpu_filter_first_check_box->setChecked(gpu_filter_available && settings.value("deep_dream/gpu_filter_first", false).toBool());
419 deep_original_check_box->setChecked(settings.value("deep_dream/deep_original", false).toBool());
420}
421
423 const DeepDreamConfiguration current = configuration();
424 QSettings settings("LostSideDead", "acmx2");
425 settings.setValue("deep_dream/enabled", current.enabled);
426 settings.setValue("deep_dream/model_file", current.model_file);
427 settings.setValue("deep_dream/layer", current.layer);
428 settings.setValue("deep_dream/iterations", current.iterations);
429 settings.setValue("deep_dream/strength", current.strength);
430 settings.setValue("deep_dream/feedback", feedback_spin_box->value());
431 settings.setValue("deep_dream/zoom", zoom_spin_box->value());
432 settings.setValue("deep_dream/rotation", rotation_spin_box->value());
433 settings.setValue("deep_dream/maximum_dimension", current.maximum_dimension);
434 settings.setValue("deep_dream/fp16", current.fp16);
435 settings.setValue("deep_dream/channel", current.channel);
436 settings.setValue("deep_dream/octaves", current.octaves);
437 settings.setValue("deep_dream/octave_scale", current.octave_scale);
438 settings.setValue("deep_dream/jitter", current.jitter);
439 settings.setValue("deep_dream/smoothing", current.smoothing);
440 settings.setValue("deep_dream/gpu_filter_first", current.gpu_filter_first);
441 settings.setValue("deep_dream/deep_original", current.deep_original);
442}
443
445 const bool enabled = enable_check_box->isChecked();
447 for (QWidget *widget : controls) {
448 widget->setEnabled(enabled);
449 }
450 maximum_dimension_spin_box->setEnabled(enabled && !native_size_check_box->isChecked());
451 const bool temporal_feedback_enabled = enabled && !deep_original_check_box->isChecked();
452 feedback_spin_box->setEnabled(temporal_feedback_enabled);
453 zoom_spin_box->setEnabled(temporal_feedback_enabled);
454 rotation_spin_box->setEnabled(temporal_feedback_enabled);
455 deep_original_check_box->setEnabled(enabled);
456 gpu_filter_first_check_box->setEnabled(enabled && gpu_filter_available);
458 gpu_filter_first_check_box->setChecked(false);
459 }
460}
QDoubleSpinBox * octave_scale_spin_box
DeepDreamConfiguration configuration() const
void refresh_model_metadata(bool report_error)
DeepDreamSettingsDialog(bool gpu_filter_enabled, QWidget *parent=nullptr)
QValidator::State validate(QString &text, int &position) const override
void applyCustomStyleIfEnabled(QWidget *widget)