Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 26 additions & 0 deletions data/datasets/div2k-dataset-diverse-2k.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
{
"name": "DIV2K dataset: DIVerse 2K",
"author": [
"radu-timofte",
"eirikur-agustsson",
"shuhang-gu",
"jiqing-wu",
"andrey-ignatov",
"luc-van-gool"
],
"license": "Academic-Research-Only",
"tags": [
"dataset:realistic"
],
"description": "The DIV2K dataset is divided into:\n\ntrain data:\nstarting from 800 high definition high resolution images we obtain corresponding low resolution images and provide both high and low resolution images for 2, 3, and 4 downscaling factors\nvalidation data: 100 high definition high resolution images are used for genereting low resolution corresponding images, the low res are provided from the beginning of the challenge and are meant for the participants to get online feedback from the validation server; the high resolution images will be released when the final phase of the challenge starts.\n\ntest data:\n100 diverse images are used to generate low resolution corresponding images; the participants will receive the low resolution images when the final evaluation phase starts and the results will be announced after the challenge is over and the winners are decided.",
"date": "2026-06-15",
"url": "https://data.vision.ee.ethz.ch/cvl/DIV2K/",
"images": [
{
"type": "paired",
"caption": "900",
"LR": "https://i.slow.pics/KWca9Mq7.png",
"SR": "https://i.slow.pics/7P7FzpFq.png"
}
]
}
12 changes: 12 additions & 0 deletions data/tag-categories.json
Original file line number Diff line number Diff line change
Expand Up @@ -162,5 +162,17 @@
"helper:invalid-scale",
"helper:invalid-channels"
]
},
"dataset": {
"name": "Dataset",
"description": "Tags specific to datasets",
"order": 9,
"simple": true,
"tags": [
"dataset:realistic",
"dataset:anime",
"dataset:manga",
"dataset:game-textures"
]
}
}
16 changes: 16 additions & 0 deletions data/tags.json
Original file line number Diff line number Diff line change
Expand Up @@ -292,6 +292,22 @@
"name": "RGBA",
"description": "The model upscales images with transparency."
},
"dataset:anime": {
"name": "Anime",
"description": "Anime dataset"
},
"dataset:game-textures": {
"name": "Game Textures",
"description": "Game textures dataset"
},
"dataset:manga": {
"name": "Manga",
"description": "Manga dataset"
},
"dataset:realistic": {
"name": "Realistic",
"description": "Realistic dataset"
},
"helper:invalid-channels": {
"name": "Invalid channels",
"description": "The number of input or output channels of the model is not valid."
Expand Down
18 changes: 18 additions & 0 deletions data/users.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,9 @@
"alsa": {
"name": "Alsa"
},
"andrey-ignatov": {
"name": "Andrey Ignatov"
},
"aptitude": {
"name": "aptitude"
},
Expand Down Expand Up @@ -83,6 +86,9 @@
"dinjerr": {
"name": "DinJerr"
},
"eirikur-agustsson": {
"name": "Eirikur Agustsson"
},
"eula": {
"name": "end user license agreement#9756"
},
Expand Down Expand Up @@ -128,6 +134,9 @@
"jingyunliang": {
"name": "JingyunLiang"
},
"jiqing-wu": {
"name": "Jiqing Wu"
},
"jixiaozhong": {
"name": "jixiaozhong"
},
Expand Down Expand Up @@ -164,6 +173,9 @@
"loinne": {
"name": "Loinne"
},
"luc-van-gool": {
"name": "Luc Van Gool"
},
"lyonhrt": {
"name": "LyonHrt"
},
Expand Down Expand Up @@ -218,6 +230,9 @@
"pokepress": {
"name": "pokepress"
},
"radu-timofte": {
"name": "Radu Timofte"
},
"rastrum": {
"name": "Rastrum"
},
Expand Down Expand Up @@ -248,6 +263,9 @@
"sharekhan": {
"name": "SharekhaN"
},
"shuhang-gu": {
"name": "Shuhang Gu"
},
"sirosky": {
"name": "Sirosky"
},
Expand Down
5 changes: 5 additions & 0 deletions scripts/validate-db.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import fs from 'fs/promises';
import path from 'path';
import { fileApi } from '../src/lib/server/file-data';
import { validateDataset } from '../src/lib/validate-dataset';
import { Report, validateModel } from '../src/lib/validate-model';

const getAllFiles = async (dir: string): Promise<string[]> => {
Expand All @@ -23,6 +24,7 @@ const getAllFiles = async (dir: string): Promise<string[]> => {

const getReports = async (): Promise<Report[]> => {
const modelData = await fileApi.models.getAll();
const datasetData = await fileApi.datasets.getAll();
const architectureData = await fileApi.architectures.getAll();
const tagData = await fileApi.tags.getAll();
const userData = await fileApi.users.getAll();
Expand All @@ -31,6 +33,9 @@ const getReports = async (): Promise<Report[]> => {
for (const [modelId, model] of modelData) {
errors.push(...validateModel(model, modelId, modelData, architectureData, tagData, userData, fileApi));
}
for (const [datasetId, dataset] of datasetData) {
errors.push(...validateDataset(dataset, datasetId, fileApi));
}

const jsonFiles = (await getAllFiles('data/')).filter((file) => file.endsWith('.json'));
await Promise.all(
Expand Down
23 changes: 23 additions & 0 deletions src/elements/components/dataset-card-grid.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import React from 'react';
import { Dataset, DatasetId } from '../../lib/schema';
import { DatasetCard } from './dataset-card';
import style from './model-card-grid.module.scss';

export interface DatasetCardGridProps {
datasets: readonly (readonly [DatasetId, Dataset])[];
}

export function DatasetCardGrid({ datasets }: DatasetCardGridProps) {
return (
<div className={style.grid}>
{datasets.map(([id, dataset], i) => (
<DatasetCard
dataset={dataset}
id={id}
key={id}
lazy={i >= 12}
/>
))}
</div>
);
}
205 changes: 205 additions & 0 deletions src/elements/components/dataset-card.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,205 @@
/* eslint-disable @next/next/no-img-element */
/* eslint-disable react/display-name */
import React, { memo, useRef, useState } from 'react';
import { LazyLoadComponent } from 'react-lazy-load-image-component';
import { useDevicePixelRatio } from '../../lib/hooks/use-device-pixel-ratio';
import { useUpdateDataset } from '../../lib/hooks/use-update-dataset';
import { useUsers } from '../../lib/hooks/use-users';
import { useWebApi } from '../../lib/hooks/use-web-api';
import { joinList } from '../../lib/react-util';
import { Dataset, DatasetId, ImageSize, PairedImage } from '../../lib/schema';
import { asArray, assertNever, joinClasses } from '../../lib/util';
import { EditableTags } from './editable-tags';
import { Link } from './link';
import style from './model-card.module.scss';

export interface DatasetCardProps {
id: DatasetId;
dataset: Dataset;
lazy?: boolean;
}

const EMPTY_SIZE: ImageSize = {
height: 0,
width: 0,
};

function getNaturalSize(image: HTMLImageElement): ImageSize {
return {
height: image.naturalHeight,
width: image.naturalWidth,
};
}

const SideBySideImage = ({ datasetName, image }: { datasetName: string; image: PairedImage }) => {
const [lrDimensions, setLrDimensions] = useState(EMPTY_SIZE);
const [srDimensions, setSrDimensions] = useState(EMPTY_SIZE);

const maxHeight = Math.max(lrDimensions.height, srDimensions.height);
const maxWidth = Math.max(lrDimensions.width, srDimensions.width);

const lrRef = useRef<HTMLImageElement>(null);
const srRef = useRef<HTMLImageElement>(null);

const dpr = useDevicePixelRatio();
const scale = (1 / dpr) * Math.max(1, Math.round(dpr + 0.16));

return (
<div className="flex h-full w-full">
<div className="relative flex h-full w-1/2 content-center overflow-hidden align-middle">
<img
alt={datasetName}
className="rendering-pixelated absolute top-1/3 left-1/2 z-0 m-auto object-cover object-center"
loading="lazy"
ref={lrRef}
src={image.LR}
style={{
height: `${maxHeight}px`,
width: `${maxWidth}px`,
transform: `translate(-50%, -50%) scale(${scale})`,
}}
onLoad={(e) => {
setLrDimensions(getNaturalSize(e.target as HTMLImageElement));
}}
/>
</div>
<div className="relative flex h-full w-1/2 content-center overflow-hidden align-middle">
<img
alt={datasetName}
className="rendering-pixelated absolute top-1/3 left-1/2 z-0 m-auto object-cover object-center"
loading="lazy"
ref={srRef}
src={image.SR}
style={{
height: `${maxHeight}px`,
width: `${maxWidth}px`,
transform: `translate(-50%, -50%) scale(${scale})`,
}}
onLoad={(e) => {
setSrDimensions(getNaturalSize(e.target as HTMLImageElement));
}}
/>
</div>
</div>
);
};

const getDatasetCardImageComponent = (dataset: Dataset | undefined) => {
const image = dataset?.images?.[0];
if (!dataset || !image) {
return <div className="margin-auto z-0 w-full py-20 text-center text-gray-500">No Image</div>;
}
switch (image.type) {
case 'paired': {
return (
<SideBySideImage
datasetName={dataset.name}
image={image}
/>
);
}
case 'standalone': {
const imageSrc = image.url;
return (
<img
alt={dataset.name}
className="margin-auto z-0 h-full w-full object-cover"
loading="lazy"
src={imageSrc}
/>
);
}
default:
return assertNever(image);
}
};

const DatasetCardContent = memo(({ id, dataset }: DatasetCardProps) => {
const { userData } = useUsers();
const { webApi, editMode } = useWebApi();
const { updateDatasetProperty } = useUpdateDataset(webApi, id);

const isPaired = dataset.images?.[0]?.type === 'paired' && !editMode;

return (
<div className={style.inner}>
<Link
className={joinClasses(style.thumbnail, isPaired && style.paired, 'bg-fade-300 dark:bg-fade-700 ')}
href={`/datasets/${id}`}
tabIndex={-1}
>
{getDatasetCardImageComponent(dataset)}
</Link>

<div className={joinClasses(style.details, isPaired && style.paired)}>
<Link
className={`${style.name} block text-xl font-bold text-gray-800 dark:text-gray-100`}
href={`/datasets/${id}`}
>
{dataset.name}
</Link>
<div className="text-sm text-gray-600 dark:text-gray-400">
{'by '}
{joinList(
asArray(dataset.author).map((userId) => (
<Link
className="font-bold text-accent-600 dark:text-accent-400"
href={`/users/${userId}`}
key={userId}
>
{userData.get(userId)?.name ?? `unknown user:${userId}`}
</Link>
))
)}
</div>

{/* Description */}
<div className="mb-2 mt-1 text-sm text-gray-600 line-clamp-3 dark:text-gray-400">
{dataset.description}
</div>

{/* Tags */}
<div className="flex flex-row flex-wrap gap-1 text-xs">
<EditableTags
readonly={!editMode}
tags={dataset.tags}
onChange={(tags) => updateDatasetProperty('tags', tags)}
/>
</div>
</div>
</div>
);
});

export const DatasetCard = memo(({ id, dataset, lazy = false }: DatasetCardProps) => {
const { editMode } = useWebApi();

const inner = (
<div
className={joinClasses(
style.modelCard,
!editMode && style.overflowHidden,
'border-gray-300 bg-white shadow-lg hover:shadow-2xl dark:border-gray-700 dark:bg-fade-900'
)}
>
<DatasetCardContent
dataset={dataset}
id={id}
/>
</div>
);

if (!lazy) return inner;

return (
<LazyLoadComponent
placeholder={
<div
className={`${style.modelCard} border-gray-300 bg-white shadow-lg hover:shadow-2xl dark:border-gray-700 dark:bg-fade-900`}
/>
}
>
{inner}
</LazyLoadComponent>
);
});
Loading
Loading