diff --git a/nextflow/configs/profiles/development.config b/nextflow/configs/profiles/development.config index ba49c04..25d56e5 100644 --- a/nextflow/configs/profiles/development.config +++ b/nextflow/configs/profiles/development.config @@ -110,6 +110,7 @@ singularity { process { executor = 'local' + maxForks = 1 // Since we can't assume local has multiple GPUs, we limit all tasks // Note that maxForks attached to GPU label will only limit processes of the same name diff --git a/nextflow/modules/compression.nf b/nextflow/modules/compression.nf new file mode 100644 index 0000000..fed540d --- /dev/null +++ b/nextflow/modules/compression.nf @@ -0,0 +1,81 @@ +/** + * Video compression using quality control + * + * @param tuple + * - video_file The input video file to compress + * - crf The constant rate factor for the compression + * - keyframe_interval The keyframe interval for the compression + * + * @return file Path to compressed video + */ +process COMPRESS_VIDEO_CRF { + label "compression" + + input: + tuple path(video_file), val(crf), val(keyframe_interval) + + output: + path("${video_file.baseName}_g${keyframe_interval}_crf${crf}.mp4"), emit: file + + script: + """ + ffmpeg -i ${video_file} -c:v libx264 -pix_fmt yuv420p -preset slow -crf ${crf} -g ${keyframe_interval} -f mp4 ${video_file.baseName}_g${keyframe_interval}_crf${crf}.mp4 + """ +} + +/** + * Video compression using bitrate control + * + * Runs a 2-pass encoding of the video with a target bitrate. + * + * @param tuple + * - video_file The input video file to compress + * - bitrate The target bitrate for the compression + * - keyframe_interval The keyframe interval for the compression + * + * @return file Path to compressed video + * + * @note COMPRESS_VIDEO_CRF will typically produce better videos with a low signal-to-noise ratio. + */ +process COMPRESS_VIDEO_BR { + label "compression" + + input: + tuple path(video_file), val(bitrate), val(keyframe_interval) + + output: + path("${video_file.baseName}_r${bitrate}_g${keyframe_interval}.mp4"), emit: file + + script: + """ + ffmpeg -i ${video_file} -c:v libx264 -b:v ${bitrate}k -maxrate ${bitrate}k -bufsize \$((${bitrate}*2))k -g ${keyframe_interval} -pass 1 -f mp4 ${video_file.baseName}_r${bitrate}_g${keyframe_interval}_PASS1.mp4 && \ + ffmpeg -i ${video_file.baseName}_r${bitrate}_g${keyframe_interval}_PASS1.mp4 -c:v libx264 -b:v ${bitrate}k -maxrate ${bitrate}k -bufsize \$((${bitrate}*2))k -g ${keyframe_interval} -pass 2 ${video_file.baseName}_r${bitrate}_g${keyframe_interval}.mp4 && \ + rm ${video_file.baseName}_r${bitrate}_g${keyframe_interval}_PASS1.mp4 ffmpeg2pass-0.log ffmpeg2pass-0.log.mbtree + + """ +} + +/** + * Computes a difference between two videos + * + * @param in_video1 The first input video file + * @param in_video2 The second input video file + * + * @return The output video file with the computed difference + * + * @note Difference videos are useful in comparing changes due to compression. + */ +process COMPUTE_VIDEO_DIFFERENCE { + label "tracking" + + input: + tuple path(in_video1), path(in_video2) + + output: + path "${in_video2.baseName}_diff.mp4" + + script: + """ + ffmpeg -i ${in_video1} -i ${in_video2} -filter_complex '[0:v]setsar=1:1[v1];[v1][1:v]blend=all_mode=difference128' -c:v mpeg4 -q 0 ${in_video2.baseName}_diff.mp4 + """ +} diff --git a/nextflow/modules/pose_qc.nf b/nextflow/modules/pose_qc.nf new file mode 100644 index 0000000..1a480d4 --- /dev/null +++ b/nextflow/modules/pose_qc.nf @@ -0,0 +1,23 @@ +/** + * Render pose on video + * + * @param in_video The input video file + * @param in_pose The input pose file + * + * @return Rendered video + */ +process RENDER_POSE { + label "tracking" + publishDir "compressed/pose/", mode:'copy' + + input: + tuple path(in_video), path(in_pose) + + output: + path "${in_video.baseName}_pose.mp4" + + script: + """ + python3 /kumar_lab_models/mouse-tracking-runtime/render_pose.py --in-vid ${in_video} --in-pose ${in_pose} --out-vid ${in_video.baseName}_pose.mp4 + """ +} diff --git a/nextflow/workflows/multi_mouse_pipeline.nf b/nextflow/workflows/multi_mouse_pipeline.nf index 9c71d7f..d8453fc 100644 --- a/nextflow/workflows/multi_mouse_pipeline.nf +++ b/nextflow/workflows/multi_mouse_pipeline.nf @@ -10,6 +10,7 @@ include { PREDICT_ARENA_CORNERS; include { VIDEO_TO_POSE; PUBLISH_RESULT_FILE as PUBLISH_MM_POSE_V6; } from "${projectDir}/nextflow/modules/utils" + include { COMPRESS_VIDEO_CRF } from "${projectDir}/nextflow/modules/compression" workflow MULTI_MOUSE_TRACKING { take: @@ -33,6 +34,12 @@ workflow MULTI_MOUSE_TRACKING { } PUBLISH_MM_POSE_V6(v_6_poses_renamed) + // Compress the original video + compressed_videos = COMPRESS_VIDEO_CRF(input_video.combine([23]).combine([3000])) + compressed_renamed = compressed_videos.map { video -> + tuple(video, "results/${video.baseName.replace("_g3000_crf23", "")}_compressed.mp4") + } + emit: pose_v6 }