1.基于Android 10 分析系统通知管理服务(NMS)

基于Android 10 分析系统通知管理服务(NMS)

相关源码文件如下:

frameworks/base/core/java/android/app/
	- NotificationManager.java
	- Notification.java
	- NotificationChannel.java
	
frameworks/base/core/java/android/service/notification/
	- NotificationListenerService.java

frameworks/base/services/core/java/com/android/server/notification/
	- NotificationManagerService.java
	
frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/
	- NotificationListener.java
	
frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/notification/
	- NotificationEntryManager.java

一. 概述

Android应用除了WMS和AMS的管理服务之外,还有通知管理服务(NMS)也是非常重要的,通知是应用界面之外向用户显示的界面。

1.1 通知使用示例

1.1.1 创建Channel

val nm: NotificationManager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
    val channel =
        NotificationChannel("123", "TestChannel", NotificationManager.IMPORTANCE_HIGH)
    nm.createNotificationChannel(channel)
}

1.1.2 创建通知

val notification: Notification = NotificationCompat.Builder(this, "123")
    .setContentText("ContentText")
    .setTicker("Ticker")
    .setGroupSummary(true)
    .setContentTitle("Hello")
    .setContentInfo("ContentInfo")
    .setSmallIcon(R.mipmap.ic_launcher)
    .build()

1.1.3 发送通知

val nm: NotificationManager =
            getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
nm.notify(notifyId, notification)

1.1.4 取消通知

val nm: NotificationManager =
            getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
nm.cancel(notifyId) // 取消指定通知
nm.cancelAll(); // 取消所有通知

1.2 架构图

1.2.1 核心类图

1.2.2 通知处理流程图

alt 通知处理流程图

二. 通知发送原理分析

由于分析是基于调用栈分析,所以建议读者按章节顺序阅读。

2.1 NM.notify

源码文件: /frameworks/base/core/java/android/app/NotificationManager.java

运行在三方App进程中

public void notify(int id, Notification notification)
{
    notify(null, id, notification);
}
public void notify(String tag, int id, Notification notification)
{
    notifyAsUser(tag, id, notification, mContext.getUser());
}
public void notifyAsUser(String tag, int id, Notification notification, UserHandle user)
{
    // 获取通知的代理对象
    INotificationManager service = getService();
    String pkg = mContext.getPackageName();
    try {
        if (localLOGV) Log.v(TAG, pkg + ": notify(" + id + ", " + notification + ")");
        // 通过Binder调用NMS的enqueueNotificationWithTag方法 [2.2]
        service.enqueueNotificationWithTag(pkg, mContext.getOpPackageName(), tag, id,
                fixNotification(notification), user.getIdentifier());
    } catch (RemoteException e) {
        throw e.rethrowFromSystemServer();
    }
}
private Notification fixNotification(Notification notification) {
    String pkg = mContext.getPackageName();
    // 将ApplicationInfo信息添加到Notification中的extras字段中
    Notification.addFieldsFromContext(mContext, notification);
    ...
    fixLegacySmallIcon(notification, pkg);
    // 对于Android API 22以上版本需要添加SmallIcon,否则将抛出异常
    if (mContext.getApplicationInfo().targetSdkVersion > Build.VERSION_CODES.LOLLIPOP_MR1) {
        if (notification.getSmallIcon() == null) {
            throw new IllegalArgumentException("Invalid notification (no valid small icon): "
                    + notification);
        }
    }
    notification.reduceImageSizes(mContext);
    ActivityManager am = (ActivityManager) mContext.getSystemService(Context.ACTIVITY_SERVICE);
    boolean isLowRam = am.isLowRamDevice();
    return Builder.maybeCloneStrippedForDelivery(notification, isLowRam, mContext);
}

在App进程中调用NotificationManager类的notify()方法,最终会通过Binder调用system_server进程中的NotifcationManagerService(简称NMS),执行enqueueNotificationWithTag方法。

2.2 NMS.enqueueNotificationWithTag

源码文件:/frameworks/base/services/core/java/com/android/server/notification/NotificationManagerService.java

运行在system_server进程中

final IBinder mService = new INotificationManager.Stub() {
    @Override
    public void enqueueNotificationWithTag(String pkg, String opPkg, String tag, int id, Notification notification, int userId) throws RemoteException {
        enqueueNotificationInternal(pkg, opPkg, Binder.getCallingUid(),
                Binder.getCallingPid(), tag, id, notification, userId);
    }
}

void enqueueNotificationInternal(final String pkg, final String opPkg, final int callingUid,
            final int callingPid, final String tag, final int id, final Notification notification,
            int incomingUserId) {
    ...

    final int userId = ActivityManager.handleIncomingUser(callingPid,
            callingUid, incomingUserId, true, false, "enqueueNotification", pkg);
    final UserHandle user = UserHandle.of(userId);

    // 如果调用 uid 没有发布权限,则可以抛出 SecurityException
    final int notificationUid = resolveNotificationUid(opPkg, pkg, callingUid, userId);
    ...
    try {
        // 修复通知信息
        fixNotification(notification, pkg, userId);
    } catch (NameNotFoundException e) {
        Slog.e(TAG, "Cannot create a context for sending app", e);
        return;
    }
    ...
    String channelId = notification.getChannelId();
    final NotificationChannel channel = mPreferencesHelper.getNotificationChannel(pkg,
            notificationUid, channelId, false /* includeDeleted */);
    // 如果Channel不存在,则提示错误。在发送通知之前需要创建Channel
    if (channel == null) {
        ...
        boolean appNotificationsOff = mPreferencesHelper.getImportance(pkg, notificationUid)
                == NotificationManager.IMPORTANCE_NONE;
        if (!appNotificationsOff) {
            doChannelWarningToast("Developer warning for package \"" + pkg + "\"\n" +
                    "Failed to post notification on channel \"" + channelId + "\"\n" +
                    "See log for more details");
        }
        return;
    }
	// 将通知信息和package信息封装到StatusBarNotification对象中
    // StatusBarNotification构造函数中会初始化key和groupKey属性
    final StatusBarNotification n = new StatusBarNotification(
            pkg, opPkg, id, tag, notificationUid, callingPid, notification,
            user, null, System.currentTimeMillis());
    // 将Channel和StatusBarNotification信息封装到NotificationRecord对象中
    final NotificationRecord r = new NotificationRecord(getContext(), n, channel);
    r.setIsAppImportanceLocked(mPreferencesHelper.getIsAppImportanceLocked(pkg, callingUid));

    if ((notification.flags & Notification.FLAG_FOREGROUND_SERVICE) != 0) {
       ... // 省略处理前台服务逻辑
    }
	// 检查是否可以发布通知。 检查速率限制器、贪睡助手和阻塞。
    if (!checkDisqualifyingFeatures(userId, notificationUid, id, tag, r,
            r.sbn.getOverrideGroupKey() != null)) {
        return;
    }
   	...
    // 通过WorkHandler提交给Runnable处理。 [见2.3]
    mHandler.post(new EnqueueNotificationRunnable(userId, r));
}

这个过程主要是创建NotificationRecord对象保存Notification信息,最后通过Handler提交给EnqueueNotificationRunnable处理。

  • key规则是“UserHandle#getIdentifier()|pkg|notifyId|tag|uid”
  • mHandler是WorkerHandler的实例对象,在NMS启动后初始化。

2.3 EnqueueNotificationRunnable

源码文件:/frameworks/base/services/core/java/com/android/server/notification/NotificationManagerService.java

运行在system_server进程中

protected class EnqueueNotificationRunnable implements Runnable {
    private final NotificationRecord r;
    private final int userId;
    EnqueueNotificationRunnable(int userId, NotificationRecord r) {
        this.userId = userId;
        this.r = r;
    };
    @Override
    public void run() {
        synchronized (mNotificationLock) {
            // 放入列表中
            mEnqueuedNotifications.add(r);
            scheduleTimeoutLocked(r);
            final StatusBarNotification n = r.sbn;
            ...
            // 处理气泡
            flagNotificationForBubbles(r, pkg, callingUid, old);
            // 处理分组通知
            handleGroupedNotificationLocked(r, old, callingUid, callingPid);
            ...
            // 将通知告诉助理服务
            if (mAssistants.isEnabled()) {
                mAssistants.onNotificationEnqueuedLocked(r);
                // [2.4]
                mHandler.postDelayed(new PostNotificationRunnable(r.getKey()),
                        DELAY_FOR_ASSISTANT_TIME);
            } else {
                // [2.4]
                mHandler.post(new PostNotificationRunnable(r.getKey()));
            }
        }
        ...
    }
}
  • 首先将NotificationRecord放入队列中待处理。
  • 处理气泡通知和分组通知
  • 最后通过Handler提交给PostNotificationRunnable处理,并且传递了key值,而非NotificationRecord对象。

2.4 PostNotificationRunnable

源码文件:/frameworks/base/services/core/java/com/android/server/notification/NotificationManagerService.java

运行在system_server进程中

protected class PostNotificationRunnable implements Runnable {
    private final String key;
    PostNotificationRunnable(String key) {
        this.key = key;
    }
    @Override
    public void run() {
        synchronized (mNotificationLock) {
            try {
            	// 通过Key值找出NotificationRecord对象
                NotificationRecord r = null;
                int N = mEnqueuedNotifications.size();
                for (int i = 0; i < N; i++) {
                    final NotificationRecord enqueued = mEnqueuedNotifications.get(i);
                    if (Objects.equals(key, enqueued.getKey())) {
                        r = enqueued;
                        break;
                    }
                }
                // 未找到情况下则提示异常,且丢弃这次任务
                if (r == null) {
                    Slog.i(TAG, "Cannot find enqueued record for key: " + key);
                    return;
                }
				// 判断是否被阻止通知,主要通过优先级和频道分组通知判断
                if (isBlocked(r)) {
                    Slog.i(TAG, "notification blocked by assistant request");
                    return;
                }
				...
                // 相同Key的上一次的通知信息
                NotificationRecord old = mNotificationsByKey.get(key);
                final StatusBarNotification n = r.sbn;
                final Notification notification = n.getNotification();
                // 从通知列表查询是否存在该通知
                int index = indexOfNotificationLocked(n.getKey());
                if (index < 0) {
                    mNotificationList.add(r);
                    mUsageStats.registerPostedByApp(r);
                    r.setInterruptive(isVisuallyInterruptive(null, r));
                } else {
                    old = mNotificationList.get(index);
                    mNotificationList.set(index, r);
                    mUsageStats.registerUpdatedByApp(r, old);
                    // 确保不会丢失前台服务状态。
                    notification.flags |=
                            old.getNotification().flags & FLAG_FOREGROUND_SERVICE;
                    r.isUpdate = true;
                    r.setTextChanged(isVisuallyInterruptive(old, r));
                }
				// 将NotificationRecord存入Map中
                mNotificationsByKey.put(n.getKey(), r);
                // 如果是前台服务的通知,则添加不允许被清除和正在运行的标签
                if ((notification.flags & FLAG_FOREGROUND_SERVICE) != 0) {
                    notification.flags |= FLAG_ONGOING_EVENT
                            | FLAG_NO_CLEAR;
                }
                mRankingHelper.extractSignals(r);
                mRankingHelper.sort(mNotificationList);
                if (!r.isHidden()) {
                    buzzBeepBlinkLocked(r);
                }
                if (notification.getSmallIcon() != null) {
                    // 通知观察者
                    StatusBarNotification oldSbn = (old != null) ? old.sbn : null;
                    mListeners.notifyPostedLocked(r, old); // 见[2.5]
                    if ((oldSbn == null || !Objects.equals(oldSbn.getGroup(), n.getGroup()))
                            && !isCritical(r)) {
                        mHandler.post(new Runnable() {
                            @Override
                            public void run() {
                                mGroupHelper.onNotificationPosted(
                                        n, hasAutoGroupSummaryLocked(n));
                            }
                        });
                    }
                } else {
                    // 不发布没有小图标的通知
                    if (old != null && !old.isCanceled) {
                        mListeners.notifyRemovedLocked(r,
                                NotificationListenerService.REASON_ERROR, r.getStats());
                        mHandler.post(new Runnable() {
                            @Override
                            public void run() {
                                mGroupHelper.onNotificationRemoved(n);
                            }
                        });
                    }
                }

                maybeRecordInterruptionLocked(r);
            } finally {
                // 最后在列表中移除
                int N = mEnqueuedNotifications.size();
                for (int i = 0; i < N; i++) {
                    final NotificationRecord enqueued = mEnqueuedNotifications.get(i);
                    if (Objects.equals(key, enqueued.getKey())) {
                        mEnqueuedNotifications.remove(i);
                        break;
                    }
                }
            }
        }
    }
}
  • 通过mEnqueuedNotifications 列表找出本次通知信息NotificationRecord。
  • 通过mNotificationsByKey列表找出上一个通知信息。
  • 通过mNotificationList列表找出当前key的通知信息,且确保前台服务状态不丢失。
  • 前台服务单独添加两个Flag (FLAG_ONGOING_EVENT | FLAG_NO_CLEAR) 。
  • 没有小图标的通知不显示
  • 最后通知观察者
  • 此mListeners为NotificationListeners对象,是NMS内部类并且在NMS初始化创建。

2.5 NotificationListeners.notifyPostedLocked

源码文件:/frameworks/base/services/core/java/com/android/server/notification/NotificationManagerService.java

运行在system_server进程中

public class NotificationListeners extends ManagedServices {
 	// ...
    @GuardedBy("mNotificationLock")
    public void notifyPostedLocked(NotificationRecord r, NotificationRecord old) {
        notifyPostedLocked(r, old, true);
    }
    @GuardedBy("mNotificationLock")
    private void notifyPostedLocked(NotificationRecord r, NotificationRecord old,
            boolean notifyAllListeners) {
        // Lazily initialized snapshots of the notification.
        StatusBarNotification sbn = r.sbn;
        StatusBarNotification oldSbn = (old != null) ? old.sbn : null;
        TrimCache trimCache = new TrimCache(sbn);
		// 遍历整个ManagedServices中的所有ManagedServiceInfo,发送给所有观察者,主要观察者是SystemUI
        for (final ManagedServiceInfo info : getServices()) {
            boolean sbnVisible = isVisibleToListener(sbn, info);
            boolean oldSbnVisible = oldSbn != null ? isVisibleToListener(oldSbn, info) : false;
            // 通过监听者判断是否需要显示该通知
            if (!oldSbnVisible && !sbnVisible) {
                continue;
            }
            // 通知被隐藏,则忽略
            if (r.isHidden() && info.targetSdkVersion < Build.VERSION_CODES.P) {
                continue;
            }
            if (!notifyAllListeners && info.targetSdkVersion >= Build.VERSION_CODES.P) {
                continue;
            }
            final NotificationRankingUpdate update = makeRankingUpdateLocked(info);
            // 通知变得不可见之后移除老的通知
            if (oldSbnVisible && !sbnVisible) {
                final StatusBarNotification oldSbnLightClone = oldSbn.cloneLight();
                mHandler.post(new Runnable() {
                    @Override
                    public void run() {
                        notifyRemoved(
                                info, oldSbnLightClone, update, null, REASON_USER_STOPPED);
                    }
                });
                continue;
            }
            // 在通知侦听器之前授予访问权限
            final int targetUserId = (info.userid == UserHandle.USER_ALL)
                    ? UserHandle.USER_SYSTEM : info.userid;
            updateUriPermissions(r, old, info.component.getPackageName(), targetUserId);

            final StatusBarNotification sbnToPost = trimCache.ForListener(info);
            mHandler.post(new Runnable() {
                @Override
                public void run() {
                    notifyPosted(info, sbnToPost, update);
                }
            });
        }
    }
    // 发出通知
    private void notifyPosted(final ManagedServiceInfo info,
                              final StatusBarNotification sbn, NotificationRankingUpdate rankingUpdate) {
        final INotificationListener listener = (INotificationListener) info.service;
        // 包装StatusBarNotification
        StatusBarNotificationHolder sbnHolder = new StatusBarNotificationHolder(sbn);
        try {
            listener.onNotificationPosted(sbnHolder, rankingUpdate); // [3.1]
        } catch (RemoteException ex) {
            Slog.e(TAG, "unable to notify listener (posted): " + listener, ex);
        }
    }
	// ...
}
  • 此处的listener来自与ManagedServiceInfo的service成员变量。
  • listener数据类型是NotificationListenerWrapper的代理对象
  • sbnHolder的数据类型是StatusBarNotificationHolder,继承于IStatusBarNotificationHolder.Stub对象。

三. SysetmUI

3.1 NotificationListenerWrapper.onNotificationPosted

源码文件:/frameworks/base/core/java/android/service/notification/NotificationListenerService.java

运行在com.android.systemui进程中

protected class NotificationListenerWrapper extends INotificationListener.Stub {
    @Override
    public void onNotificationPosted(IStatusBarNotificationHolder sbnHolder,
            NotificationRankingUpdate update) {
        StatusBarNotification sbn;
        try {
            sbn = sbnHolder.get(); // 通过Binder向 system_server 读取sbn对象。
        } catch (RemoteException e) {
            Log.w(TAG, "onNotificationPosted: Error receiving StatusBarNotification", e);
            return;
        }
        // ...
        synchronized (mLock) {
            applyUpdateLocked(update);
            if (sbn != null) {
                SomeArgs args = SomeArgs.obtain();
                args.arg1 = sbn;
                args.arg2 = mRankingMap;
                mHandler.obtainMessage(MyHandler.MSG_ON_NOTIFICATION_POSTED,
                        args).sendToTarget(); // [2.7]
            } else {
                // still pass along the ranking map, it may contain other information
                mHandler.obtainMessage(MyHandler.MSG_ON_NOTIFICATION_RANKING_UPDATE,
                        mRankingMap).sendToTarget();
            }
        }
    }
}
  • 通过Binder向 system_server 读取sbn对象。
  • 提交给Handler处理,此Handler运行在systemui进程的主线程中。

3.2 MyHandler

源码路径: /frameworks/base/core/java/android/service/notification/NotificationListenerService.java

运行在com.android.systemui进程中

private final class MyHandler extends Handler {
    public static final int MSG_ON_NOTIFICATION_POSTED = 1;
    public MyHandler(Looper looper) {
        super(looper, null, false);
    }
    @Override
    public void handleMessage(Message msg) {
        if (!isConnected) {
            return;
        }
        switch (msg.what) {
            case MSG_ON_NOTIFICATION_POSTED: {
                SomeArgs args = (SomeArgs) msg.obj;
                StatusBarNotification sbn = (StatusBarNotification) args.arg1;
                RankingMap rankingMap = (RankingMap) args.arg2;
                args.recycle();
                onNotificationPosted(sbn, rankingMap); // [3.3]
            } break;
            // ...
        }
    }
}
  • 最后调用NotificationListenerService实例对象的onNotificationPosted()
  • MyHandler为NotificationListenerService内部类

3.3 NotificationListener.onNotificationPosted

源码文件:/frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/NotificationListener.java

运行在com.android.systemui进程中

public class NotificationListener extends NotificationListenerWithPlugins {
    private static final String TAG = "NotificationListener";

    // Dependencies:
    private final NotificationRemoteInputManager mRemoteInputManager =
            Dependency.get(NotificationRemoteInputManager.class);
    private final NotificationEntryManager mEntryManager =
            Dependency.get(NotificationEntryManager.class);
    private final NotificationGroupManager mGroupManager =
            Dependency.get(NotificationGroupManager.class);

    private final ArrayList<NotificationSettingsListener> mSettingsListeners = new ArrayList<>();
    private final Context mContext;

	// ...

    @Override
    public void onNotificationPosted(final StatusBarNotification sbn,
            final RankingMap rankingMap) {
        // ...
        if (sbn != null && !onPluginNotificationPosted(sbn, rankingMap)) {
            Dependency.get(Dependency.MAIN_HANDLER).post(() -> {
                // 处理远端输入(手表)
                processForRemoteInput(sbn.getNotification(), mContext);
                String key = sbn.getKey();
                // 判断是否已经存在当前key,存在则更新,否则新增
                boolean isUpdate =
                        mEntryManager.getNotificationData().get(key) != null;
                if (!ENABLE_CHILD_NOTIFICATIONS
                        && mGroupManager.isChildInGroupWithSummary(sbn)) {
                    // 删除现有通知,以避免陈旧数据
                    if (isUpdate) {
                        mEntryManager.removeNotification(key, rankingMap, UNDEFINED_DISMISS_REASON);
                    } else {
                        mEntryManager.getNotificationData()
                                .updateRanking(rankingMap);
                    }
                    return;
                }
                if (isUpdate) {
                    // 更新通知
                    mEntryManager.updateNotification(sbn, rankingMap);
                } else {
                    // 新增通知
                    mEntryManager.addNotification(sbn, rankingMap); // [3.4]
                }
            });
        }
    }
}
  • 最终通过 isUpdate 来区分是更新还是新增通知。

3.4 NotificationEntryManager.addNotification

源码文件:/frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java

运行在com.android.systemui进程中

public class NotificationEntryManager implements
        Dumpable,
        NotificationContentInflater.InflationCallback,
        NotificationUpdateHandler,
        VisualStabilityManager.Callback {
    // ...

    private void addNotificationInternal(StatusBarNotification notification,
            NotificationListenerService.RankingMap rankingMap) throws InflationException {
        String key = notification.getKey();
        if (DEBUG) {
            Log.d(TAG, "addNotification key=" + key);
        }
        mNotificationData.updateRanking(rankingMap);
        NotificationListenerService.Ranking ranking = new NotificationListenerService.Ranking();
        rankingMap.getRanking(key, ranking);

        NotificationEntry entry = new NotificationEntry(notification, ranking);
        Dependency.get(LeakDetector.class).trackInstance(entry);
        // 构建布局
        requireBinder().inflateViews(entry, () -> performRemoveNotification(notification,
                REASON_CANCEL));

        abortExistingInflation(key);

        mPendingNotifications.put(key, entry);
        for (NotificationEntryListener listener : mNotificationEntryListeners) {
            listener.onPendingEntryAdded(entry);
        }
    }

    @Override
    public void addNotification(StatusBarNotification notification,
            NotificationListenerService.RankingMap ranking) {
        try {
            addNotificationInternal(notification, ranking);
        } catch (InflationException e) {
            handleInflationException(notification, e);
        }
    }
}

四. 小结

源码分析中共涉及三个进程,分别是三方app进程、system_server进程、SystermUI进程,进程之间通过Binder通信。

持续更新…

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: nms.cpu_nms是一种非极大值抑制(Non-Maximum Suppression,NMS)算法,主要用于目标检测领域的物体框去重处理。在目标检测任务,常常会生成多个候选框,这些框可能有一定的重叠区域,而NMS的作用就是根据一定的规则选择出最具代表性的框。 nms.cpu_nms是一个基于CPU的实现,其主要流程包括以下几个步骤: 1. 输入:nms.cpu_nms接受候选框列表作为输入,每个候选框由矩形框的坐标和其对应的置信度组成。 2. 按置信度排序:首先对所有候选框按照置信度进行降序排序,将置信度最高的框放在前面。 3. 计算重叠区域:对排序后的框从前往后依次遍历,计算当前框与其后面框的重叠区域。 4. 筛选框:若两个框的重叠区域大于一定的阈值,则保留置信度较高的框,否则将其舍弃。 5. 循环迭代:重复步骤3和4,直到遍历完所有的框。 6. 输出:最终得到筛选后的一组最具代表性的框。 nms.cpu_nms是一种计算复杂度较高的算法,由于其使用CPU进行计算,相对速度较慢。为了提高性能,也可以使用nms.gpu_nms等基于GPU加速的实现。目前,NMS在目标检测广泛应用,并成功解决了多个候选框导致的冗余问题,提高了检测结果的准确性和效率。 ### 回答2: nms.cpu_nms是一种计算机视觉的非极大值抑制(Non-Maximum Suppression,NMS)的算法。它主要用于目标检测任务,将多个重叠的候选目标框缩减为较少的、最具代表性的目标框。其核心思想是通过消除掉重叠度较高的候选框,从而得到更准确的检测结果。 nms.cpu_nms的工作流程如下: 1. 输入:给定一组检测框候选区域和对应的置信度得分。 2. 根据置信度得分对所有候选区域进行降序排列。 3. 选择具有最高置信度得分的第一个候选框,并将其添加到最终的目标检测结果列表。 4. 遍历剩余的候选框,并计算其与已选定的目标框的重叠度(如交并比)。 5. 如果重叠度高于一定阈值,则将该候选框丢弃;如果重叠度低于阈值,则将该候选框添加到最终的目标检测结果列表。 6. 重复步骤4和5,直到所有的候选框都被遍历完成。 7. 输出:最终得到缩减后的目标检测结果列表。 nms.cpu_nms的实现通常基于CPU处理器,利用快速计算来实现高效的非极大值抑制。它是目标检测不可或缺的一部分,可以帮助提高检测结果的准确性和稳定性。而nms.cpu_nms是其一种算法的具体实现方式,在目标检测任务得到了广泛应用。 ### 回答3: nms.cpu_nms是一个用于目标检测任务的非极大值抑制算法,用于在检测结果去除重叠的边界框。在目标检测任务,检测器常常会生成多个边界框来覆盖目标物体,但是由于物体的大小、形状和姿态等因素的差异,这些边界框会存在一定的重叠。为了避免重复检测和提高检测结果的准确性,就需要使用非极大值抑制算法进行处理。 nms.cpu_nms是在CPU上执行的一种非极大值抑制算法,其原理是遍历所有的边界框,根据它们的置信度和重叠度进行筛选。具体来说,首先将所有边界框按照置信度进行排序,然后从置信度最高的边界框开始,计算它与其他边界框的重叠度,如果重叠度大于设定的阈值,就将其删除,否则保留。随后,继续处理下一个置信度最高的边界框,直到所有边界框都被遍历完成。 通过应用nms.cpu_nms,可以有效地减少冗余的检测结果,提高目标检测任务的准确性和效率。它常被广泛应用于各种计算机视觉任务,如目标检测、行人检测、车辆检测等。另外,由于nms.cpu_nms是在CPU上执行的,相对于在GPU上执行的nms算法,它的计算速度可能会较慢。但是,由于CPU具有广泛的兼容性和可扩展性,因此nms.cpu_nms在一些需要在较低性能设备上运行的场景仍然具有一定的优势。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值