Skip to content
Merged
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
2 changes: 1 addition & 1 deletion client/dive-common/apispec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -170,7 +170,7 @@ interface Api {
openFromDisk(datasetType: DatasetType | 'calibration' | 'annotation' | 'text' | 'zip', directory?: boolean):
Promise<{canceled?: boolean; filePaths: string[]; fileList?: File[]; root?: string}>;
importAnnotationFile(id: string, path: string, file?: File,
additive?: boolean, additivePrepend?: string): Promise<boolean>;
additive?: boolean, additivePrepend?: string, maskLogic?: 'replace' | 'merge'): Promise<boolean>;
}
const ApiSymbol = Symbol('api');

Expand Down
16 changes: 15 additions & 1 deletion client/dive-common/components/ImportAnnotations.vue
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<script lang="ts">
import { defineComponent, ref } from 'vue';
import { defineComponent, Ref, ref } from 'vue';
import { useApi } from 'dive-common/apispec';
import { usePrompt } from 'dive-common/vue-utilities/prompt-service';
import { useHandler } from 'vue-media-annotator/provides';
Expand Down Expand Up @@ -37,6 +37,8 @@ export default defineComponent({
const menuOpen = ref(false);
const additive = ref(false);
const additivePrepend = ref('');
const maskActions = ref(['replace', 'merge']);
const maskActionSelection: Ref<'replace' | 'merge'> = ref('merge');
const openUpload = async () => {
try {
const ret = await openFromDisk('annotation');
Expand All @@ -52,6 +54,7 @@ export default defineComponent({
ret.fileList[0],
additive.value,
additivePrepend.value,
maskActionSelection.value,
);
} else {
importFile = await importAnnotationFile(
Expand All @@ -60,6 +63,7 @@ export default defineComponent({
undefined,
additive.value,
additivePrepend.value,
maskActionSelection.value,
);
}

Expand Down Expand Up @@ -91,6 +95,8 @@ export default defineComponent({
menuOpen,
additive,
additivePrepend,
maskActionSelection,
maskActions,

};
},
Expand Down Expand Up @@ -183,6 +189,14 @@ export default defineComponent({
clearable
/>
</div>
<v-row>
<v-select
v-model="maskActionSelection"
:items="maskActions"
label="Mask Import Action"
hide-details
/>
</v-row>
</v-col>
</v-container>
</v-card>
Expand Down
2 changes: 1 addition & 1 deletion client/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "dive-dsa",
"version": "1.11.16",
"version": "1.11.17",
"author": {
"name": "Kitware, Inc.",
"email": "Bryon.Lewis@kitware.com"
Expand Down
4 changes: 2 additions & 2 deletions client/platform/web-girder/api/dataset.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ function makeViameFolder({
);
}

async function importAnnotationFile(parentId: string, path: string, file?: HTMLFile, additive = false, additivePrepend = '') {
async function importAnnotationFile(parentId: string, path: string, file?: HTMLFile, additive = false, additivePrepend = '', maskLogic: 'replace' | 'merge' = 'merge') {
if (file === undefined) {
return false;
}
Expand All @@ -138,7 +138,7 @@ async function importAnnotationFile(parentId: string, path: string, file?: HTMLF
if (path.endsWith('.json') || path.endsWith('.csv')) {
skipJobs = true;
}
const final = await postProcess(parentId, skipJobs, false, additive, additivePrepend);
const final = await postProcess(parentId, skipJobs, false, additive, additivePrepend, maskLogic);
return final.status === 200;
}
}
Expand Down
4 changes: 2 additions & 2 deletions client/platform/web-girder/api/rpc.service.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import girderRest from 'platform/web-girder/plugins/girder';
import type { GirderJob } from '@girder/components/src';

function postProcess(folderId: string, skipJobs = false, skipTranscoding = false, additive = false, additivePrepend = '') {
function postProcess(folderId: string, skipJobs = false, skipTranscoding = false, additive = false, additivePrepend = '', maskLogic: 'replace' | 'merge' = 'merge') {
return girderRest.post(`dive_rpc/postprocess/${folderId}`, null, {
params: {
skipJobs, skipTranscoding, additive, additivePrepend,
skipJobs, skipTranscoding, additive, additivePrepend, maskLogic,
},
});
}
Expand Down
14 changes: 7 additions & 7 deletions dive-dsa-slicer/example-docker-containers/SAM2Demo/SAM2Demo.py
Original file line number Diff line number Diff line change
Expand Up @@ -359,11 +359,11 @@ def process_masks_folder(
folderId: str,
masks_path: Path,
subfolder_name='masks',
logic: Literal['replace', 'additive', 'merge'] = 'merge'):
maskLogic: Literal['replace', 'merge'] = 'merge'):
"""
Upload mask images and RLE_MASKS.json file (if available or generate an empty one).
"""
if logic == 'replace':
if maskLogic == 'replace':
folders = list(gc.listFolder(folderId, 'folder', name=subfolder_name))
if len(folders) > 0:
for folder in folders:
Expand Down Expand Up @@ -400,7 +400,7 @@ def process_masks_folder(
track_id = track_dir.name
if not has_rle_mask:
rle_masks_json[str(track_id)] = {}
if logic == 'replace':
if maskLogic == 'replace':
# Check if the track already exists
track_folders = list(gc.listFolder(masks_folder['_id'], 'folder', name=track_id))
if len(track_folders) > 0:
Expand All @@ -412,7 +412,7 @@ def process_masks_folder(

for image_path in track_dir.glob("*.png"):
frame_number = image_path.stem
if logic == 'merge':
if maskLogic == 'merge':
# Check if the image already exists
existing_items = list(gc.listItem(track_folder['_id'], name=image_path.name))
if len(existing_items) > 0:
Expand Down Expand Up @@ -469,15 +469,15 @@ def process_masks_folder(

track_json_path = masks_path / 'TrackJSON.json'
if track_json_path.exists():
# Process the TrackJSON.json file based on the logic parameter
if logic == 'replace':
# Process the TrackJSON.json file based on the maskLogic parameter
if maskLogic == 'replace':
# Replace the existing TrackJSON.json file
gc.uploadFileToFolder(
folderId,
str(track_json_path),
)
gc.post(f'dive_rpc/postprocess/{folderId}', data={"skipJobs": True})
if logic in ['additive', 'merge']:
if maskLogic == 'merge':
with open(track_json_path, 'r') as f:
json_data = json.load(f)
# Get the existing Tracks in the system
Expand Down
36 changes: 26 additions & 10 deletions scripts/masks/maskTrackGenerator.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,15 @@
# /// script
# requires-python = ">=3.13"
# dependencies = [
# "click",
# "girder-client",
# "numpy",
# "pillow",
# "pycocotools",
# "scikit-image",
# "setuptools"
# ]
# ///
import json
import random
import numpy as np
Expand All @@ -19,9 +31,9 @@
SHAPE_SIZE = (100, 100) # width, height of shape

CONFIG = [
{'track_number': 0, 'num_frames': 500, 'mask_type': 'pentagon', 'motion_type': 'circle'},
{'track_number': 1, 'num_frames': 300, 'mask_type': 'circle', 'motion_type': 'bounce'},
{'track_number': 2, 'num_frames': 200, 'mask_type': 'rectangle', 'motion_type': 'circle'},
{'track_number': 2, 'num_frames': 500, 'mask_type': 'pentagon', 'motion_type': 'circle'},
{'track_number': 3, 'num_frames': 300, 'mask_type': 'circle', 'motion_type': 'bounce'},
{'track_number': 4, 'num_frames': 200, 'mask_type': 'rectangle', 'motion_type': 'circle'},
]


Expand Down Expand Up @@ -99,10 +111,10 @@ def bouncing_motion(num_frames, img_size, shape_size):
@click.option('--upload', is_flag=True, help='Upload generated masks to Girder.')
def generate_tracks(upload):
output_masks_exist = os.path.exists('outputMasks')
rle_json_exist = os.path.exists('RLEMask.json')
rle_json_exist = os.path.exists('RLE_MASKS.json')

if output_masks_exist and rle_json_exist:
print("Found existing outputMasks folder and RLEMask.json. Skipping generation.")
print("Found existing outputMasks folder and RLE_MASKS.json. Skipping generation.")
else:
print("Generating new masks and JSON files...")

Expand Down Expand Up @@ -173,7 +185,7 @@ def generate_tracks(upload):
}

if OUTPUT_MASKS:
output_dir = os.path.join('outputMasks', f'{track_id}')
output_dir = os.path.join('outputMasks/masks', f'{track_id}')
os.makedirs(output_dir, exist_ok=True)

alpha_channel = (mask > 0).astype(np.uint8) * 255
Expand All @@ -187,18 +199,22 @@ def generate_tracks(upload):

track_data['tracks'][str(track_id)] = track

with open('trackJSON.json', 'w') as f:
with open('TrackJSON.json', 'w') as f:
json.dump(track_data, f, indent=2)

with open('RLEMask.json', 'w') as f:
json.dump(mask_data, f, indent=2)

with open('RLE_MASKS.json', 'w') as f:
json.dump(rle_masks_json, f, indent=2)

print('Generated trackJSON.json, RLEMask.json, RLE_MASKS.json', end='')
print('Generated TrackJSON.json, RLE_MASKS.json', end='')
if OUTPUT_MASKS:
print(', and PNG masks in outputMasks/')
with open('outputMasks/masks/TrackJSON.json', 'w') as f:
json.dump(track_data, f, indent=2)

with open('outputMasks/masks/RLE_MASKS.json', 'w') as f:
json.dump(rle_masks_json, f, indent=2)

else:
print('.')

Expand Down
4 changes: 2 additions & 2 deletions server/dive_server/crud_rpc.py
Original file line number Diff line number Diff line change
Expand Up @@ -201,7 +201,7 @@ def postprocess(
skipTranscoding=False,
additive=False,
additivePrepend='',
logic='replace',
maskLogic='merge',
) -> dict:
"""
Post-processing to be run after media/annotation import
Expand Down Expand Up @@ -251,7 +251,7 @@ def postprocess(
itemId=str(item["_id"]),
user_id=str(user["_id"]),
user_login=str(user["login"]),
logic=logic,
maskLogic=maskLogic,
girder_job_title=f"Extracting {item['_id']} to folder {str(dsFolder['_id'])}",
girder_client_token=str(token["_id"]),
girder_job_type="private" if job_is_private else "convert",
Expand Down
9 changes: 4 additions & 5 deletions server/dive_server/views_rpc.py
Original file line number Diff line number Diff line change
Expand Up @@ -84,26 +84,25 @@ def __init__(self, resourceName):
required=False,
)
.param(
"logic",
"maskLogic",
"Logic to use when combining annotations using Zip files (specifically masks.zip). \
'replace' will replace existing annotations with new ones. \
'merge' will combine existing and new annotations. \
'additive' will add new annotations to existing ones.",
'merge' will combine existing and new annotations.",
paramType="formData",
dataType="string",
default='merge',
required=False,
)
)
def postprocess(self, folder, skipJobs, skipTranscoding, additive, additivePrepend, logic):
def postprocess(self, folder, skipJobs, skipTranscoding, additive, additivePrepend, maskLogic):
result = crud_rpc.postprocess(
self.getCurrentUser(),
folder,
skipJobs,
skipTranscoding,
additive,
additivePrepend,
logic,
maskLogic,
)
# Return the folder for backward compatibility, but also include job_ids
return result
Expand Down
12 changes: 6 additions & 6 deletions server/dive_tasks/sam_tasks.py
Original file line number Diff line number Diff line change
Expand Up @@ -391,12 +391,12 @@ def process_masks_folder(
folderId: str,
masks_path: Path,
subfolder_name='masks',
logic: Literal['replace', 'additive', 'merge'] = 'merge',
maskLogic: Literal['replace', 'merge'] = 'merge',
):
"""
Upload mask images and RLE_MASKS.json file (if available or generate an empty one).
"""
if logic == 'replace':
if maskLogic == 'replace':
folders = list(gc.listFolder(folderId, 'folder', name=subfolder_name))
if len(folders) > 0:
for folder in folders:
Expand Down Expand Up @@ -433,7 +433,7 @@ def process_masks_folder(
track_id = track_dir.name
if not has_rle_mask:
rle_masks_json[str(track_id)] = {}
if logic == 'replace':
if maskLogic == 'replace':
# Check if the track already exists
track_folders = list(gc.listFolder(masks_folder['_id'], 'folder', name=track_id))
if len(track_folders) > 0:
Expand All @@ -445,7 +445,7 @@ def process_masks_folder(

for image_path in track_dir.glob("*.png"):
frame_number = image_path.stem
if logic == 'merge':
if maskLogic == 'merge':
# Check if the image already exists
existing_items = list(gc.listItem(track_folder['_id'], name=image_path.name))
if len(existing_items) > 0:
Expand Down Expand Up @@ -506,14 +506,14 @@ def process_masks_folder(
track_json_path = masks_path / 'TrackJSON.json'
if track_json_path.exists():
# Process the TrackJSON.json file based on the logic parameter
if logic == 'replace':
if maskLogic == 'replace':
# Replace the existing TrackJSON.json file
gc.uploadFileToFolder(
folderId,
str(track_json_path),
)
gc.post(f'dive_rpc/postprocess/{folderId}', data={"skipJobs": True})
if logic in ['additive', 'merge']:
if maskLogic == 'merge':
with open(track_json_path, 'r') as f:
json_data = json.load(f)
# Get the existing Tracks in the system
Expand Down
Loading
Loading