: s" ^$ s) _6 h6 P: e - {, w+ l2 j# I 7 o1 a; k4 _- }$ j8 u# K) e* ^% b( k1 c( @% n D
方法一: lableme% Q, A H3 f2 v9 d" t
lableme标注完后。得到一个json文件,然后将这种json文件转成掩码图. 5 t' g8 k9 V2 p3 D. P 6 m8 n) [0 n s8 c# R0 A2 o# 代码来自 https://blog.csdn.net/hello_dear_you/article/details/120130155- S" N5 v0 E6 V
import json 0 ^4 m; p2 f2 h) |! m; Y" pimport numpy as np 5 _) [; f @. a9 E# r; l8 ?import cv21 t- W- ^4 r Q9 V/ s* I
# read json file : v! X. F1 Z. B7 e/ [4 ~with open("origin_json/mypic.json", "r") as f:3 o2 J0 u: [% r+ Z
data = f.read() # V! L# \3 s g" e9 p. R! b$ c$ A. ? & D/ b% q" L% o( s/ l; A1 J# convert str to json objs 4 L* f' K- n/ H/ G2 Kdata = json.loads(data)1 T2 {9 p' p! f) J
) [" ?5 _* s" P9 N4 h2 ~( t# get the points 2 E2 S+ j# m9 I. z0 R( Wpoints = data["shapes"][0]["points"] ( n/ @$ Q- C$ R; R+ i+ j. ]$ u+ k2 zpoints = np.array(points, dtype=np.int32) # tips: points location must be int32( c: R5 x1 Y9 x& Z
& l* \ a2 [$ _/ I3 E" M
# read image to get shape Y$ E( }6 E$ I% I9 zimage = cv2.imread("origin_png/person.jpg")/ F- e/ u2 s/ V/ B, b |4 w
: |2 U/ L; g# j; F
# create a blank image; i+ X$ S; W8 u& N4 h/ C
mask = np.zeros_like(image, dtype=np.uint8) ; |) }# x2 ~4 E+ r2 P5 K$ S3 G* e% e+ v+ [: R+ Q
# fill the contour with 255' B6 L5 r! }1 a( S' X7 k( F6 ^
cv2.fillPoly(mask, [points], (255, 255, 255)) ; s% ^8 O; K/ C) ?" U - Z" F# }% }7 J5 I7 d0 g# save the mask * z5 ~' N% W% E, Z0 W, }$ P$ v) W- f
cv2.imwrite("mask/person_mask.png", mask). {' K" o; n% g6 F
1 ( Y! a9 w$ x5 i; P" M$ Y2 # {& p3 r$ ?0 ?* {3 a' T7 u( o3& Q4 M! l( {4 i O: M
4 ) Q6 h7 y6 R( I& D1 B59 K& M p8 r/ X" L; G
6 + k. W- W' C5 w; I; j7% a! {/ J2 ^8 ~) p/ @4 a
8 3 z0 R/ S, T9 U/ M0 X9 K9 " V7 F# }8 s, z& g3 D* [9 [5 K2 Z10/ D' H/ j! O2 ~; Z& h& L8 D+ @
11" R; O8 t8 ^+ b( v
12( m/ {4 v N! A: K$ l+ A
13 $ ^/ y8 a8 K6 d: J% W8 G14% M6 v: ?5 s s, Y; I
15- n) R+ i& g: J2 m
16+ u( Q `+ V- h' A- d5 K
17/ a; @7 i! P% n. y8 M; W/ j
18 - l) Q2 k5 {9 ], y9 `' F+ ]& a19) v5 q9 z1 n. _
20 ' ?: ^% E) [, F& W) P4 x21- e3 {+ b5 C& ]# u$ S0 {" y
22' Y, p! z# U! P8 E0 r- n
23 ( z/ c1 |& e" A: m) n$ k2 y! \24 ( E/ F! X O. W5 G! \8 e25 * w- s) I! `' Y- @4 k3 C+ S26 x3 C* F! T/ U6 \; {
大概是这样:$ N/ p9 s9 u4 u! O; T ~! a' z
! S+ q/ W: w0 _- N3 }$ T
! F+ s& m3 F6 s8 z0 \然后利用这个mask生成图片 i' }, z6 s' ~% T$ @ 6 d6 G" M" X4 L- Y# 参考自: https://www.jianshu.com/p/1961aa0c02ee6 B2 y2 H- P) e4 N5 y, g
import cv2 $ U+ z7 y) {4 \" Cimport numpy as np8 ^ x1 l( y: b n: \* a0 A
$ P9 i; w9 w% S! L/ C
1 Q E/ M( r9 J' K9 m4 B
origin_png = 'origin_png/person.jpg'; ]& C& z6 \% l; O4 K
# maskPath = 'mask/person_mask.png' / ?# h+ q$ k$ n4 s7 xmaskPath = 'mask/bmv2.png' 4 v7 O; L( E: `! Y' nresult_png = 'result_png/result_png.png' ! g- g/ m9 p* X9 O" o , s# w$ L- w$ ]: \+ ] . s% L2 v; i7 F7 omaskImg = cv2.imread(maskPath) 4 W( @# [" q5 a# x) h3 x- `img = cv2.imread(origin_png) a) y# A. D6 y* h( y8 O% }9 P
assert maskImg.shape == img.shape, 'maskImg.shape != origin_png.shape'# `, [6 ?/ Y: O: w. B6 X0 G8 d
~- `/ L. {7 g+ \
h, w = img.shape[0], img.shape[1]) N0 i8 H% N/ @1 H
print('图片宽度: {}, 高度: {}'.format(h, w)) . T0 a7 E# d9 |3 I$ s, J) G+ U( J0 c2 `
rgb = (19,122,171) ! }1 v9 V1 H* ?, jbgr = (rgb[2], rgb[1], rgb[0]) / x9 w5 p: H& A2 x& i, I( y# (B, G, R) & n2 O6 ]( @* L6 N: l% Pfor i in range(h):/ @# @ h* {3 o- n8 @' V
for j in range(w): d& @0 e3 f4 J8 w) L# p. ]: W
if (maskImg[i, j] == 0).all():8 h* f5 s/ K1 ^9 l# `2 }& d
img[i, j] = bgr " w, V1 ~; G( X. Icv2.imwrite(result_png, img) k W; F" A7 H9 rprint('图片写入 {} 成功'.format(result_png)) 8 m3 y- p1 J) a' F1 0 M/ y6 D- `: |4 U0 { ~# G28 X+ M2 n' y8 i6 P
3 ! x* ^* O1 I8 j, B) S4 F; }4 + y/ B; ~8 n" v _9 ~% h5 3 X) J/ q. v/ L8 h/ l6 $ e1 I& K2 ~ Y& f2 U0 \8 J) {" e( Q7* k$ M( w* n/ V$ O9 o- u3 k
8 + n1 m" T$ |7 |9 5 U+ f6 U) X, T: W' Q3 z10 ! s m" N+ V" C& @& v11. K: a w9 @! S O" O5 E
128 M$ G3 X2 y0 b# h
13 & @1 g. m C0 u& }/ A: a! T# I14 . w' [( ^) ^3 R! y3 h' z7 l. \15 - W' T! ~! N4 ^; c) C16 ' H# L& I5 L% ]/ T17 " O0 [5 C8 [& d8 e18! Q! x* `4 \2 M3 `
190 E, t6 \8 }0 ?+ Q6 f, F" E. d
20 * ~1 H# L( g q* m- K, V219 f# G' C) l- l* }" F' A
22 0 \$ x/ E+ d- C) D+ p( @4 v. r239 D( L' t5 a, F, J6 h6 s
24$ \8 G) K& H( ^# a- e; j% R( U5 N2 s
25' j, O/ n5 d. d- I) I7 Y7 M' z! q
26 0 j s7 z( x( c/ E* ^. l278 T8 P( K. Y" t% ?
由于人长得一般,就不放图了… & p) C* S3 [3 b; r+ a. g5 s; d. K+ _ S
缺点: - C% D- U2 h% \5 R; Alableme标注时挺费力,并且难以避免人与背景边缘会有残留红色像素的情况。. `$ t6 A5 ~! T. ]2 |% D2 e- W6 D
9 d: K3 s9 y( T9 m. s7 T
2 U6 U" j" l) m) ?/ e: \7 i! C; o- `5 h' A" L/ q
: \2 `8 H" o- \+ n" N' ]. l4 u . @9 j, T, r1 o/ f方法二: 阈值$ m5 v2 D6 l: Q1 Q
该方法通过比较像素的RGB与背景的RGB来区分是否为图像背景。) c% z M& o. l7 n
3 y& y4 U. j, @7 t' o9 t7 a* K
Opencv& f; \* x- _5 f' U: A& e$ n5 x6 D
import cv2% ?/ u# \4 C5 q: Y% ~4 e
import numpy as np 8 O# l) {& r1 }, e: k: u" U. U. E$ x
) x2 q' u- h# p; Fdef mean_square_loss(a_np, b_np):7 X N0 \( p" O* t8 t5 B9 s
sl = np.square(a_np - b_np) 5 V5 q" R k3 D& F: r& e return np.mean(sl) 9 x. W! j1 H9 ~- o! j9 F5 \% Z& T6 D8 ?8 P
* C5 L" K9 i7 i! Q* Udef change_red2blue(origin_png, result_png):! C1 Z2 H$ R# ], _
img = cv2.imread(origin_png) ' l+ K; h- n; n* } o- W: Z; M' s4 `. e: j: N K
h, w = img.shape[0], img.shape[1] ) J( o9 z2 K L/ i print('图片宽度: {}, 高度: {}'.format(h, w)) % _! w4 p% L& W- Z* Y . H: T: f6 V& V origin_rgb = (168,36,32) # 可以用浏览器啥的控制台工具提取出背景的rgb值 % D! W3 x/ s, }' T origin_bgr = (origin_rgb[2], origin_rgb[1], origin_rgb[0]) i z' X- c! d9 i3 }3 S! k target_rgb = (19,122,171) # 蓝底RBG ) ^+ D( L, Z5 P* ~6 i4 K* n target_bgr = (target_rgb[2], target_rgb[1], target_rgb[0]) 9 `/ @( W ^' z- ]* X* u) s; d/ p: @0 C8 c" e6 M
for i in range(h): " h: v( n, V0 A3 Y; L6 z" k% A( A for j in range(w): & @# b& k' V# T/ \ # (B, G, R)' b* s% Q# T. }0 I. ]% R
if mean_square_loss(img[i, j], origin_bgr) < 50:/ [0 {+ d) J/ ?+ y' M6 _2 ?
img[i, j] = target_bgr $ h& @1 [; S/ w9 K. a8 K2 D1 }
% \8 G1 r0 e% t$ g/ d
cv2.imwrite(result_png, img)) a4 |' v& p) W
print('图片写入 {} 成功'.format(result_png)) ( H) R7 z1 C9 t' l 4 g- L/ {: q# {1 K' b& R; e9 a' f4 j5 F
if __name__ == '__main__': $ U8 w. N! n, I9 A7 u # origin_png = 'result_png/result_png.png' " b! K- ~+ \$ n/ [! ]! x origin_png = 'origin_png/person.jpg'2 \9 }! g6 z, ]$ @% f! D+ n5 W
result_png = 'result_png/result_refine.png' ) M7 C, a5 A* g+ c change_red2blue(origin_png, result_png)- K) t! _! }* }& { c" B/ |% P
1 : P3 _. Y7 n8 }% M, O20 }+ L$ ~. w1 Z0 a1 k5 F5 j
3 * X5 H' ?" k/ _9 N+ }8 n4 H! L) g4 @9 u/ T# ~6 r% C3 Y5 # J2 r' a9 u6 s U( U; A6 G: e2 y4 l/ r
7 ; C& ?* M, z+ O/ U9 C5 d: O8& P) q7 t0 v( p3 H- n/ k
9 8 c" J* L2 D2 d7 d1 o1 ^+ `10 * h+ ?# |% g$ t9 h' I# h/ a' V& V s11 1 I) Z) x3 g U( z127 z' C; _# t3 [/ W- v8 z' c1 Q3 r, a
13 5 D8 h' U( V9 p- W' a+ I! }; q! k14 ! B# Y+ g- f7 x$ y; }6 p15 5 i; b& n& m0 \2 q/ v: c/ n0 B$ S16 x/ W3 u) _4 m" O
17- Y! q1 q8 z3 t: V( K1 K. p
185 x$ A& H6 b' @# w( V) p
19 0 Z- g0 b3 n' I, X3 r: S C20" ^ P) ~* l' E. Q, T% g
219 a# O6 F) H0 l: H8 r
229 L8 C+ w: ]. `7 m
23% _1 i/ V" B9 h l
24 6 ~2 C2 q( v/ l, q) v25 - @. K- E0 t h9 e: s9 b8 q6 N* W26 6 L- `, l1 U! x4 j- F- W27 6 W. x9 Y6 P+ T0 m# p' v28 3 ]! ~% l, M; L$ e* L1 w+ b v. L29 9 }- l% J a I8 O% y1 q5 o30& o; A1 B& G& G% l- j0 I7 P& _5 c" Y
314 ~- f7 H' \7 ?* l" h; w
32+ [: M; t4 h) w% H; s/ B+ S2 ?# N
33$ i0 ^4 l* s6 x- n" c8 ~' p5 k
344 S; s$ [ k) N7 c6 _
352 B2 K5 d& x# S# F
结果人与背景边缘仍会存在红色像素残留& s, y! m6 b; P. s: t
" r9 b1 l7 I3 `9 C ( ^3 c3 y: L: f, F9 Y % [( S( k9 Z* y1 l; P3 ` * b8 q6 y3 T2 e - V( l; x9 ]7 D7 qPIL 5 p" L8 f! ]# r; ifrom torchvision.transforms.functional import to_tensor, to_pil_image0 n3 ~% _% o y# r, M$ G1 B
from PIL import Image " W2 t9 O" v: d1 K, M5 t+ [import torch4 O. ]" t3 _0 i% w
import time 1 x- r- i6 i4 r- X$ y# r0 A/ i9 V. ~" l' Z L; D2 T
9 e4 ~ a, v# O
def mean_square_loss(a_ts, b_ts): / Y5 c" T! Y! k- ] # print(a_ts.shape) % s+ X. ?& g6 d% u( W& ` # print(b_ts)! W% k1 V) M o$ v
sl = (a_ts - b_ts) ** 2 G8 p) B" A: u Y! N# v return sl.sum()8 I; \8 v# ?( R2 D4 i) z( m# Z* Z
9 K R8 N9 C2 Z8 @+ M
9 b$ o* L0 K4 e2 p' P
def change_red2blue(origin_png, result_png): 2 n4 I7 M" G" _/ N& e/ K/ a; n1 r src = Image.open(origin_png) s: f% w' z9 z2 N
src = to_tensor(src)6 t( I9 o( n: `
# print(src.shape) # torch.Size([3, 800, 600])7 P) k! I) Z" |% u& w
# channel: (R, G, B) / 255 % I) g+ R. W* _+ f+ S, {) A h, w = src.shape[1], src.shape[2]8 a+ G) M( T o% E
0 |2 o' a/ C& g pha = torch.ones(h, w, 3)1 s# w2 {# m6 W9 K
; m( l' r. _+ I6 ?: M9 R) M
bg = torch.tensor([168,36,32]) / 255 0 e. {! x8 l/ I. F! O, Z1 d target_bg = torch.tensor([19,122,171]) / 255! N/ X2 L* v- M h* m- f* |, k4 y
' M: p0 ?% N4 F$ U # C, H, W -> H, W, C3 b# S1 W) Y& X( n: }5 ~7 R' O# X
src = src.permute(1, 2, 0) ( G8 C4 v3 S: B4 ~3 H2 l+ w5 Z! f& u* w for i in range(h): 3 o* E# q5 |' d* v1 {1 @ for j in range(w):/ ~/ D/ i! w- ~+ ~7 {% a! S
if mean_square_loss(src[j], bg) < 0.025: # 0.025是阈值,超参数' N Z( {( |' V- d2 L5 ]9 _
pha[j] = torch.tensor([0.0, 0.0, 0.0])% d7 q- ~& C1 R- T: A: Q6 @* k
3 b$ [. }$ t3 E) w& n # H, W, C -> C, H, W , Z/ {5 J1 n" E# n src = src.permute(2, 0, 1)6 F9 e! }# V1 g4 U% A5 O( @7 h
pha = pha.permute(2, 0, 1). t9 k5 Q3 Z/ Z% O( L
com = pha * src + (1 - pha) * target_bg.view(3, 1, 1) 9 @2 d' Q4 v! M f to_pil_image(com).save(result_png) % `2 |" w1 w) \. X5 C$ d2 i6 @$ Q" q" `/ ~
* P; |2 V7 `7 J s+ X) `) X5 B
if __name__ == '__main__':, T8 B+ I, \ w2 @9 z+ E
origin_png = 'origin_png/person.jpg' + i5 n0 |# l$ C, P' h result_png = 'result_png/com.png'- P, {; _3 j% i; u3 ]' B0 J/ }
start_time = time.time()! c3 P. q8 V( R
change_red2blue(origin_png, result_png)1 w5 R3 {; H9 b- C3 M& C
spend_time = round(time.time() - start_time, 2)* i0 @: C9 ^8 |1 R) S' ~7 O5 ^
print('生成成功,共花了 {} 秒'.format(spend_time))/ g1 O/ ^/ }, c( R7 q
1* R* z/ n5 @" Y0 p8 b1 A$ c5 @4 d
2 # Q4 t6 s) D& f4 u3 O3 $ z" D& F8 W8 M3 b4 2 d4 \2 {. g" v7 Q: ` {$ s5 ! V( O" D. b* n4 L$ L: ?1 i# t6 ) @! e7 y Q' A* E5 y73 {$ b% D) B2 D0 l; l$ x
8 2 U+ g. d8 t% D8 p2 Z1 u9 6 r0 `1 ], W8 E [1 Z7 ?4 @101 P/ q+ b9 V/ I. ]6 r
110 g4 l u& K- T( R: J
12 V" S. V5 c* Q( K13 ; e0 c* U0 d& l7 E1 k" e14 , `4 N; \% [$ R; W15 5 _) }* B2 X& Q, m16 $ k% V2 v/ e" c5 u, n17+ ~; |! t' a2 l* O0 Z ] G: ]+ L; D
18, z. ], Y, s! R+ j- E3 `3 L
19+ v2 @- g6 ^' k6 a) J
20 * ^# d* N7 B- Z# R' ?21; j; J3 r0 w) `% N% R6 p0 j
226 K) o# z/ A" |7 k; h+ o. M. Q
23* r% y5 s. |; `6 q G
244 X3 D( `) \9 _: M! c8 n' G; _0 i3 ~
25( o p6 |9 [0 ^# B+ z9 B
26& |/ m/ v+ O2 S: z1 u h
27+ w* b8 E% m! V) c
283 c5 m* F T' I/ e# ~5 S& r
29 + O$ i3 R- b: m ?( ^5 Z2 n9 c30$ r) Z) S3 p* Y. I# ^9 r+ [' u" E8 d
31 v3 o0 t/ Y, V6 x
32 3 ^5 l& B8 z( B" a337 K: L* G* I9 K
34. `, ^( u. `" O( ?& Q1 r
353 \7 p3 r; Y! {8 |/ D& |
36$ _4 y& A7 G" h
37 ! p3 G) y% \% c- D( N4 a38 0 L) Q5 G8 O& u39; R% C6 V! d' ]/ d0 o, |# B
40 , A! v% x# \. A) | w41 2 {/ t) X' I {- R42 & N2 f9 {9 G8 |0 @43 ( Y4 e1 D" r4 S& N( ?44' {6 W. y; y& g% o) c
45 * n) v; K/ {1 w( g) K' k9 G3 q3 b464 J$ ^* u0 r! ~( p
该方法质量较好,但一张图片大概需要12秒。 / _1 Z* q' M6 l. B `, z8 |( g" ~# H+ g1 q. X- V% q1 w
0 f/ x& t2 j3 H