<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Projects | Dat Viet Thanh Nguyen</title><link>https://datvtn.github.io/project/</link><atom:link href="https://datvtn.github.io/project/index.xml" rel="self" type="application/rss+xml"/><description>Projects</description><generator>Hugo Blox Builder (https://hugoblox.com)</generator><language>en-us</language><lastBuildDate>Sun, 19 May 2024 00:00:00 +0000</lastBuildDate><image><url>https://datvtn.github.io/media/icon_hu82c06b08a282889123d30d9b11b05ced_13465_512x512_fill_lanczos_center_3.png</url><title>Projects</title><link>https://datvtn.github.io/project/</link></image><item><title>Personality-Driven AI Avatar Bot</title><link>https://datvtn.github.io/project/personality-driven-ai-bot/</link><pubDate>Wed, 01 Jan 2025 00:00:00 +0000</pubDate><guid>https://datvtn.github.io/project/personality-driven-ai-bot/</guid><description>&lt;p>&lt;strong>Dec 2023 – Jan 2025&lt;/strong>&lt;/p>
&lt;h2 id="problem">Problem&lt;/h2>
&lt;p>Group chat applications are text-first. Adding an AI participant that feels like a &lt;em>character&lt;/em> rather than a chat completion means giving it a face, a voice, and a consistent personality — and generating all of that fast enough that it can take part in a live conversation rather than replying minutes later.&lt;/p>
&lt;h2 id="approach">Approach&lt;/h2>
&lt;p>I built the avatar generation side of the product: turning a character definition into a speaking AI avatar that could be dropped into a group chat.&lt;/p>
&lt;p>The pipeline combines several pieces I had been building in parallel:&lt;/p>
&lt;ul>
&lt;li>&lt;strong>Identity generation&lt;/strong> — a consistent visual identity per character, stable across every clip the bot produces.&lt;/li>
&lt;li>&lt;strong>Audio-driven talking head&lt;/strong> — driving lip-sync and head motion from a single audio stream while preserving the character&amp;rsquo;s identity, head pose, and facial expression.&lt;/li>
&lt;li>&lt;strong>Expression control&lt;/strong> — routing the character&amp;rsquo;s intended emotion (joy, sadness, anger) into the generated video, so the avatar&amp;rsquo;s delivery matches the tone of what it is saying.&lt;/li>
&lt;li>&lt;strong>Real-time diffusion video&lt;/strong> — a Stable Diffusion video generation pipeline producing avatar speech video directly from audio input.&lt;/li>
&lt;/ul>
&lt;p>The engineering constraint throughout was latency: a conversational avatar that lags is worse than no avatar, so each stage was optimised for real-time or near-real-time throughput rather than best-possible offline quality.&lt;/p>
&lt;h2 id="tech-stack">Tech stack&lt;/h2>
&lt;p>&lt;code>PyTorch&lt;/code> · &lt;code>Stable Diffusion&lt;/code> · &lt;code>TensorRT&lt;/code> · &lt;code>Python&lt;/code> · &lt;code>Docker&lt;/code>&lt;/p>
&lt;h2 id="outcome">Outcome&lt;/h2>
&lt;p>Shipped as the avatar generation component of a group chat application.&lt;/p>
&lt;!-- TODO: add concrete numbers here if they can be shared — e.g. end-to-end latency per utterance, resolution/FPS of generated video, number of supported characters. --></description></item><item><title>AI Photo Editing App</title><link>https://datvtn.github.io/project/ai-photo-editing/</link><pubDate>Mon, 01 Jan 2024 00:00:00 +0000</pubDate><guid>https://datvtn.github.io/project/ai-photo-editing/</guid><description>&lt;p>&lt;strong>Sep 2023 – Jan 2024&lt;/strong>&lt;/p>
&lt;h2 id="problem">Problem&lt;/h2>
&lt;p>The edits people actually want from a photo app — &lt;em>remove that person&lt;/em>, &lt;em>drop the background&lt;/em>, &lt;em>make the sky dramatic&lt;/em>, &lt;em>put him in a suit&lt;/em> — are each a different technical problem, but users expect all of them behind a single tap. Each feature needs an accurate mask, a plausible fill, and a result that survives being zoomed into.&lt;/p>
&lt;h2 id="approach">Approach&lt;/h2>
&lt;p>I built four editing features on a shared backbone of segmentation plus generative inpainting:&lt;/p>
&lt;ul>
&lt;li>&lt;strong>Object removal&lt;/strong> — segment the unwanted region, then inpaint it with surroundings-consistent content rather than a blur or clone-stamp patch.&lt;/li>
&lt;li>&lt;strong>Background removal&lt;/strong> — high-quality alpha matting around hard cases: hair, glasses, motion blur at the subject boundary.&lt;/li>
&lt;li>&lt;strong>Sky replacement&lt;/strong> — sky segmentation combined with colour harmonisation, so the relit foreground matches the new sky instead of looking pasted on.&lt;/li>
&lt;li>&lt;strong>Suit changer&lt;/strong> — garment-region segmentation and generative synthesis that keeps the subject&amp;rsquo;s identity and pose intact.&lt;/li>
&lt;/ul>
&lt;p>The common thread is that mask quality dominates perceived output quality: a strong generative model behind a sloppy mask still looks wrong at the edges, so most of the tuning effort went into the segmentation stage and its boundary refinement.&lt;/p>
&lt;h2 id="tech-stack">Tech stack&lt;/h2>
&lt;p>&lt;code>PyTorch&lt;/code> · &lt;code>Diffusion Models&lt;/code> · &lt;code>Segmentation networks&lt;/code> · &lt;code>OpenCV&lt;/code> · &lt;code>Python&lt;/code>&lt;/p>
&lt;h2 id="outcome">Outcome&lt;/h2>
&lt;p>Delivered as a set of consumer-facing editing features.&lt;/p>
&lt;!-- TODO: add before/after examples and any quality or runtime numbers that can be shared publicly. --></description></item><item><title>Real-Time Portrait Segmentation on Mobile</title><link>https://datvtn.github.io/project/portrait-segmentation/</link><pubDate>Wed, 01 Dec 2021 00:00:00 +0000</pubDate><guid>https://datvtn.github.io/project/portrait-segmentation/</guid><description>&lt;p>&lt;strong>Aug 2021 – Dec 2021&lt;/strong>&lt;/p>
&lt;h2 id="problem">Problem&lt;/h2>
&lt;p>Portrait effects — background blur, replacement, relighting — need a person mask computed every frame, on a phone, inside the camera&amp;rsquo;s frame budget, without draining the battery. A model that is accurate but too slow is useless here, and so is a fast model that flickers between frames or cuts through someone&amp;rsquo;s hair.&lt;/p>
&lt;h2 id="approach">Approach&lt;/h2>
&lt;p>Two halves, and the data half mattered as much as the model:&lt;/p>
&lt;p>&lt;strong>The network.&lt;/strong> A deliberately small segmentation architecture designed against the mobile inference budget rather than trimmed down afterwards. Design decisions were driven by what the target runtime could actually execute efficiently, not just by parameter count — an operation that is cheap on paper is not cheap if the on-device runtime falls back to CPU for it.&lt;/p>
&lt;p>&lt;strong>The data pipeline.&lt;/strong> I built the full pipeline: collection, annotation, quality control, and augmentation. Boundary quality is where portrait segmentation visibly succeeds or fails, so the annotation standard around hair and edges drove most of the final result.&lt;/p>
&lt;p>The model was quantized and deployed through the mobile inference stack used for VinSmart devices, alongside the other lightweight face analysis networks (FaceID, Masked FaceID, camera effects) our team shipped.&lt;/p>
&lt;h2 id="tech-stack">Tech stack&lt;/h2>
&lt;p>&lt;code>PyTorch&lt;/code> · &lt;code>TFLite&lt;/code> · &lt;code>SNPE&lt;/code> · &lt;code>NCNN&lt;/code> · &lt;code>INT8 quantization&lt;/code> · &lt;code>Python&lt;/code> · &lt;code>C++&lt;/code>&lt;/p>
&lt;h2 id="outcome">Outcome&lt;/h2>
&lt;p>A portrait segmentation network running in real time on target mobile hardware.&lt;/p>
&lt;!-- TODO: add the model size, on-device latency, and IoU if these can be shared publicly. --></description></item><item><title>Industrial Surface Defect Detection</title><link>https://datvtn.github.io/project/surface-defect-detection/</link><pubDate>Thu, 01 Apr 2021 00:00:00 +0000</pubDate><guid>https://datvtn.github.io/project/surface-defect-detection/</guid><description>&lt;p>&lt;strong>Jan 2021 – Apr 2021&lt;/strong>&lt;/p>
&lt;h2 id="problem">Problem&lt;/h2>
&lt;p>Industrial defect inspection is an unbalanced detection problem with an unforgiving cost function: defects are rare, visually subtle, and vary in severity, while a false negative ships a bad part and a false positive stops a line. The output the customer needs is not just &lt;em>defect / no defect&lt;/em> but a &lt;strong>grade&lt;/strong> — how severe, and therefore what to do about it.&lt;/p>
&lt;h2 id="approach">Approach&lt;/h2>
&lt;p>I built the detection and grading models, and — the part that shaped the project most — the integration path.&lt;/p>
&lt;p>The customer&amp;rsquo;s existing inspection software was a .NET application, not a Python stack. Rather than standing up a separate inference service and forcing a network hop into a line-rate process, the model was packaged as a &lt;strong>.NET-consumable DLL&lt;/strong> so it could be called directly from the existing application. That removed a whole class of latency and deployment failure modes, at the cost of doing the native packaging and memory-management work properly.&lt;/p>
&lt;p>On the modelling side, the emphasis was on the severity grading step: getting a reliable ordering of defect severity out of a detector matters more to the operator than squeezing the last point of detection mAP.&lt;/p>
&lt;h2 id="tech-stack">Tech stack&lt;/h2>
&lt;p>&lt;code>PyTorch&lt;/code> · &lt;code>LibTorch&lt;/code> · &lt;code>C++&lt;/code> · &lt;code>.NET DLL integration&lt;/code> · &lt;code>OpenCV&lt;/code>&lt;/p>
&lt;h2 id="outcome">Outcome&lt;/h2>
&lt;p>Delivered as an integrated component of the customer&amp;rsquo;s existing inspection application.&lt;/p>
&lt;!-- TODO: add defect classes covered, grading accuracy, and throughput per part if these can be shared publicly. --></description></item><item><title>Autopilot Perception on Jetson TX2</title><link>https://datvtn.github.io/project/autopilot-perception/</link><pubDate>Fri, 01 Jan 2021 00:00:00 +0000</pubDate><guid>https://datvtn.github.io/project/autopilot-perception/</guid><description>&lt;p>&lt;strong>Jan 2020 – Jan 2021&lt;/strong>&lt;/p>
&lt;h2 id="problem">Problem&lt;/h2>
&lt;p>An autonomous vehicle has to see and react inside a control loop that does not wait. On a Jetson TX2, the whole perception stack — not just one model — has to fit in a few watts and a few tens of milliseconds, while staying reliable under changing light, weather, and road surfaces.&lt;/p>
&lt;h2 id="approach">Approach&lt;/h2>
&lt;p>I worked on the perception side of the vehicle: the models that turn camera frames into the lane, sign, and obstacle information the control stack acts on.&lt;/p>
&lt;p>The interesting constraint was that accuracy and latency traded against each other continuously. Time spent on a heavier detector is time the controller does not have to react, so the work was as much about &lt;strong>inference engineering&lt;/strong> — optimising the graph, choosing precisions, keeping the pipeline saturated — as about model architecture. The stack was built against TensorRT to get the most out of the TX2&amp;rsquo;s GPU.&lt;/p>
&lt;p>This was developed for the FPT Digital Race, where the system had to run a real course rather than a held-out test set — which surfaces a different class of failure than offline evaluation does.&lt;/p>
&lt;h2 id="tech-stack">Tech stack&lt;/h2>
&lt;p>&lt;code>PyTorch&lt;/code> · &lt;code>TensorRT&lt;/code> · &lt;code>NVIDIA Jetson TX2&lt;/code> · &lt;code>OpenCV&lt;/code> · &lt;code>Python&lt;/code> · &lt;code>C++&lt;/code>&lt;/p>
&lt;h2 id="outcome">Outcome&lt;/h2>
&lt;p>&lt;strong>Top 10 — FPT Digital Race 2020 Final.&lt;/strong>&lt;/p>
&lt;!-- TODO: add the perception pipeline's end-to-end FPS on TX2 and which specific tasks the stack covered. --></description></item></channel></rss>