문제

저는 모 조직에 속한 클럽의 웹사이트를 구축하고 있습니다.모 기관의 프로필 페이지에 있는 이미지를 내 페이지에 표시하기 위해 다운로드(리칭 ;)하는 중입니다.하지만 그 웹사이트의 배경은 멋진 흰색이고, 제 웹사이트의 배경은 멋진 회색 그라데이션입니다.이건 잘 어울리지 않네요.그래서 내 생각은 이미지를 내 서버에 저장하기 전에 편집하는 것이 었습니다.

저는 이미지를 향상시키기 위해 GDI+를 사용하고 있으며 Bitmap의 MakeTransparent 메서드를 사용하면 제대로 작동하고 예상된 작업을 수행하지만 여전히 흰색 jpeg 아티팩트가 곳곳에 남아 있습니다.아티팩트 때문에 이미지가 너무 나빠집니다. 이미지를 투명하게 만들지 않고 흰색으로 두는 것이 더 좋지만 내 웹사이트에서는 정말 보기 흉합니다.물론 항상 흰색 배경의 멋진 테두리에 있을 수 있지만 배경을 투명하게 변경하는 것이 좋습니다.

그래서 C#에서 간단한 JPEG 아티팩트를 제거할 수 있는지, 그리고 어떻게 제거할 수 있는지 궁금합니다.전에 이런 일을 해본 사람이 있나요?

시간 내 줘서 고마워.

예시 이미지:

TB-5404

변환된 이미지:

TB-5404 transformed

도움이 되었습니까?

해결책

글쎄요, 완벽과는 거리가 먼 것을 시도했지만 다른 사람에게는 유용할 수도 있다고 생각합니다.

나는 다음을 수행했습니다.

alt text

발생한 문제:그림자는 '오프 화이트'에서 충분히 멀리 떨어져 있어 자동 변환이 어렵고, 변환하더라도 그림자는 여전히 이미지 자체에 남아 있습니다.윗부분의 반짝임..허브의 경우 앤티앨리어싱 비트보다 황백색에 더 가깝습니다.이미지에는 기본 모서리에 연결되지 않은 흰색 점이 3~7개 있습니다.그리고 마지막으로 가장자리에 약간의 흰색이 남아 있습니다(코드를 조정하면 제거할 수 있지만 눈부심 상단 ​​부분을 제거하지 않고서는 제거할 수 없습니다).

C# 비효율적인 코드:

    static void Main()
    {
        Bitmap bmp=new Bitmap("test.jpg");

        int width = bmp.Width;
        int height = bmp.Height;
        Dictionary<Point, int> currentLayer = new Dictionary<Point, int>();
        currentLayer[new Point(0, 0)] = 0;
        currentLayer[new Point(width - 1, height - 1)] = 0;
        while (currentLayer.Count != 0)
        {
            foreach (Point p in currentLayer.Keys)
                bmp.SetPixel(p.X, p.Y, Color.Black);
            Dictionary<Point, int> newLayer = new Dictionary<Point, int>();
            foreach (Point p in currentLayer.Keys)
                foreach (Point p1 in Neighbors(p, width, height))
                    if (Distance(bmp.GetPixel(p1.X, p1.Y), Color.White) < 40)
                        newLayer[p1] = 0;
            currentLayer = newLayer;
        }

        bmp.Save("test2.jpg");
    }

    static int Distance(Color c1, Color c2)
    {
        int dr = Math.Abs(c1.R - c2.R);
        int dg = Math.Abs(c1.G - c2.G);
        int db = Math.Abs(c1.B - c2.B);
        return Math.Max(Math.Max(dr, dg), db);
    }

    static List<Point> Neighbors(Point p, int maxX, int maxY)
    {
        List<Point> points=new List<Point>();
        if (p.X + 1 < maxX) points.Add(new Point(p.X + 1, p.Y));
        if (p.X - 1 >= 0) points.Add(new Point(p.X - 1, p.Y));
        if (p.Y + 1 < maxY) points.Add(new Point(p.X, p.Y + 1));
        if (p.Y - 1 >= 0) points.Add(new Point(p.X, p.Y - 1));
        return points;
    }

코드는 두 지점으로 시작하여 작동합니다.검은색으로 설정한 다음 근처에 흰색 근처에 있는 이웃이 있는지 확인합니다.만약 그렇다면 목록에 추가되어 실행됩니다.결국에는 변경할 흰색 픽셀이 부족해집니다.

대안으로 흰색 배경을 사용하도록 사이트를 다시 디자인하는 것을 고려할 수도 있습니다.

다른 팁

이미지의 각 픽셀을 반복합니다. R, G 및 B가 230보다 높으면 색상을 원하는 색상(또는 투명)으로 바꿉니다.어쩌면 이전 색상이 '진짜' 흰색과 얼마나 멀리 떨어져 있는지에 따라 새 색상에 가중치를 부여할 수도 있습니다.

실제 이미지도 흰색이면 문제가 발생할 수 있습니다. 그렇지 않으면 회색 폭풍우 조종사가 됩니다. :)

이 작업을 100% 정확도로 자동으로 수행할 수는 없습니다.

그 이유는 당신이 가지고 있는 유일한 정보는 당신이 알고 있는 색상뿐이기 때문입니다. 일부 이미지의 픽셀이 잘 섞이려고 시도하고 있습니다.이미지의 일부 픽셀만 실제로 배경에 음영을 적용하기 위해 이 값 또는 그에 가까운 색상을 사용하고, 표현된 실제 개체가 실제로 흰색이기 때문에 다른 픽셀에서는 (흰색의 경우) 사용하게 됩니다(정확도가 빌어먹을) 이 제국 폭풍 기병).

어느 것이 흥미로운 문제 도메인인지 감지하는 일종의 정교한 기계 학습은 재미있는 프로젝트일 수 있지만 확실히 즉각적인 문제에 대한 빠른 해결책을 제공하지는 않습니다.

또 다른 문제는 배경과 혼합하려고 시도하는 이미지 영역을 좋은 신뢰성으로 감지할 수 있더라도 해당 영역을 '혼합 해제'한 다음 색상이 일치하지 않는 한 새 배경색으로 다시 혼합하는 데 문제가 있다는 것입니다. 합리적으로 호환됩니다.이 경우 회색은 흰색과 같이 스펙트럼이 넓은 색상이므로 효과가 있을 수 있습니다.

사용하려는 기술은 다음과 같습니다.

  • 플러드 채우기 알고리즘을 사용하여 이미지 가장자리에서 안쪽으로 알려진 배경 색상의 x%(1) 내에 있는 모든 픽셀을 선택합니다.
  • 해당 픽셀의 경우 알파 채널을 원래 색상과 일치하는 비율의 값으로 설정하고 이와 관련된 색상 경향성을 제거합니다.
    • 따라서 배경이 RGB 값 A, B, C이고 픽셀이 A+5, B, C-7이면 결과는 RGBA 5,0,0, ((A+B+C-7)/(A+입니다. B+C)*256) (1)
  • 새로운 배경 색상의 페인 스퀘어 위에 이 알파 블렌딩 이미지를 합성합니다.
  • 알파 채널이 없는 결과를 새 이미지로 렌더링합니다.

색상이 두 배경색에 가까운 개체의 경우 여전히 문제가 있습니다.* 원본의 경우 객체의 존재를 암시하기 위해 그림자가 사용되는 것일 수 있습니다. 따라서 플러드 필이 이미지 내부를 '침투'하게 됩니다.* 후자의 경우 결과 이미지는 개체의 선명도를 잃게 되며 개체가 끝나고 배경이 끝나는 위치를 나타내는 미묘한 음영, 하이라이트 또는 일반 선만 표시됩니다.

이는 매우 대략적인 첫 번째 근사치이지만 목표의 합리적인 비율을 포함할 수 있습니다.투명하고 완전히 밀폐된 구멍이 있는 사진(예: 외부 아치의 간격)은 알고리즘이 화이트홀과 화이트 스톰트루퍼를 구별할 수 없기 때문에 자동 방식으로 제대로 작동하지 않을 것입니다.
알고리즘이 재혼합할 그림의 영역을 강조 표시하고 포함/제외할 영역을 간단히 선택할 수 있도록 할 수 있습니다(원하는 경우 이를 수행하는 방법의 예로 Pain.Net의 마술 지팡이 선택 도구를 사용함). 더 적은 초기 노력으로 픽셀당 간단한 선택이 가능하도록 화려하게 만듭니다.


  1. x 값은 사용자가 조정하는 값입니다. 이미지의 일부 측면(예: 배경색에 가까운 이미지의 비율)을 기반으로 자동으로 조정할 수 있습니다.
  2. 이 수식은 흰색에 가까운 색상을 가정합니다. 검정색에 가까우면 반전시키려는 것입니다.

댓글 대화 상자를 기반으로 한 또 다른 접근 방식은 다음과 같습니다.

static void Main()
{
    Bitmap mask = new Bitmap(@"mask.bmp");
    Bitmap bmp=new Bitmap(@"test.jpg");
    int width = bmp.Width;
    int height = bmp.Height;

    for(int x=0; x<width; x++)
        for (int y = 0; y < height; y++)
            if (mask.GetPixel(x, y).R < 250)
                bmp.SetPixel(x,y,mask.GetPixel(x,y));
    bmp.Save(@"test3.jpg");
}

주어진 마스크:

alt text

결과는 다음과 같습니다.

alt text

안티 앨리어싱이 비활성화된 Paint.NET에서 마스크 테두리가 약간 정리되었습니다.다시 말하지만, 어떤 테두리가 사용되고 있는지 식별할 수 있는 경우에만 적용 가능합니다.근데 이쁘게 나왔어...그린 빼고..

미색의 색조도 처리해야 합니다.아마도 처음에 iamge를 만들고 배경색을 설정할 때 약간의 앤티앨리어싱이 있었고 jpg로 저장하면 모든 색상이 완벽하게 유지되지는 않을 것입니다.따라서 특정 색상을 투명하게 만드는 경우 해당 색상의 모든 음영을 얻을 수 없어 많은 아티팩트가 남게 됩니다.색상이 주요 색상에 얼마나 가까운지에 비례하여 투명도를 제공하는 것이 필요합니다.이는 Photoshop과 같은 배치 스크립트로 수행하는 것이 더 쉬울 수 있지만 이것이 실시간으로 수행해야 하는 작업인지는 모르겠습니다.

이 문제를 프로그래밍 방식으로 처리할 수 있는 (원격적으로 쉬운) 방법은 없습니다.이미지 가장자리 주변의 흰색 아티팩트 영역은 흰색에 가깝지만 완전하지는 않은 픽셀의 결과이므로 투명도 효과를 얻을 수 없습니다.마스크/커피 머그에도 순백색 반점이 몇 군데 있어서 투명해져서 회색이 됩니다.

가장 좋은 방법은 원본 사이트의 웹마스터에게 연락하여 원본 이미지를 Photoshop이나 원본 레이어가 별도로 보존되는 다른 형식으로 보낼 수 있는지 확인하는 것입니다.그런 다음 원본 투명도(PNG 등)를 유지하는 형식으로 이미지를 다시 생성하거나 배경에 그라디언트를 사용할 수 있습니다. 그림이 렌더링되는 그라데이션).

당신이 제안한대로 이미지 주위에 일종의 테두리를 사용하겠습니다.

답변해 주셔서 감사합니다..모두 좋은 제안입니다..

어제 제가 요리한 내용은 다음과 같습니다.

public const int PIXEL_REGION = 60;
public const int TRANSPARENT_DISTANCE = 60;
public static readonly Color TRANSPARENT_COLOR = Color.White;

private System.Drawing.Image ProcessImage(System.Drawing.Image image)
{
    Bitmap result = new Bitmap(image.Width, image.Height);

    Bitmap workImage = new Bitmap(image);

    for (int x = 0; x < image.Width; x++)
    {
        for (int y = 0; y < image.Height; y++)
        {
            Color color = workImage.GetPixel(x, y);

            if (x < PIXEL_REGION || x > image.Width - PIXEL_REGION || y < PIXEL_REGION || y > image.Height - PIXEL_REGION)
            {
                double distance = CalculateColourDistance(TRANSPARENT_COLOR, color);

                if(distance < TRANSPARENT_DISTANCE)
                {
                    result.SetPixel(x, y, Color.Transparent);
                    continue;
                }
            }

            result.SetPixel(x, y, color);
        }
    }

    return result;
}

private double CalculateColourDistance(Color c1, Color c2)
{
    int a = c2.A - c1.A;
    int r = c2.R - c1.R;
    int g = c2.G - c1.G;
    int b = c2.B - c1.B;

    return Math.Sqrt(Math.Pow(a, 2) + Math.Pow(r, 2) + Math.Pow(g, 2) + Math.Pow(b, 2));
}

책에서 가장 아름다운 코드는 아니지만 그 기능은 픽셀 영역의 모든 픽셀에 대해 Color.White와 내 색상 사이의 거리를 계산하고 미리 정의된 거리보다 낮으면 투명하게 색상이 지정됩니다. ..

이 코드 조각은 다음과 같은 결과를 생성합니다.

TB-5404 transformed v 2.0

나쁘지 않아요..하지만 여전히 짜증나 :)

성공하면 여러분에게도 공유할 비결이 하나 있습니다...

또 다른 실패한 시도가 있습니다.)

아이디어가 있었는데..영화는 녹색 화면을 사용하는데, 내 이미지에 녹색 오버레이를 사용하는 것은 어떨까요..오버레이는 다음과 같습니다.

Green overlay with transparent inner thing

결과는 다음과 같습니다.

Green stuff all over the place

그래서 PNG 압축에 대한 또 다른 귀중한 교훈을 배웠습니다..bmp로도 해봤는데 어쩐지 항상 녹색 테두리가 나오네요..그것을 지우려고 노력할 수는 있지만 흰색 배경에 그대로 두고 주위에 어리석은 테두리를 배치한 다음 끝내야 할 것 같습니다...

아 글쎄..;)

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top