File duplicate finder
Some suggestions:


I would usually inline single line functions unless what they do is hard to understand from just reading the command. filesize might be one such candidate.
I believe that not specifying the block size can be faster, by allowing Python to use whatever block size it thinks is appropriate. This would need some actual testing though.
Your function arguments are only partially typed. You might want to use a stricter mypy configuration, such as the following, in your setup.cfg:

[mypy]
check_untyped_defs = true
disallow_untyped_defs = true
ignore_missing_imports = true
no_implicit_optional = true
warn_redundant_casts = true
warn_return_any = true
warn_unused_ignores = true

MD5 was optimized for cryptographic use, not for speed. Another Q&amp;A explores options for fast non-cryptographic hashing.
I would avoid converting digests to strings - it might be slightly easier for debugging purposes, but data type conversions are in general very costly.
Optional parameters mean you have to test at least two things rather than one. Your code only ever calls the hash_* functions without optional parameters, so you might as well either inline them.
On a related note, static values which are used in multiple places are perfect for pulling out as constants. The hashing function and block size would be obvious candidates for this.
Mutable default parameter values are an accident waiting to happen. The default pattern for this is to use a default of None and to assign a default if foo is None.
r-strings like r"./testdata" are meant for regular expressions. Since pic_dir is not used as such it should probably be a regular string.
duplicates is at first a list of potential duplicates and later is trimmed in stages. This makes the code hard to follow.
Things like the directory/directories to include should be arguments. argparse can deal with this easily.
To make this more scriptable I would print the result on standard output. It's trivial to then redirect it to a file.