Skip to content

fix: prevent face image stretching distortion#3004

Merged
deepin-bot[bot] merged 1 commit intolinuxdeepin:masterfrom
mhduiy:face
Feb 4, 2026
Merged

fix: prevent face image stretching distortion#3004
deepin-bot[bot] merged 1 commit intolinuxdeepin:masterfrom
mhduiy:face

Conversation

@mhduiy
Copy link
Contributor

@mhduiy mhduiy commented Feb 4, 2026

  1. Added image cropping to square aspect ratio before scaling to prevent stretching
  2. Calculate center crop region based on minimum dimension of source image
  3. Scale cropped image to target size while maintaining aspect ratio
  4. Apply circular mask to the properly proportioned image

Log: Fixed face image display issue where images appeared stretched or distorted

fix: 修复人脸图像拉伸变形问题

  1. 在缩放前添加图像裁剪为正方形比例,防止拉伸变形
  2. 根据源图像最小尺寸计算中心裁剪区域
  3. 将裁剪后的图像按比例缩放到目标尺寸
  4. 对比例正确的图像应用圆形遮罩

Log: 修复了人脸图像显示时出现拉伸或变形的问题

PMS: BUG-304729

Summary by Sourcery

Bug Fixes:

  • Fix distortion of face images in the authentication UI by preserving aspect ratio through centered square cropping and scaling before masking.

1. Added image cropping to square aspect ratio before scaling to prevent
stretching
2. Calculate center crop region based on minimum dimension of source
image
3. Scale cropped image to target size while maintaining aspect ratio
4. Apply circular mask to the properly proportioned image

Log: Fixed face image display issue where images appeared stretched
or distorted

fix: 修复人脸图像拉伸变形问题

1. 在缩放前添加图像裁剪为正方形比例,防止拉伸变形
2. 根据源图像最小尺寸计算中心裁剪区域
3. 将裁剪后的图像按比例缩放到目标尺寸
4. 对比例正确的图像应用圆形遮罩

Log: 修复了人脸图像显示时出现拉伸或变形的问题

PMS: BUG-304729
@sourcery-ai
Copy link

sourcery-ai bot commented Feb 4, 2026

Reviewer's guide (collapsed on small PRs)

Reviewer's Guide

Adds a preprocessing step to crop face images to a centered square before scaling and masking, so the displayed circular avatar is not stretched or distorted.

Sequence diagram for updated face image processing pipeline

sequenceDiagram
    actor User
    participant FaceAuthController
    participant DA_img_source as DA_img
    participant QImage
    participant QPixmap
    participant QPainter

    User->>FaceAuthController: updateFaceImgContent(context, DA_img)
    FaceAuthController->>DA_img_source: Validate img
    DA_img_source-->>FaceAuthController: img data

    FaceAuthController->>QImage: Construct QImage from RGB data
    QImage-->>FaceAuthController: im

    FaceAuthController->>QPixmap: fromImage(im) to create sourcePix
    QPixmap-->>FaceAuthController: sourcePix

    FaceAuthController->>FaceAuthController: Compute sourceSize, offsetX, offsetY
    FaceAuthController->>QPixmap: copy(offsetX, offsetY, sourceSize, sourceSize)
    QPixmap-->>FaceAuthController: square croppedPix
    FaceAuthController->>QPixmap: scaled(Faceimg_SIZE, Faceimg_SIZE, KeepAspectRatio)
    QPixmap-->>FaceAuthController: croppedPix scaled

    FaceAuthController->>QPixmap: Create target pix(Faceimg_SIZE, Faceimg_SIZE)
    FaceAuthController->>QPainter: Begin painting on pix
    FaceAuthController->>QPainter: Set circular clip path
    FaceAuthController->>QPainter: drawPixmap(0, 0, croppedPix)
    QPainter-->>FaceAuthController: Circular masked avatar

    FaceAuthController->>FaceAuthController: Encode pix to base64
    FaceAuthController->>User: Emit faceImgContentChanged (updated avatar)
Loading

File-Level Changes

Change Details Files
Crop source face image to a centered square and scale it before applying the circular mask to avoid distortion.
  • Convert the input QImage to a QPixmap and compute the minimum source dimension to determine a centered square crop region.
  • Copy the centered square region from the source pixmap and scale it to Faceimg_SIZE x Faceimg_SIZE using Qt::KeepAspectRatio with smooth transformation.
  • Use the preprocessed, square-and-scaled pixmap when painting into the circular masked QPixmap instead of drawing directly from the original image.
src/plugin-authentication/operation/faceauthcontroller.cpp

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

Copy link

@sourcery-ai sourcery-ai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey - I've left some high level feedback:

  • Consider deriving sourceSize, offsetX, and offsetY from im.width()/im.height() instead of img->width/img->height so the cropping logic is tied to the actual QImage being used rather than the raw buffer metadata.
  • It may be worth guarding against zero or negative dimensions before creating QPixmap/cropping (e.g., early return if sourceSize <= 0) to avoid undefined behavior when an invalid DA_img is passed in.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- Consider deriving `sourceSize`, `offsetX`, and `offsetY` from `im.width()`/`im.height()` instead of `img->width`/`img->height` so the cropping logic is tied to the actual `QImage` being used rather than the raw buffer metadata.
- It may be worth guarding against zero or negative dimensions before creating `QPixmap`/cropping (e.g., early return if `sourceSize <= 0`) to avoid undefined behavior when an invalid `DA_img` is passed in.

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

@deepin-ci-robot
Copy link

deepin pr auto review

这段代码主要实现了人脸识别图像的裁剪、缩放和圆形遮罩处理的功能。以下是对该代码片段的审查意见,涵盖语法逻辑、代码质量、代码性能和代码安全四个方面:

1. 语法逻辑

  • 潜在逻辑问题:居中裁剪
    代码逻辑 int offsetX = (img->width - sourceSize) / 2; 表明意图是从原图中心裁剪出一个正方形。
    • 审查意见:对于人脸识别,通常需要确保人脸位于图像中心。如果输入的图像 DA_img 中的人脸并没有居中,或者识别算法返回的 img 已经是裁剪过的人脸区域,那么再次进行简单的中心裁剪可能会导致部分人脸被切掉。
    • 改进建议:确认 DA_img 的来源。如果它是原始摄像头帧,通常需要先进行人脸检测获取坐标再裁剪;如果它已经是算法返回的人脸图,直接缩放可能比中心裁剪更安全,除非算法保证返回的矩形框内人脸是居中的且留有足够边距。

2. 代码质量

  • 魔法数字

    • 审查意见QImage::Format_RGB888Faceimg_SIZE 是硬编码的格式和尺寸。
    • 改进建议:确保 Faceimg_SIZE 是在头文件中定义的常量。如果 DA_img 的格式可能变化,建议增加格式检查或转换逻辑,而不是直接假设为 RGB888
  • 代码健壮性

    • 审查意见:代码假设 img->widthimg->height 都大于 0。如果输入图像数据损坏,这可能导致 QImage 构造失败或后续计算错误。
    • 改进建议:在构造 QImage 之前,增加对 img 指针、img->data 以及宽高的有效性检查。

3. 代码性能

  • 深拷贝开销

    • 审查意见QPixmap::fromImage(im) 会进行一次图像数据拷贝。随后的 .copy().scaled() 操作可能会产生额外的临时对象或内存拷贝。
    • 改进建议
      1. 考虑是否可以直接在 QImage 上进行操作(QImage 的像素操作通常比 QPixmap 快,且便于在后台线程处理,尽管此处看起来是在 UI 相关逻辑中)。
      2. 如果必须使用 QPixmap,确保这些操作不是在极高频率的回调中执行,以免造成 UI 卡顿。
  • 平滑变换

    • 审查意见:使用了 Qt::SmoothTransformation 进行缩放。
    • 改进建议:这能提供更好的视觉效果,但计算量较大。如果性能测试发现此处是瓶颈,可以考虑在低性能设备上降级为 Qt::FastTransformation,或者根据图片大小动态决定。

4. 代码安全

  • 空指针解引用风险

    • 审查意见:虽然开头检查了 contextcontroller,但 img->data 的访问依赖于 img 的有效性。
    • 改进建议:增加 if (!img || !img->data) return; 的检查。
  • 资源泄漏风险

    • 审查意见QBuffer buffer 是栈对象,自动管理生命周期,这部分是安全的。但 QImage im 构造时使用了外部指针 (uchar *)img->data
    • 改进建议:确保 im 的生命周期不会超过 img->data 的生命周期。由于 im 是局部变量,在函数结束时销毁,这通常是安全的。但需注意 QImage 构造时默认不拷贝数据(除非显式调用 copy()),这里只是引用了 img->data。如果在 im 存活期间 img->data 被释放,会导致崩溃。目前的局部变量写法是安全的。

综合改进代码示例

void FaceAuthController::updateFaceImgContent(void* const context, const DA_img* img)
{
    // 1. 安全性检查:增加对 img 和 data 的检查
    if (!context || !img || !img->data) {
        qWarning() << "Invalid context or image data";
        return;
    }

    // 获取 controller
    auto controller = static_cast<FaceAuthController*>(context);
    
    // 2. 健壮性:检查图像尺寸
    if (img->width <= 0 || img->height <= 0) {
        qWarning() << "Invalid image dimensions";
        return;
    }

    // 假设输入必须是 RGB888,如果不是则需要转换,这里保持原逻辑但增加注释说明假设条件
    QImage im((uchar *)img->data, img->width, img->height, QImage::Format_RGB888);
    if (im.isNull()) {
        qWarning() << "Failed to construct QImage from data";
        return;
    }

    // 3. 性能与逻辑优化:直接使用 QImage 进行操作可能比 QPixmap 稍快,且便于处理像素
    // 先进行裁剪
    int sourceSize = qMin(img->width, img->height);
    int offsetX = (img->width - sourceSize) / 2;
    int offsetY = (img->height - sourceSize) / 2;
    
    // copy() 会进行深拷贝,确保数据独立
    QImage croppedImage = im.copy(offsetX, offsetY, sourceSize, sourceSize);
    
    // 缩放至目标尺寸
    QImage scaledImage = croppedImage.scaled(Faceimg_SIZE, Faceimg_SIZE, 
                                            Qt::IgnoreAspectRatio, // 因为已经裁剪为正方形,这里可以直接 IgnoreAspectRatio
                                            Qt::SmoothTransformation);

    // 4. 绘制圆形遮罩
    QPixmap pix(Faceimg_SIZE, Faceimg_SIZE);
    pix.fill(Qt::transparent);
    
    {
        QPainter painter(&pix);
        painter.setRenderHint(QPainter::Antialiasing); // 增加抗锯齿提示,提升边缘质量
        QPainterPath path;
        path.addEllipse(0, 0, Faceimg_SIZE, Faceimg_SIZE);
        painter.setClipPath(path);
        // 这里将 QImage 转换为 QPixmap 绘制,或者直接 drawImage
        painter.drawPixmap(0, 0, QPixmap::fromImage(scaledImage));
    }

    QBuffer buffer;
    buffer.open(QIODevice::WriteOnly);
    // 保存为 PNG 格式通常比 JPG 更适合这种带透明度的图形,且质量无损
    pix.save(&buffer, "PNG"); 
    
    controller->m_faceImgContent = "data:image/png;base64," + buffer.data().toBase64();
    emit controller->faceImgContentChanged();
}

总结

这段代码的主要改进点在于增加了输入参数的空指针和尺寸检查,提升了程序的健壮性安全性。同时,通过明确 QImage 的拷贝行为,避免了潜在的悬空指针风险。在性能方面,虽然 QPixmap 在 GUI 线程绘制有其优势,但在纯图像处理步骤使用 QImage 通常更灵活且易于维护。最后,增加了抗锯齿提示,提升了最终生成头像的视觉质量。

@deepin-ci-robot
Copy link

[APPROVALNOTIFIER] This PR is NOT APPROVED

This pull-request has been approved by: mhduiy, robertkill

The full list of commands accepted by this bot can be found here.

Details Needs approval from an approver in each of these files:

Approvers can indicate their approval by writing /approve in a comment
Approvers can cancel approval by writing /approve cancel in a comment

@mhduiy
Copy link
Contributor Author

mhduiy commented Feb 4, 2026

/forcemerge

@deepin-bot
Copy link

deepin-bot bot commented Feb 4, 2026

This pr force merged! (status: blocked)

@deepin-bot deepin-bot bot merged commit 088ef30 into linuxdeepin:master Feb 4, 2026
16 of 18 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants