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]

... [16]  17  18  19  20  21  22  23  24  25  26  27  28  29  30  ...
NoWriterDateCnt.TitleFile(s)
13238정성태1/31/20235896.NET Framework: 2092. IIS 웹 사이트를 TLS 1.2 또는 TLS 1.3 프로토콜로만 운영하는 방법
13237정성태1/30/20235589.NET Framework: 2091. C# - 웹 사이트가 어떤 버전의 TLS/SSL을 지원하는지 확인하는 방법
13236정성태1/29/20235133개발 환경 구성: 663. openssl을 이용해 인트라넷 IIS 사이트의 SSL 인증서 생성
13235정성태1/29/20234696개발 환경 구성: 662. openssl - 윈도우 환경의 명령행에서 SAN 적용하는 방법
13234정성태1/28/20235788개발 환경 구성: 661. dnSpy를 이용해 소스 코드가 없는 .NET 어셈블리의 코드를 변경하는 방법 [1]
13233정성태1/28/20237173오류 유형: 840. C# - WebClient로 https 호출 시 "The request was aborted: Could not create SSL/TLS secure channel" 예외 발생
13232정성태1/27/20234928스크립트: 43. uwsgi의 --processes와 --threads 옵션
13231정성태1/27/20233885오류 유형: 839. python - TypeError: '...' object is not callable
13230정성태1/26/20234247개발 환경 구성: 660. WSL 2 내부로부터 호스트 측의 네트워크로 UDP 데이터가 1개의 패킷으로만 제한되는 문제
13229정성태1/25/20235268.NET Framework: 2090. C# - UDP Datagram의 최대 크기
13228정성태1/24/20235358.NET Framework: 2089. C# - WMI 논리 디스크가 속한 물리 디스크의 정보를 얻는 방법 [2]파일 다운로드1
13227정성태1/23/20235046개발 환경 구성: 659. Windows - IP MTU 값을 바꿀 수 있을까요? [1]
13226정성태1/23/20234740.NET Framework: 2088. .NET 5부터 지원하는 GetRawSocketOption 사용 시 주의할 점
13225정성태1/21/20233941개발 환경 구성: 658. Windows에서 실행 중인 소켓 서버를 다른 PC 또는 WSL에서 접속할 수 없는 경우
13224정성태1/21/20234343Windows: 221. Windows - Private/Public/Domain이 아닌 네트워크 어댑터 단위로 방화벽을 on/off하는 방법
13223정성태1/20/20234523오류 유형: 838. RDP 연결 오류 - The two computers couldn't connect in the amount of time allotted
13222정성태1/20/20234217개발 환경 구성: 657. WSL - DockerDesktop.vhdx 파일 위치를 옮기는 방법
13221정성태1/19/20234416Linux: 57. C# - 리눅스 프로세스 메모리 정보파일 다운로드1
13220정성태1/19/20234520오류 유형: 837. NETSDK1045 The current .NET SDK does not support targeting .NET ...
13219정성태1/18/20234094Windows: 220. 네트워크의 인터넷 접속 가능 여부에 대한 판단 기준
13218정성태1/17/20234018VS.NET IDE: 178. Visual Studio 17.5 (Preview 2) - 포트 터널링을 이용한 웹 응용 프로그램의 외부 접근 허용
13217정성태1/13/20234630디버깅 기술: 185. windbg - 64비트 운영체제에서 작업 관리자로 뜬 32비트 프로세스의 덤프를 sos로 디버깅하는 방법
13216정성태1/12/20234874디버깅 기술: 184. windbg - 32비트 프로세스의 메모리 덤프인 경우 !peb 명령어로 나타나지 않는 환경 변수
13215정성태1/11/20236526Linux: 56. 리눅스 - /proc/pid/stat 정보를 이용해 프로세스의 CPU 사용량 구하는 방법 [1]
13214정성태1/10/20235990.NET Framework: 2087. .NET 6부터 SourceGenerator와 통합된 System.Text.Json [1]파일 다운로드1
13213정성태1/9/20235478오류 유형: 836. docker 이미지 빌드 시 "RUN apt install ..." 명령어가 실패하는 이유
... [16]  17  18  19  20  21  22  23  24  25  26  27  28  29  30  ...