英雄联盟二次元头像怎么获得 lol属于二次元游戏吗

时间:2024-11-09 15:59:08栏目:手游测评
写在前面

大家都玩过拼图,玩法是将一张张小的图像块拼成一张大图。因为小图是大图的一部分,大家可以根据大图的内容判断小图的位置,从而完成拼图。问题来了,如果每个图块是一张独立的图像呢,这样的拼图该怎么拼呢?今天在这里带大家制作这种由独立图像块组成的拼图,这样的图像也有另外一个名称——马赛克图像。使用的素材是二次元头像,目的是拼成一副DOTA游戏里痛苦女王和骷髅王的形象。下面是图片素材,大家可以自行感受下两者的画风差异。

英雄联盟二次元头像怎么获得 lol属于二次元游戏吗-第1张

二次元头像

英雄联盟二次元头像怎么获得 lol属于二次元游戏吗-第2张

DOTA英雄

拼图原理

马赛克拼图的原理很简单,大家都知道,一张图像是由若干个像素组成,例如一张64x64的图像就是由4096个像素组成。如果把这张64x64的图像分成16份4x4的图像,然后把每一个4x4的区域换成一个特征相近的4x4的独立图像,这样这张64x64的图像就变成了一张马赛克拼图,而且整体的结构信息不会有太大的改变。保证结构信息不变的关键有两个:

特征选择要合理,能表示颜色和纹理特征用于拼图的图块要充足,至少要能覆盖大图的颜色

这里有一份示例代码,特征直接选择像素值,用欧几里德距离计算相似度,大家如果要改进可以直接换特征,比如换成LBP算子、HOG特征描述子等等,当然最完美的是采用卷积神经网络来提取特征。这里可以先看下主要的代码结构,总体分三步:

将大图按小图的个数分块找到大图每个图像块区域颜色最接近的小图将小图粘贴到大图对应位置

import ctypes\nimport re\nimport numpy as np\nfrom numpy import ctypeslib\nimport os\nimport cv2\nimport multiprocessing as mp\nfrom multiprocessing.sharedctypes import RawArray\nfrom scipy.spatial.distance import euclidean\nfrom tqdm import tqdm\n\nIMG_DIR = \"images\" # 二次元图像路径\nRATIO = 10\n\ndef resize(im, tile_row, tile_col):\n shape_row = im.shape[0]\n shape_col = im.shape[1]\n shrink_ratio = min(shape_row/tile_row, shape_col/tile_col)\n resized = cv2.resize(im, (int(shape_col/shrink_ratio)+1, int(shape_row/shrink_ratio)+1), interpolation=cv2.INTER_CUBIC)\n result = resized[:tile_row, :tile_col,:]\n return result\n\ndef img_distance(im1, im2):\n if im1.shape != im2.shape:\n msg = \"shapes are different {} {}\".format(im1.shape, im2.shape)\n raise Exception(msg)\n array1 = im1.flatten()\n array2 = im2.flatten()\n dist = euclidean(array1, array2)\n return dist\n\ndef load_all_images(tile_row, tile_col):\n img_dir = IMG_DIR\n filenames = os.listdir(img_dir)\n result = []\n print(len(filenames))\n for filename in tqdm(filenames):\n if not re.search(\".jpg\", filename, re.I):\n continue\n try:\n filepath = os.path.join(img_dir, filename)\n im = cv2.imread(filepath)\n row = im.shape[0]\n col = im.shape[1]\n im = resize(im, tile_row, tile_col)\n result.append(np.array(im))\n except Exception as e:\n msg = \"error with {} - {}\".format(filepath, str(e))\n print(msg)\n return np.array(result, dtype=np.uint8)\n\ndef find_closest_image(q, shared_tile_images, tile_images_shape, shared_result, img_shape, tile_row, tile_col):\n tile_images_array = np.frombuffer(shared_tile_images, dtype=np.uint8)\n tile_images = tile_images_array.reshape(tile_images_shape)\n while True:\n [row, col, im_roi] = q.get()\n print(row)\n min_dist = float(\"inf\")\n min_img = None\n for im in tile_images:\n dist = img_distance(im_roi, im)\n if dist < min_dist:\n min_dist = dist\n min_img = im\n im_res = np.frombuffer(shared_result, dtype=np.uint8).reshape(img_shape)\n im_res[row:row+tile_row,col:col+tile_col,:] = min_img\n q.task_done()\n\ndef get_tile_row_col(shape):\n if shape[0] >= shape[1]:\n return [120, 90]\n else:\n return [90, 120]\n\ndef generate_mosaic(infile, outfile):\n img = cv2.imread(infile)\n tile_row, tile_col = get_tile_row_col(img.shape)\n img_shape = list(img.shape)\n img_shape[0] = int(img_shape[0]/tile_row) * tile_row * RATIO\n img_shape[1] = int(img_shape[1]/tile_col) * tile_col * RATIO\n img = cv2.resize(img, (img_shape[1], img_shape[0]), interpolation=cv2.INTER_CUBIC)\n print(img_shape)\n im_res = np.zeros(img_shape, np.uint8)\n tile_images = load_all_images(tile_row, tile_col)\n shared_tile_images = mp.sharedctypes.RawArray(ctypes.c_ubyte, len(tile_images.flatten()))\n tile_images_shape = tile_images.shape\n np.copyto(np.frombuffer(shared_tile_images, dtype=np.uint8).reshape(tile_images_shape), tile_images)\n shared_result = mp.sharedctypes.RawArray(ctypes.c_ubyte, len(im_res.flatten()))\n\n q = mp.JoinableQueue()\n for i in range(5):\n p = mp.Process(target=find_closest_image,\n args=(q, shared_tile_images, tile_images_shape, shared_result, img_shape, tile_row, tile_col),\n daemon=True)\n p.start()\n print(\"started process\")\n\n for row in range(0, img_shape[0], tile_row):\n for col in range(0, img_shape[1], tile_col):\n roi = img[row:row+tile_row,col:col+tile_col,:]\n q.put([row, col, roi])\n\n q.join()\n cv2.imwrite(outfile, np.frombuffer(shared_result, dtype=np.uint8).reshape(img_shape))\n\nif __name__ == \"__main__\":\n generate_mosaic(\"test.jpg\", \"out.jpg\") # test.jpg是DOTA图像效果展示

这里要说明下,上面的代码只是方便大家理解制作马赛克拼图的原理,但是要达到比较好的效果这份代码是远远不够的。但没关系,已经有人给大家做好了轮子!有个软件叫 Foto-Mosaik-Edda,下载地址 https://fmedda.com/en/download,使用起来也很简单。这里我用2000张二次元头像拼成了两个DOTA英雄,效果还不错吧,放大看还能看到二次元头像的细节。大家快去尝试吧。

英雄联盟二次元头像怎么获得 lol属于二次元游戏吗-第3张

马赛克拼图效果

上一篇:明日之明日之后全钢盾牌有什么用 明日之后盾牌和无双副武器对比

下一篇:航海王热血航线互通吗 航海王热血航线怎么登录别人的账号