首先感谢《利用Python求解带约束的最优化问题》让我不用自己重0开始敲代码了!不过就是效率有点慢了,还在解决中,至少问题有解了~
=================== 问题描述 =======================
昨天项目例会上产品提了一个看似很正常但是细想却很棘手的要求,在前端页面上展示出来的内容如果仅仅只有分割网络的结果会很奇怪,对用户的体验不好,他需要在包含目标的bbox基础上向外扩充一点,即符合前端展示的长宽比例又要突出重点(检测网络出的结果)。初看没毛病,也没啥困难的地方。可是当去准备给出这个坐标的时候就傻了!扩多少?检测出来的bbox长宽比千奇百怪,怎么变成符合前端需要的长宽比?还不能一个不动,只动另一个,这样做的结果很奇怪,我昨天就这么干过一次/捂脸。图中绿色的框就是我的bbox,蓝色的框就是我的目标(前端只显示蓝色框中的内容!),要向外扩的(为了避免不适,我把重点都马赛克了)
========================== 数学好好玩========================
问题转换成,已知bbox、目标矩形的长宽比hw_rate,求一个extented_bbox,符合extented_bbox.h/extented_bbox.w = hw_rate,且extented_bbox.area不能太小或太大,暂且定义成bbox.area/extented_bbox.area <=0.8,设宽和长各需扩充x,y,原bbox的长宽为h,w,目标长宽比为r,则有:
z
=
f
(
x
,
y
)
=
(
w
+
x
)
⋅
(
h
+
y
)
z=f(x,y)=(w+x)\cdot (h+y)
z=f(x,y)=(w+x)⋅(h+y)
st.
φ
(
x
,
y
)
=
w
+
x
h
+
y
−
r
=
0
\varphi(x,y)=\frac{w+x}{h+y}-r=0
φ(x,y)=h+yw+x−r=0
(
w
+
x
)
⋅
(
h
+
y
)
w
⋅
h
−
1
0.8
⩽
0
\frac{(w+x)\cdot (h+y)}{w\cdot h}-\frac{1}{0.8}\leqslant 0
w⋅h(w+x)⋅(h+y)−0.81⩽0
最小化z即可,然后将求得的x,y带入就是一个符合r的最小框,这时得到的(w+x)和(h+y)还不是最终结果,只是求得了一个符合目标长宽比的值,然后再一点点的向外扩,由于没人知道到底扩多少合适,大概满足前端可以再绿色和蓝色的中间地带写字即可,我留了100像素。不过这里有个问题,一直不明白为什么,就是不等式约束条件写成上面的形式就有结果,如果写成(wh)/(w+x)(h+y)<=0.8这种就无解了。知道的还请说一声,谢谢先~
代码几乎和《利用Python求解带约束的最优化问题》一样,只是把公式改成了我自己的公式
from sympy import *
def bbox_extened(w, h, wh_rate, area_rate):
# 设置变量
deltaW = symbols("deltaW")
deltaH = symbols("deltaH")
alpha = symbols("alpha")
beta = symbols("beta")
# 构造拉格朗日等式
L = (w + deltaW) * (h + deltaH) + \
alpha * (((w + deltaW) * (h + deltaH)) / (w * h) - area_rate) + \
beta * ((w + deltaW) / (h + deltaH) - wh_rate)
# 求导,构造KKT条件
difyL_x1 = diff(L, deltaW) # 对变量x1求导
difyL_x2 = diff(L, deltaH) # 对变量x2求导
difyL_beta = diff(L, beta) # 对乘子beta求导
dualCpt = alpha * (((w + deltaW) * (h + deltaH)) / (w * h) - area_rate) # 对偶互补条件
# 求解KKT等式
res = solve([difyL_x1, difyL_x2, difyL_beta, dualCpt], [deltaW, deltaH, alpha, beta])
# for item in res:
# print(item)
# print((w*h)/((w + item[0]) * (h + item[1])))
return res