Microsoft MVP성태의 닷넷 이야기
글쓴 사람
정성태 (techsharer at outlook.com)
홈페이지
첨부 파일
 

C# - EXE/DLL로부터 추출한 이미지/아이콘의 배경색 투명 처리

지난번에 EXE/DLL로부터 이미지 추출하는 방법을 설명했는데요.

프로그램에 보여지는 리소스(예: 아이콘) 추출하는 방법
; https://www.sysnet.pe.kr/2/0/1524

실제로 해보니, 아이콘은 투명 처리가 잘 되어 있는데 이미지의 경우에는 BMP 파일로 저장되어 불투명 이미지가 나왔습니다. 그래서 투명 처리가 필요했는데요. 그래픽 툴을 사용하는 것보다 명령행 도구가 없나 해서 검색해 보았더니 다음의 도구가 나오는군요. ^^

ImageMagick  - Convert, Edit, and Compose Images
; http://www.imagemagick.org

Windows Binary Release
; http://www.imagemagick.org/script/binary-releases.php#windows

압축 버전도 제공하므로 이를 내려받아 해제합니다. exe 파일이 몇 개 나오는데, 그중에서 convert.exe 실행 파일을 이용하면 됩니다.

예를 들어 BMP 이미지에서 0xFF00FF RGB 색을 투명으로 처리하고 싶다면 이렇게 실행해 줍니다.

convert file.bmp -transparent #ff00ff file.png

그런데, EXE/DLL로부터 추출된 이미지 같은 경우에는 가끔 다음과 같은 식으로 오류가 발생하면서 처리가 안 될 때가 있습니다.

C:temp>convert file.bmp -transparent #ff00ff file.png
convert.exe: Length and filesize do not match `file.bmp' @ error/bmp.c/ReadBMPImage/817. 

혹시나 싶어, convert.exe 폴더에 있는 다른 실행 파일인 imdisplay.exe로 파일을 열어도 역시 마찬가지의 오류가 발생합니다.

IMDisplayDoc function [DoReadImage] reported an error.
IMDisplay.exe: length and filesize do not match '...' @ error/bmp.c/ReadBMPImage/817

가만보니, palette가 구성된 이미지인 경우인데 Visual Studio로 확인하면 다음과 같이 왼쪽에 팔렛트가 펼쳐집니다.

bmp2png_transparent_1.png

이런 BMP 파일은 그냥 그림판(mspaint.exe)으로 열어서 24bit BMP 파일로 저장해 주고 convert 프로그램을 실행하면 됩니다.




ImageMagick의 convert.exe에는 많은 옵션이 있지만 투명 처리 정도는 C#으로도 간단히 할 수 있습니다. 다음은 이에 대한 코드를 담고 있는 글입니다.

Setting transparency in an image
; http://blogs.msdn.com/b/jmstall/archive/2007/08/06/setting-transparency-in-an-image.aspx

나중에 찾아 보기 쉽도록 여기다 코드를 그대로 복사해 두죠. ^^

// Simple tool to mark a color on the bitmap transparent.
// http://blogs.msdn.com/jmstall
using System;
using System.Collections.Generic;
using System.Text;
using System.Drawing;
using System.IO;

namespace MakeTransparent
{
    class Program
    {
        static void Main(string[] args)
        {
            if (args.Length == 0)
            {
                Console.WriteLine(
@"Usage:
    MakeTransparent <filename>

Makes the background color in the image transparent. 
This assumes the pixel at 0,0 is the background color. 

This saves it as a new file appended with '.t', so that it doesn't erase 
the original filename.  For example 'c:\dir\a.bmp' becomes 'c:\dir\a.t.bmp'.
Callers can rename as needed.
");
                return;
            }

            string filename = args[0];
            filename = Path.GetFullPath(filename);
            Console.WriteLine("Loading image from:{0}", filename);

            Bitmap myBitmap = new Bitmap(filename);

            // Get the color of a background pixel.
            // Assume upper left corner is opaque.
            Color backColor = myBitmap.GetPixel(0, 0);

            Console.WriteLine("Choosing background color based of pixel at (0,0). Color ={0}", backColor.ToString());

            // Make backColor transparent for myBitmap. This is the heart of the program.
            myBitmap.MakeTransparent(backColor);

            // Change "c:\dir\thing.bmp" to "c:\dir\thing.t.bmp"
            string left = Path.ChangeExtension(filename, null);
            string ext = Path.GetExtension(filename); // includes period ".bmp"
                        
            string outFile = left + ".t" + ext;
            Console.WriteLine("Saving back to file: {0}", outFile);

            myBitmap.Save(outFile);
        }
    }
}

주석에도 달려 있지만, (0,0) 위치의 색을 투명색으로 지정할 뿐 별다르게 특별한 처리는 없습니다.




// https://twitter.com/PR0GRAMMERHUM0R/status/1773122861487575208/photo/1

from rembg import remove
from PIL import Image

input = Image.open('cl.jpg')
output = remove(input)
output.save('output.png')




[이 글에 대해서 여러분들과 의견을 공유하고 싶습니다. 틀리거나 미흡한 부분 또는 의문 사항이 있으시면 언제든 댓글 남겨주십시오.]







[최초 등록일: ]
[최종 수정일: 3/29/2024]

Creative Commons License
이 저작물은 크리에이티브 커먼즈 코리아 저작자표시-비영리-변경금지 2.0 대한민국 라이센스에 따라 이용하실 수 있습니다.
by SeongTae Jeong, mailto:techsharer at outlook.com

비밀번호

댓글 작성자
 



2018-12-18 08시10분
[초보입니다] 안녕하세요 다름이 아니라..

아이콘 만들기에 대해 자세히 알고 계신거 같아서 질문드립니다..

저는 해당 exe경로로부터 절대경로든 상대 경로든 일단 프로세스 라이브러리를 통해 실행파일을 만들었으나 해당 exe파일도 절대경로 혹은 상대경로에 따른 exe경로의 ico도 같이 들고와 적용 시키려 합니다... 이것이 가능할까요??
[guest]
2018-12-18 08시12분
[초보입니다] 혹은.. 바로가기 만드는 방법으로 바로가기를 생성했으나..

해당 생성 exe의 속성에 바로가기 탭에 파일경로가 다 들어나기에 해당 서버로 공유 스토리지가 사용자에게 들어납니다..

이것을 제어 할 수 있는 방법중 둘중 어느것이 더 좋을지 잘모르겠으나 방법 또한 찾기가 힘듭니다.. 도움을 주셧으면 감사하겠습니다
[guest]
2018-12-18 10시00분
죄송한데요, 질문의 의도를 전혀 모르겠습니다. 다시, 제가 전혀 모르는 사람이라고 생각하고 질문해 주세요.
정성태
2018-12-18 12시56분
[초보입니다] 죄송합니다.
일단 하고자하는 목표는 이렇습니다.
AD정책을 사용하여 exe 포터블 파일를 사용자에게 날리려 합니다.
로그온 스크립트 사용하여 해당 서버로부터 즉 batch 파일의 형식의 파일을 해당 cmd 명령어 copy로 USER로 전달하는 형식입니다.

해당 파일 크기도 크고 용량을 최대한 줄이기 위해서 바로가기를 만들어 바로가기 아이콘 탭의 바로가기 - 파일 위치 열기를 누르면 해당 서버의 공유 폴더에 접근이 되기에 우회 할 수 없도록 코딩을 하고싶은게 저의 목표입니다.

그래서 고안한게..

using IWshRuntimeLibrary;
using System;
using System.IO;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading.Tasks;


namespace ConsoleApp1
{
    class Program
    {
       // static string _createInk = Console.ReadLine();
       // static string _executeEXE = Console.ReadLine();
        static void Main(string[] args)
        {
            string a = @"C:\Documents and Settings\OSH\DESKTOP\lol_Test.lnk";
            //string a = $@"C:\Documents and Settings\OSH\DESKTOP\{_createInk}.lnk";
            if (!System.IO.File.Exists(a))
            {
                CreateEXE();
                ExecutionStartLocation();
            }
            else
            {
                ExecutionStartLocation();
            }
        }
        public static void CreateEXE()
        {
            Process[] processes = Process.GetProcessesByName("ConsoleApp1");
            foreach (var process in processes)
            {
                //dll, exe 모듈을 가져옵니다.
                ProcessModule mainModule = process.MainModule;
                string fileName = mainModule.FileName.ToString();

                string path = Environment.GetFolderPath(Environment.SpecialFolder.DesktopDirectory);
                var wsh = new WshShell();
                IWshRuntimeLibrary.IWshShortcut myShortcut;
                myShortcut = (IWshRuntimeLibrary.IWshShortcut)wsh.CreateShortcut(path + "/lol_Test.lnk");


                myShortcut.TargetPath = fileName; //@"C:\Users\OSH\source\repos\ConsoleApp1\ConsoleApp1\bin\Debug\ConsoleApp1.exe";
                myShortcut.Description = "osh test";
                myShortcut.IconLocation = @"%ProgramFiles(x86)%\Google\Chrome\Application\chrome.exe";
                //myShortcut.IconLocation = $@"D:\lol\LeagueClient.exe";
                //myShortcut.IconLocation = $@"D:\lol\{_executeEXE}.exe";
                //@"c:\reg\assign.ico";
                myShortcut.Save();
            }

            
        }
        public static void ExecutionStartLocation()
        {
            System.Diagnostics.Process ps = new System.Diagnostics.Process();
            //ps.StartInfo.FileName = @"D:\lol\LeagueClient.exe";
            ps.StartInfo.FileName = @"C:\Program Files (x86)\Google\Chrome\Application\chrome.exe";
            //ps.StartInfo.FileName = $@"D:\lol\{_executeEXE}.exe";
            ps.Start();
        }
    }
}

이러한 코딩이 되고.. 공유폴더로 접근하는 방법을 ps.StartInfo.FileName = @"D:\\AD-SEVER\test\LeagueClient.exe"; 이런식으로 접근함으로서도..
사용자에게 실제적 위치를 보여줄 수 없게 해놨습니다.
하지만 단점이 해당 ConsoleApp1.exe이라는 exe가 없으면 실행이 되지않기에.. 더좋은 방법이 없을까하고 질문드렸습니다.
[guest]
2018-12-18 01시16분
그러니까, 간단하게 정리해 보면.

test.exe를 실행하는 바로가기 아이콘을 만들어 놓으면 그 바로가기의 속성으로 test.exe를 찾을 수 있으니까, proxy.exe를 대신 사용자 컴퓨터에 로그온 스크립트로 다운로드하게 만들어 놓고 바로가기 아이콘은 그 proxy.exe를 가리키게 한 후 proxy.exe에서는 내부적으로 test.exe를 실행시키도록 (현재 그렇게) 만들었다는 거죠?

그런데 그 방법이 마음에 들지 않아 proxy.exe를 만들지 않고도 test.exe를 실행하는 바로가기 아이콘을 만들고 싶은데 test.exe임을 숨기는 방법이 없냐고 묻는 것이고요? 맞나요?
정성태
2018-12-18 01시31분
[초보입니다] 네네 정확합니다.
제가 워낙 초보자라 설명법이 너무 부족합니다 ㅠㅠ
[guest]
2018-12-18 02시17분
일단, 바로가기 아이콘으로는 해당 문제를 해결할 수 없습니다. 그렇긴 한데, 나름 재치있게 잘 하셨는데요, 딱히 더 나은 방법이 있을 것 같진 않습니다. 가령 mklink를 이용해 또 다른 symbolic link를 만드는 것도 가능하긴 하지만 그것 역시 속성 창을 통해 원래 파일의 위치가 알려집니다.
정성태
2018-12-18 02시29분
[초보입니다] 아하.. 그렇군요..
속성창을 사용자가 제어를 못하게 막던지..
혹은 기존 바로가기와 같은 기능을 만들어야하는데..
해당 C# 콘솔 어플리케이션 라이브러리에서는 ICON만 떠와서 만들 수 있는 기능없는거같고 바로가기를 만드는 과정이 그냥 있는거같아 두가지중 하나를 나름 해결해야보려 하는데.. 저도 여기까지가 한계인거같아 최대한으로 해보고 질문 드려본거라..
해당 작성된 콘솔 앱과 콘솔로 작성하여 만들어진 바로가기 exe를 같이 들고가는 방법으로 콘솔앱을 숨겨서 가져가는 방법도 있는거같은데..
해당 사항에 대해 윗 선임분한테 여쭤봐야할거같습니다..

다른 방법또한 있으신데 비슷하다 하니 참고한번해보겠습니다 ^^
[guest]

1  2  3  4  [5]  6  7  8  9  10  11  12  13  14  15  ...
NoWriterDateCnt.TitleFile(s)
13697정성태7/26/20243505닷넷: 2284. C# - async 메서드에서의 lock/Monitor.Enter/Exit 잠금 처리파일 다운로드1
13696정성태7/26/20243374오류 유형: 920. dotnet publish - error NETSDK1047: Assets file '...\obj\project.assets.json' doesn't have a target for '...'
13695정성태7/25/20243077닷넷: 2283. C# - Lock / Wait 상태에서도 STA COM 메서드 호출 처리파일 다운로드1
13694정성태7/25/20243579닷넷: 2282. C# - ASP.NET Core Web App의 Request 용량 상한값 (Kestrel, IIS)
13693정성태7/24/20243114개발 환경 구성: 717. Visual Studio - C# 프로젝트에서 레지스트리에 등록하지 않은 COM 개체 참조 및 사용 방법파일 다운로드1
13692정성태7/24/20243669디버깅 기술: 199. Windbg - 리눅스에서 뜬 닷넷 응용 프로그램 덤프 파일에 포함된 DLL의 Export Directory 탐색
13691정성태7/23/20243179디버깅 기술: 198. Windbg - 스레드의 Win32 Message Queue 정보 조회
13690정성태7/23/20242891오류 유형: 919. Visual C++ 리눅스 프로젝트 - error : ‘u8’ was not declared in this scope
13689정성태7/22/20243329디버깅 기술: 197. Windbg - PE 포맷의 Export Directory 탐색
13688정성태7/21/20243185닷넷: 2281. C# - Lock / Wait 상태에서도 일부 Win32 메시지 처리파일 다운로드1
13687정성태7/19/20243419닷넷: 2280. C# - PostThreadMessage로 보낸 메시지를 Windows Forms에서 수신하는 방법파일 다운로드1
13686정성태7/19/20243470오류 유형: 918. Visual Studio - ATL Simple Object 추가 시 error C2065: 'IDR_...': undeclared identifier
13685정성태7/19/20243559스크립트: 66. Windows 디렉터리 경로를 WSL의 /mnt 포맷으로 구하는 방법 - 두 번째 이야기
13684정성태7/19/20243274닷넷: 2279. C# - 문자열 보간식 사례
13683정성태7/18/20243435오류 유형: 917. ClrMD - Linux 환경의 .NET 5 덤프 분석 시 hang 현상
13682정성태7/18/20243326닷넷: 2278. WPF - 스레드에 종속되는 DependencyObject파일 다운로드1
13681정성태7/17/20243172닷넷: 2277. C# 13 - (2) 메서드 그룹의 자연 타입 개선 (메서드 추론 개선)파일 다운로드1
13680정성태7/16/20243111닷넷: 2276. C# - Method Group, Natural Type, function_type파일 다운로드1
13679정성태7/16/20242899Linux: 76. Linux - C++ (getaddrinfo 등을 담고 있는) libnss 정적 링크
13678정성태7/15/20242772VS.NET IDE: 191. Visual Studio 2022 - .NET 5 프로젝트를 Docker Support로 실행했을 때 오류
13677정성태7/15/20242634오류 유형: 916. MSBuild - CheckEolTargetFramework (warning NETSDK1138)
13676정성태7/14/20242789Linux: 75. gdb에서 glibc의 함수에 Breakpoint 걸기
13675정성태7/13/20243937C/C++: 166. C/C++ - DLL에서 template 함수를 export하는 방법 [1]파일 다운로드1
13674정성태7/13/20243181오류 유형: 915. Unhandled Exception: Microsoft.Diagnostics.NETCore.Client.ServerNotAvailableException: Unable to connect to Process
13673정성태7/11/20243335닷넷: 2275. C# 13 - (1) 신규 이스케이프 시퀀스 '\e'파일 다운로드1
13672정성태7/10/20243151닷넷: 2274. IIS - (프로세스 종료 없는) AppDomain Recycle
1  2  3  4  [5]  6  7  8  9  10  11  12  13  14  15  ...