36 setWindowTitle(
"Deep Dream Settings");
37 setMinimumSize(560, 620);
42 model_file_edit->setPlaceholderText(
"Select a TorchScript .pt model...");
51 {
"relu1_1",
"relu1_2",
"relu2_1",
"relu2_2",
"relu3_1",
52 "relu3_2",
"relu3_3",
"relu4_1",
"relu4_2",
"relu4_3",
53 "relu5_1",
"relu5_2",
"relu5_3"});
81 "Use half-precision model and working tensors to reduce CUDA memory "
82 "and improve performance.");
99 new QCheckBox(
"Run acidcam-gpu filters before Deep Dream",
this);
102 ?
"Transform the CUDA source with acidcam-gpu before LibTorch."
103 :
"Enable an acidcam-gpu filter chain first.");
105 new QCheckBox(
"Independent-frame preview (--deep-orig)",
this);
107 "Process each video frame independently without temporal feedback, "
108 "zoom, or rotation. Fully applies on the next launch.");
110 auto *model_group =
new QGroupBox(
"Model",
this);
111 auto *model_layout =
new QFormLayout(model_group);
112 auto *model_row =
new QHBoxLayout;
115 model_layout->addRow(
"TorchScript model:", model_row);
119 auto *dream_group =
new QGroupBox(
"Gradient Ascent",
this);
120 auto *dream_layout =
new QFormLayout(dream_group);
129 auto *feedback_group =
new QGroupBox(
"Temporal Feedback",
this);
130 auto *feedback_layout =
new QFormLayout(feedback_group);
135 auto *performance_group =
new QGroupBox(
"Performance and Pipeline",
this);
136 auto *performance_layout =
new QFormLayout(performance_group);
138 performance_layout->addRow(
"Maximum dimension:",
143 auto *contents =
new QWidget(
this);
144 auto *contents_layout =
new QVBoxLayout(contents);
147 contents_layout->addWidget(model_group);
148 contents_layout->addWidget(dream_group);
149 contents_layout->addWidget(feedback_group);
150 contents_layout->addWidget(performance_group);
151 contents_layout->addStretch();
153 auto *scroll_area =
new QScrollArea(
this);
154 scroll_area->setWidgetResizable(
true);
155 scroll_area->setWidget(contents);
157 auto *buttons =
new QDialogButtonBox(
158 QDialogButtonBox::Ok | QDialogButtonBox::Apply |
159 QDialogButtonBox::Cancel,
161 QPushButton *randomize_button =
162 buttons->addButton(
"Randomize", QDialogButtonBox::ActionRole);
163 randomize_button->setToolTip(
164 "Generate and immediately apply a random Deep Dream preset.");
165 auto *layout =
new QVBoxLayout(
this);
166 layout->addWidget(scroll_area, 1);
167 layout->addWidget(buttons);
177 connect(buttons, &QDialogButtonBox::accepted,
this,
179 connect(buttons->button(QDialogButtonBox::Apply), &QPushButton::clicked,
181 connect(randomize_button, &QPushButton::clicked,
this,
183 connect(buttons, &QDialogButtonBox::rejected,
this, &QDialog::reject);
237 if (model_filename.isEmpty()) {
242 const QString metadata_filename = model_filename +
".json";
243 QFile metadata_file(metadata_filename);
244 const QFileInfo metadata_info(metadata_file);
246 if (!metadata_info.isFile()) {
248 "No .pt.json sidecar found; enter a feature layer manually.");
251 constexpr qint64 MAXIMUM_METADATA_BYTES = 1024 * 1024;
252 if (metadata_info.size() <= 0 ||
253 metadata_info.size() > MAXIMUM_METADATA_BYTES) {
254 error =
"The model metadata sidecar has an invalid size.";
255 }
else if (!metadata_file.open(QIODevice::ReadOnly)) {
256 error =
"The model metadata sidecar could not be opened.";
259 QJsonDocument document;
260 if (error.isEmpty()) {
261 QJsonParseError parse_error;
262 document = QJsonDocument::fromJson(metadata_file.readAll(),
264 if (parse_error.error != QJsonParseError::NoError ||
265 !document.isObject()) {
266 error =
"The model metadata sidecar is not valid JSON.";
270 QString architecture;
271 QString default_layer;
273 int minimum_size = 0;
274 if (error.isEmpty()) {
275 const QJsonObject root = document.object();
276 if (root.value(
"format").toString() !=
"acmxvk-deep-dream" ||
277 root.value(
"version").toInt(-1) != 1) {
278 error =
"The sidecar is not supported ACMXVK Deep Dream metadata.";
280 architecture = root.value(
"architecture").toString().trimmed();
281 default_layer = root.value(
"default_layer").toString().trimmed();
282 const QJsonArray layer_array = root.value(
"layers").toArray();
283 static const QRegularExpression name_pattern(
284 QStringLiteral(
"^[A-Za-z0-9_.-]{1,64}$"));
285 if (!name_pattern.match(architecture).hasMatch() ||
286 layer_array.isEmpty() || layer_array.size() > 256) {
287 error =
"The model metadata fields are invalid.";
289 for (
const QJsonValue value : layer_array) {
291 value.toObject().value(
"name").toString().trimmed();
292 if (!name_pattern.match(name).hasMatch() ||
293 layers.contains(name)) {
294 error =
"The model metadata contains an invalid or duplicate layer.";
300 const QJsonObject input = root.value(
"input").toObject();
301 minimum_size = input.value(
"minimum_size").toInt(0);
302 if (error.isEmpty() &&
303 (!layers.contains(default_layer) || minimum_size < 1 ||
304 minimum_size > 4096)) {
305 error =
"The model default layer or input size is invalid.";
310 if (!error.isEmpty()) {
314 QMessageBox::warning(
this,
"Invalid Deep Dream Metadata",
315 error +
"\n\n" + metadata_filename);
320 const QString selected_layer =
layer_combo_box->currentText().trimmed();
327 QString(
"%1, %2 feature layers, minimum input %3 px")
381 QMessageBox::warning(
this,
"Deep Dream Model Required",
382 "Select a TorchScript model before randomizing.");
387 QRandomGenerator *random = QRandomGenerator::global();
401 zoom_spin_box->setValue(0.985 + random->generateDouble() * 0.03);
402 const double rotation_magnitude =
403 0.5 + random->generateDouble() * 2.5;
405 ? -rotation_magnitude
406 : rotation_magnitude);
407 static constexpr std::array<int, 4> RANDOM_DIMENSIONS = {256, 384, 512,
411 RANDOM_DIMENSIONS[
static_cast<std::size_t
>(
412 random->bounded(
static_cast<int>(RANDOM_DIMENSIONS.size())))]);