상세 컨텐츠

본문 제목

[2020 KHU SW Festival]리그오브레전드 하이라이트 추출기(LoL eSports Highlight-Extractor)

Portfolio

by J2on 2022. 9. 5. 01:27

본문

- 2020년 경희대학교 SW페스티벌 제출을 위해 소프트웨어융합학과 과동아리 소융튜브 팀에서 제작한 프로젝트입니다.

자세한 프로젝트 정보는 링크에서 찾아보실 수 있습니다

. (제출 당시 군 휴학으로 홈페이지에는 이름이 오르지 못했지만 영상에서 찾아보실 수 있습니다.)

 

KHU SW페스티벌

 

swf.khu.ac.kr

- 2020년 경희대학교 SW페스티벌 주니어상 수상 (링크) 

 

본 게시글에서는 본인이 작성했던 코드만을 다룹니다. 

 - 부서 : 음성팀

 - 사용언어 : python

 - 코드 작성 목적 : 경기의 흐름에 따라 해설진의 음성, 인게임 사운드 그리고 관객들의 함성소리의 차이가 있음을 확인하였음. 이 점을 이용하여 e-sports 경기의 하이라이트 구간을 도출하기 위함.

 

 * 당시 군 입대를 앞두고 있던 상황으로 시간과 실력이 충분치 않은 상태로 작성한 코드이기에 수정 겸 코드리뷰 겸 작성하는 글입니다.

 

from pydub import AudioSegment

프레임워크는 pydub의 AudioSegment를 사용했습니다.

 

def divide_section(sound : AudioSegment) -> dict:
    """

        Divide section every second And measure highest amplitude of audio section

    """
    section_sec = 1000  # sound(AudioSegment) divided into 0.001 sec / 45min -> 272811 section
    number_of_section = int(len(sound) / section_sec) # reduced the number of sections / section : 272811 -> 2728

    amplitude_dict = {}
    for index in range(number_of_section) :
        section = sound[section_sec * (index) : section_sec * (index + 1)]
        high_amplitude = section.max
        high_amplitude = high_amplitude
        amplitude_dict[index] = high_amplitude

        if ((index + 1) == number_of_section):
            high_amplitude = sound[section_sec * (index + 1):].max
            amplitude_dict[(index + 1)] = high_amplitude
        else :
            continue

    return amplitude_dict

기본 AudioSegment에서는 0.001초 단위로 음량 값을 나누었는데, 이것을 1초 단위로 줄이는 과정을 거쳤습니다.

이것을 dictionary로 key : 초 / value : 해당 초의 최고 음량으로 저장했습니다.

 

이때 amplitude_dict는 

{0: 3771, 1: 20379, 2: 16253, 3: 8154, 4: 7170, 5: 10111, 6: 9103, 7: 7344, 8: 5740, 9: 1860, 10: 14159, ...}

의 형태로 저장됩니다.

def score_amps(amp_dict : dict) -> dict:
    amps_list = list(amp_dict.values())
    amps_list.sort(reverse = True)
    top_sound = amps_list[0]

    for index in range (len(amp_dict)) :
        amp_dict[index] = round((amp_dict[index] / top_sound) * 100, 4)

    print(amp_dict)
    return amp_dict

그리고 이 음량 값들을 0-100으로 수치화하는 과정입니다.

value(음량)의 list를 만들어 내림차순으로 정렬하고 영상의 최고 음량을 100으로 기준으로 잡고 소수 4번째 자릿수까지 수치화하여

{0: 11.6245, 1: 62.8206, 2: 50.1017, 3: 25.1356, 4: 22.1023, 5: 31.1683, 6: 28.061, 7: 22.6387, 8: 17.6942, 9: 5.7337,...}

형태로 amp_dict에 저장합니다.

def filter_amps(amp_dict : dict) -> dict :
    """
    앞서 스코어링한 딕셔너리 파일에서 필요한 부분 외에는 삭제하는 함수
    """
    for index in range(len(amp_dict)) :
        if (amp_dict[index] < 40) : # 임의의 값 설정
            del amp_dict[index]
        else :
            continue

    time_list = list(amp_dict.keys())

앞서 수치화한 음량들을 임의의 값 40을 기준으로 40 이상의 시점(초)들을 time_list에 저장합니다.

 

이때 time list에는 { 3,10, 23, 35, ... } 처럼 연속되지 않은 값들이 대부분이므로

    # 5초 안에 연결되지 않은 하이라이트 시점이 있다면 연결함
    for index in range(1, len(amp_dict)):
        if ((time_list[index] - time_list[index - 1]) <= 5):
            for num in range(1, time_list[index] - time_list[index - 1]):
                time_list.append(time_list[index - 1] + num)
    time_list.sort()

시점들이 5초 이내로 차이가 날 경우 그 사이를 채워 넣었습니다.

#테이블 형식으로 이어져 있는 시간들을 리스트로 묶어 리스트 in 리스트로 제작. 원소가 3개 미만의 리스트는 삭제
    time_table = []
    temp_index = 0
    for index in range(len(time_list) - 1) :
        if ((time_list[index] + 1) != time_list[index + 1]) :
            small_list = time_list[temp_index : index + 1]
            time_table.append(small_list)
            temp_index = index + 1
            
	empty_list = []
    for index in range(len(time_table)) :
        if (len(time_table[index]) >= 3) :
            empty_list.append(time_table[index])
        else :
            continue
    time_table = empty_list
    
    return time_table

이어진 시점들끼리 list로 묶어 2차원 matrix형태로 구성하고 원소가 3개 이상인 시점 list만 포함시켰습니다.

[[20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30], [44, 45, 46], [100, 101, 102, 103, 104, 105], [185, 186, 187, 188, 189, 190, 191, 192, 193, 194], [202, 203, 204, 205, 206, 207, 208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221],...}

이렇게 저장이 되었네요.

 

def expend_timelist(time_table : list) -> None : #전후과정을 알 수 있게 앞뒤 2초씩 추가
    for time_list in time_table :
        start_num = time_list[0]
        end_num = time_list[-1]
        if (start_num >= 2) :
            temp_list = [start_num - 2, start_num - 1, end_num + 1, end_num + 2]
            time_list.extend(temp_list)
            time_list.sort()

마지막 과정이네요. 하이라이트에서도 전개과정과 마무리도 중요하니 앞 뒤로 2초씩 연장해주었습니다.

 

def main():
    sound = AudioSegment.from_file("T1 vs SANDBOX Game 2 - LCK 2020 Spring Split W4D3 - SK Telecom T1 vs SBG G2.wav", format = "wave")
    amps_dict = divide_section(sound)
    amps_dict = score_amps(amps_dict)
    time_table = filter_amps(amps_dict)
    expend_timelist(time_table)
    print(time_table)

    return None

if __name__ == '__main__':
    main()

이 과정을 거치면 아래의 하이라이트 시점들을 얻을 수 있습니다.

 

TestCase로는 2020년 LCK 스프링 T1과 샌드박스의 경기를 사용했습니다.

https://www.youtube.com/watch?v=Je0G6aUJht0 

 

 

결과물

[[18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32], [42, 43, 44, 45, 46, 47, 48], [98, 99, 100, 101, 102, 103, 104, 105, 106, 107], [183, 184, 185, 186, 187, 188, 189, 190, 191, 192, 193, 194, 195, 196], [200, 201, 202, 203, 204, 205, 206, 207, 208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223], [295, 296, 297, 298, 299, 300, 301], [321, 322, 323, 324, 325, 326, 327, 328, 329, 330, 331, 332, 333, 334, 335, 336, 337, 338, 339], [444, 445, 446, 447, 448, 449, 450, 451, 452, 453], [455, 456, 457, 458, 459, 460, 461, 462, 463, 464], [466, 467, 468, 469, 470, 471, 472, 473, 474, 475, 476, 477, 478, 479, 480, 481], [485, 486, 487, 488, 489, 490, 491, 492, 493], [510, 511, 512, 513, 514, 515, 516, 517, 518], [525, 526, 527, 528, 529, 530, 531, 532, 533], [736, 737, 738, 739, 740, 741, 742, 743, 744, 745, 746, 747, 748, 749], [804, 805, 806, 807, 808, 809, 810], [816, 817, 818, 819, 820, 821, 822, 823, 824, 825, 826, 827, 828, 829, 830, 831], [837, 838, 839, 840, 841, 842, 843, 844, 845, 846], [850, 851, 852, 853, 854, 855, 856, 857, 858, 859, 860, 861, 862], [916, 917, 918, 919, 920, 921, 922, 923, 924, 925, 926], [928, 929, 930, 931, 932, 933, 934, 935, 936, 937, 938, 939, 940, 941, 942, 943, 944, 945, 946, 947, 948, 949, 950, 951, 952, 953, 954, 955, 956, 957, 958, 959, 960, 961, 962, 963, 964, 965, 966, 967, 968, 969, 970, 971, 972, 973, 974, 975, 976, 977, 978, 979, 980, 981, 982, 983, 984, 985, 986, 987, 988, 989, 990, 991, 992, 993, 994, 995, 996, 997, 998, 999, 1000, 1001, 1002, 1003, 1004, 1005, 1006, 1007, 1008, 1009, 1010, 1011, 1012, 1013, 1014], [1030, 1031, 1032, 1033, 1034, 1035, 1036, 1037, 1038], [1127, 1128, 1129, 1130, 1131, 1132, 1133, 1134, 1135], [1219, 1220, 1221, 1222, 1223, 1224, 1225, 1226, 1227, 1228, 1229, 1230, 1231, 1232, 1233, 1234, 1235], [1264, 1265, 1266, 1267, 1268, 1269, 1270, 1271, 1272, 1273, 1274, 1275, 1276, 1277], [1339, 1340, 1341, 1342, 1343, 1344, 1345, 1346, 1347, 1348, 1349], [1437, 1438, 1439, 1440, 1441, 1442, 1443, 1444, 1445], [1506, 1507, 1508, 1509, 1510, 1511, 1512, 1513, 1514], [1531, 1532, 1533, 1534, 1535, 1536, 1537, 1538, 1539, 1540, 1541], [1554, 1555, 1556, 1557, 1558, 1559, 1560, 1561, 1562, 1563, 1564, 1565, 1566, 1567, 1568, 1569, 1570, 1571, 1572, 1573, 1574, 1575, 1576, 1577, 1578, 1579, 1580, 1581, 1582, 1583, 1584, 1585, 1586, 1587, 1588, 1589], [1593, 1594, 1595, 1596, 1597, 1598, 1599, 1600, 1601, 1602, 1603, 1604, 1605, 1606, 1607], [1626, 1627, 1628, 1629, 1630, 1631, 1632, 1633], [1650, 1651, 1652, 1653, 1654, 1655, 1656, 1657], [1679, 1680, 1681, 1682, 1683, 1684, 1685, 1686, 1687, 1688, 1689, 1690], [1743, 1744, 1745, 1746, 1747, 1748, 1749, 1750, 1751, 1752, 1753, 1754, 1755, 1756, 1757, 1758, 1759, 1760, 1761, 1762, 1763, 1764, 1765, 1766, 1767, 1768, 1769, 1770, 1771, 1772, 1773, 1774, 1775, 1776, 1777, 1778, 1779, 1780, 1781, 1782, 1783, 1784, 1785, 1786, 1787, 1788, 1789, 1790, 1791, 1792, 1793, 1794, 1795, 1796, 1797, 1798, 1799], [1839, 1840, 1841, 1842, 1843, 1844, 1845, 1846, 1847, 1848, 1849, 1850], [1876, 1877, 1878, 1879, 1880, 1881, 1882, 1883, 1884], [1904, 1905, 1906, 1907, 1908, 1909, 1910, 1911, 1912], [1922, 1923, 1924, 1925, 1926, 1927, 1928, 1929, 1930, 1931, 1932, 1933, 1934, 1935, 1936, 1937, 1938, 1939, 1940, 1941, 1942, 1943, 1944, 1945, 1946, 1947, 1948, 1949, 1950, 1951, 1952, 1953, 1954, 1955, 1956, 1957, 1958, 1959, 1960, 1961, 1962, 1963, 1964, 1965, 1966, 1967, 1968], [1976, 1977, 1978, 1979, 1980, 1981, 1982], [1985, 1986, 1987, 1988, 1989, 1990, 1991, 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016], [2031, 2032, 2033, 2034, 2035, 2036, 2037, 2038, 2039, 2040, 2041], [2050, 2051, 2052, 2053, 2054, 2055, 2056, 2057, 2058], [2068, 2069, 2070, 2071, 2072, 2073, 2074, 2075, 2076, 2077, 2078, 2079, 2080, 2081, 2082, 2083, 2084, 2085, 2086, 2087, 2088, 2089, 2090], [2096, 2097, 2098, 2099, 2100, 2101, 2102, 2103, 2104, 2105, 2106, 2107, 2108, 2109, 2110, 2111, 2112, 2113, 2114, 2115, 2116, 2117, 2118, 2119, 2120, 2121, 2122, 2123, 2124, 2125, 2126, 2127, 2128, 2129, 2130, 2131, 2132, 2133, 2134, 2135, 2136, 2137], [2182, 2183, 2184, 2185, 2186, 2187, 2188, 2189, 2190, 2191, 2192, 2193, 2194, 2195, 2196, 2197, 2198, 2199], [2218, 2219, 2220, 2221, 2222, 2223, 2224, 2225], [2229, 2230, 2231, 2232, 2233, 2234, 2235], [2237, 2238, 2239, 2240, 2241, 2242, 2243, 2244, 2245, 2246, 2247, 2248, 2249, 2250, 2251, 2252, 2253, 2254, 2255, 2256, 2257, 2258, 2259, 2260, 2261, 2262, 2263, 2264, 2265, 2266, 2267, 2268, 2269, 2270, 2271, 2272], [2278, 2279, 2280, 2281, 2282, 2283, 2284, 2285, 2286, 2287], [2306, 2307, 2308, 2309, 2310, 2311, 2312, 2313, 2314, 2315], [2321, 2322, 2323, 2324, 2325, 2326, 2327, 2328, 2329, 2330, 2331, 2332, 2333], [2341, 2342, 2343, 2344, 2345, 2346, 2347, 2348, 2349], [2360, 2361, 2362, 2363, 2364, 2365, 2366, 2367, 2368], [2396, 2397, 2398, 2399, 2400, 2401, 2402, 2403, 2404, 2405, 2406, 2407, 2408, 2409], [2411, 2412, 2413, 2414, 2415, 2416, 2417, 2418], [2424, 2425, 2426, 2427, 2428, 2429, 2430, 2431, 2432], [2513, 2514, 2515, 2516, 2517, 2518, 2519, 2520, 2521, 2522, 2523, 2524, 2525], [2527, 2528, 2529, 2530, 2531, 2532, 2533, 2534, 2535, 2536, 2537, 2538, 2539, 2540, 2541], [2547, 2548, 2549, 2550, 2551, 2552, 2553, 2554], [2557, 2558, 2559, 2560, 2561, 2562, 2563, 2564, 2565, 2566, 2567, 2568, 2569, 2570, 2571, 2572, 2573, 2574, 2575, 2576, 2577, 2578], [2606, 2607, 2608, 2609, 2610, 2611, 2612, 2613, 2614, 2615, 2616, 2617, 2618], [2628, 2629, 2630, 2631, 2632, 2633, 2634, 2635, 2636, 2637, 2638, 2639, 2640, 2641, 2642, 2643, 2644, 2645], [2677, 2678, 2679, 2680, 2681, 2682, 2683, 2684, 2685], [2690, 2691, 2692, 2693, 2694, 2695, 2696, 2697, 2698, 2699, 2700, 2701, 2702, 2703, 2704, 2705, 2706, 2707, 2708, 2709, 2710, 2711, 2712]]

https://youtu.be/FuqcvMTJ6GI

 

더 자세한 내용은 깃허브에서 확인하실 수 있습니다...

https://github.com/SWTube/LoL-eSports-Highlight-Extractor

 

GitHub - SWTube/LoL-eSports-Highlight-Extractor: This program will have a full match video of an LoL game, especially the ones b

This program will have a full match video of an LoL game, especially the ones based in LCK. The program will analyze the match via CNN, and extract the highlight clips of the game. - GitHub - SWTub...

github.com

https://github.com/SWTube/LoL-eSports-Highlight-Extractor/blob/feature-speech-recognition/audio_processing.py

 

GitHub - SWTube/LoL-eSports-Highlight-Extractor: This program will have a full match video of an LoL game, especially the ones b

This program will have a full match video of an LoL game, especially the ones based in LCK. The program will analyze the match via CNN, and extract the highlight clips of the game. - GitHub - SWTub...

github.com

 

-----------------------------------------------------------------------------------------------------------------------------------------------------------------

이 코드를 작성했던 2020년 여름에는 꽤 오래 고민하며 작성했던 코드였는데 지금보니 필요없는 코드도 많이 보이네요. 더 노력해야겠습니다.

관련글 더보기

댓글 영역