Skip to content

Fix objectness is not assigned in dnn::region_layer#22660

Merged
asmorkalov merged 1 commit intoopencv:4.xfrom
zhouzq-thu:4.x
Apr 12, 2023
Merged

Fix objectness is not assigned in dnn::region_layer#22660
asmorkalov merged 1 commit intoopencv:4.xfrom
zhouzq-thu:4.x

Conversation

@zhouzq-thu
Copy link
Copy Markdown
Contributor

Fix objectness (dstData[index + 4]) is not assigned if new_coords == 1.

Pull Request Readiness Checklist

See details at https://github.com/opencv/opencv/wiki/How_to_contribute#making-a-good-pull-request

  • I agree to contribute to the project under Apache 2 License.
  • To the best of my knowledge, the proposed patch is not based on a code under GPL or another license that is incompatible with OpenCV
  • The PR is proposed to the proper branch
  • There is a reference to the original bug report and related work
  • There is accuracy test, performance test and test data in opencv_extra repository, if applicable
    Patch to opencv_extra has the same branch name.
  • The feature is well documented and sample code can be built with the project CMake

Fix objectness (dstData[index + 4]) is not assigned if new_coords == 1.
@asmorkalov
Copy link
Copy Markdown
Contributor

@zhouzq-thu Could you point the issue, enabled model, or better to add test for the fix?

@asmorkalov asmorkalov added the pr: needs test New functionality requires minimal tests set label Oct 19, 2022
@zhouzq-thu
Copy link
Copy Markdown
Contributor Author

@asmorkalov

In the latest 4.x branch, the objectness in the region_layer is only assigned at

dstData[index + 4] = logistic_activate(x); // logistic activation

But if YOLOv4 model is used (if new_coords == 1), dstData[index + 4] is not assigned. Therefore, some values in dstData are not predictable.

@asmorkalov asmorkalov added this to the 4.7.0 milestone Oct 20, 2022
@rogday
Copy link
Copy Markdown
Member

rogday commented Oct 21, 2022

@AlexeyAB, could you validate please?

Comment on lines +320 to 323
dstData[box_index + 4] = srcData[p_index];

scale = srcData[p_index];
if (classfix == -1 && scale < thresh)
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

If I understand correctly, new_coords only have affects on how we calculate the width and height of bboxes. It does not relate to whether objectness should be kept or not. See these lines from darknet: https://github.com/AlexeyAB/darknet/blob/62e5549cef53bd11890808615e0bfae59cfd4491/src/yolo_layer.c#L132-L153

By they way, the scale in line 322 should be the objectness as you mentioned. Objectness is only used to calculate the final object score, and hence we do not need to keep it in the output.

Copy link
Copy Markdown
Contributor Author

@zhouzq-thu zhouzq-thu Nov 1, 2022

Choose a reason for hiding this comment

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

  1. The dstData[index + 4] is uninitialized if new_coords=1, but initialized if new_coords=0. I think it is not good.
  2. If you have dstData[index + 4] values, you can optimize NMS by filtering some boxes (dstData[index + 4] < score_thresh), no need to find the maximum of all classes probabilities and check if it greater than score_thresh for each box.

ping @AlexeyAB

@asmorkalov asmorkalov modified the milestones: 4.7.0, 4.8.0 Dec 12, 2022
@asmorkalov asmorkalov requested review from asmorkalov and removed request for rogday March 13, 2023 13:26
@asmorkalov asmorkalov self-assigned this Mar 13, 2023
@asmorkalov
Copy link
Copy Markdown
Contributor

@zhouzq-thu @fengyuentau Thanks for the patch and review. I made small investigation and outcome is the following:

So, the proposed patch is relevant and correct.

@fengyuentau
Copy link
Copy Markdown
Member

@zhouzq-thu @fengyuentau Thanks for the patch and review. I made small investigation and outcome is the following:

So, the proposed patch is relevant and correct.

Thanks for the information. It is indeed should be set in case of initialization.

Just want to emphasize that the following do_nms_sort does not directly utilize the objectness score,

void do_nms_sort(float *detections, int total, float score_thresh, float nms_thresh)
{
std::vector<Rect2d> boxes(total);
std::vector<float> scores(total);
for (int i = 0; i < total; ++i)
{
Rect2d &b = boxes[i];
int box_index = i * (classes + coords + 1);
b.width = detections[box_index + 2];
b.height = detections[box_index + 3];
b.x = detections[box_index + 0] - b.width / 2;
b.y = detections[box_index + 1] - b.height / 2;
}
std::vector<int> indices;
for (int k = 0; k < classes; ++k)
{
for (int i = 0; i < total; ++i)
{
int box_index = i * (classes + coords + 1);
int class_index = box_index + 5;
scores[i] = detections[class_index + k];
detections[class_index + k] = 0;
}
NMSBoxes(boxes, scores, score_thresh, nms_thresh, indices);
for (int i = 0, n = indices.size(); i < n; ++i)
{
int box_index = indices[i] * (classes + coords + 1);
int class_index = box_index + 5;
detections[class_index + k] = scores[indices[i]];
}
}
}

because the objectness score (scale in the following code piece) has been blended into the class score with class_score=objectness*argmax(classes_score):

scale = srcData[p_index];
if (classfix == -1 && scale < thresh)
{
scale = 0; // if(t0 < 0.5) t0 = 0;
}
int class_index = index_sample_offset + index * cell_size + 5;
for (int j = 0; j < classes; ++j) {
float prob = scale*srcData[class_index + j]; // prob = IoU(box, object) = t0 * class-probability
dstData[class_index + j] = (prob > thresh) ? prob : 0; // if (IoU < threshold) IoU = 0;
}

Copy link
Copy Markdown
Member

@fengyuentau fengyuentau left a comment

Choose a reason for hiding this comment

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

👍 Approve in case of initialization.

@asmorkalov
Copy link
Copy Markdown
Contributor

I dig in tests and extra and found out that new_coords does not depend on Yolo version and we have test for YOLOv4x_mish with new_coords==1. For the case new_coords==1 objectness is taken from src tensor, but for another case it's taken from dst tensor. So on our side the final score is computed correctly, but exposed dst tensor contains random values for objectness. Further do_nms_sort does not use it and the issue is not visible with tests and popular use cases.

@asmorkalov asmorkalov assigned fengyuentau and unassigned asmorkalov and rogday Apr 12, 2023
@asmorkalov asmorkalov merged commit 136121f into opencv:4.x Apr 12, 2023
@asmorkalov asmorkalov added bug and removed pr: needs test New functionality requires minimal tests set labels Apr 12, 2023
@asmorkalov asmorkalov mentioned this pull request May 31, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants