Bug 149578 - libjpeg JPEG subsampling setting is not user-controlable
Summary: libjpeg JPEG subsampling setting is not user-controlable
Status: RESOLVED FIXED
Alias: None
Product: digikam
Classification: Applications
Component: Plugin-DImg-JPEG (show other bugs)
Version: 0.9.1
Platform: Ubuntu Linux
: NOR wishlist
Target Milestone: ---
Assignee: Digikam Developers
URL:
Keywords:
Depends on:
Blocks:
 
Reported: 2007-09-05 21:01 UTC by Jean-Marc Liotier
Modified: 2017-08-02 06:37 UTC (History)
0 users

See Also:
Latest Commit:
Version Fixed In: 0.9.3


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description Jean-Marc Liotier 2007-09-05 21:01:38 UTC
Version:           0.9.1 (using KDE KDE 3.5.6)
Installed from:    Ubuntu Packages
OS:                Linux

This but is basically a reopening of bug #130996. Bug #130996 has been marked as a duplicate of #123649 but it was not the case : #123649 specifically called for the inclusion of the JPEG "quality" factor (an integer between 0 and 100) into the "save as" dialog whereas #130996 also asked for other controls including subsampling.

#130996 expressed exactly what I felt, so I am going to quote it :

"I am really interested in having easy control over Subsampling.  This option was originally in jpeg to help further compress large files by averaging out similar colors, in able to reduce the jpeg file size.  The default value for libjpeg is 2x2, 1x1, 1,1 .  Digikam currently does not set this option, and therefore uses this default.  Breifly, this default option throws away half of the color information, perhaps great for vidio compression, but not for todays high-quality color photography".

Just like the reporter of #130996 I come from a Gimp background and I miss control over subsampling for the same reasons.

You can take a look at Gimp's "save as" JPEG dialog and you will find everything. I understand that Digikam's goal is to be as simple as possible, but JPEG subsampling is not a needlessly complicated tweak : it is the second most important parameter in controlling JPEG's quality.
Comment 1 caulier.gilles 2007-09-07 14:20:39 UTC
Jean Marc,

I have take a look into Gimp source code. following chroma subsampling options are available :

2x2 1x1 1x1
2x1 1x1 1x1
1x1 1x1 1x1

Following WikiPedia page : http://en.wikipedia.org/wiki/Chroma_subsampling

Where are these options ??? This is another non homogenous stuff in Gimp...

Also, "2x2 1x1 1x1" and others are not really easy to understand for common users. Something like "no subsampling" (best quality), "medium subsampling"  (medium quality), and  "high subsampling" (low quality) is more user friendly.

I willpatch svn to provide 3 options from gimp, but using ratio subsampling value as option label. I'm waiting your tips to improve this point...

Gilles Caulier



Comment 2 caulier.gilles 2007-09-07 14:26:57 UTC
Jean Marc,

Note than from gimp source code, "1x1 1x1 1x1" is 4:2:2. Still:

- "2x1 1x1 1x1"
- "2x2 1x1 1x1"

It's not really clear for me... Somebody understand the gimp logic (@^!...) ???

Gilles 
Comment 3 Arnd Baecker 2007-09-07 14:33:35 UTC
Maybe this helps a bit:
http://www.ftgimp.com/help/C/filters/jpeg.html

This one is interesting:
http://www.mail-archive.com/gimp-user@lists.xcf.berkeley.edu/msg13130.html
Comment 4 Arnd Baecker 2007-09-07 14:40:14 UTC
> Note than from gimp source code, "1x1 1x1 1x1" is 4:2:2. Still:


In this text
http://photo.net/bboard/q-and-a-fetch-msg?msg_id=009Heq
Bill Tuthill writes:
"""GIMP allows users to select all three types of chroma subsampling:
1x1,1x1,1x1 (ImageMagick 1x1 AKA 4:4:4), 2x1,1x1,1x1 (AKA 4:2:2), and
smallest 2x2,1x1,1x1 (ImageMagick 2x1 AKA 4:1:1)."""

http://photo.net/learn/jpeg/ one also provides a lot of info...
Comment 5 caulier.gilles 2007-09-07 14:43:32 UTC
Jean Marc,

The gimp doc is wrong !!!

Look page 70 of http://docs.gimp.org/en.pdf We can read :

// -----------------------------------------------------------

Subsampling Human eye is not sensitive in the same way all over color spectrum. Compression can use this to consider
    as identical slightly different colors. Three methods are available :
    • 2x2, 1x1, 1x1 : important compression; suits images with weak borders but tends to denature colors.
    • 1x1, 1x1, 1x1 : preserves borders and contrasting colors, but compression is less.
    • 1x1, 1x1, 1x1 (4:2:2) : between other both.

// -----------------------------------------------------------

There are both "1x1, 1x1, 1x1" (:=(((

After investiguations using wikipedia page, i will set in digiKam :

"High chroma subsampling" ==> 2x2 1x1 1x1
"Medium chroma subsampling" ==> 2x1 1x1 1x1 (4:2:2)
"Low chroma subsampling" ==> 1x1 1x1 1x1

I will set the "Low chroma subsampling as default value because we always want to preserve the _best_ image quality. If user want a lower quality, it must change it in settings.

Gilles Caulier



Comment 6 Arnd Baecker 2007-09-07 15:14:58 UTC
Gilles,

note that this might cause confusion ;-)
"Low chroma subsampling"  --> high quality
"High chroma subsampling"  --> low quality
It is not unlikely to always think wrong here ;-)

Maybe what you suggested before 
"no subsampling (best quality)", "medium subsampling  (medium quality)", and  "high subsampling (low quality)"
is better? 
Comment 7 Jean-Marc Liotier 2007-09-07 15:38:09 UTC
Sorry for coming late...

Indeed you spotted an error in the Gimp documentation. I'll try to notify a maintainer.
Comment 8 Jean-Marc Liotier 2007-09-07 15:39:07 UTC
A comment by Thom Sharp on the Photo.net article gives interesting clarification of the origins and uses of various subsampling settings. I think it summarizes it quite well :

"Within the professional cinematography, video, and imaging industries, chroma subsampling is expressed using the Y:Cr:Cb notation. This notation was originally devised to convey the horizontal (not vertical) resolution ratios of the Cr (red) and Cb (blue) channels to the horizontal resolution of Y (luminance). 4:4:4, therefore, indicates the absence of horizontal subsampling (1:1), while the 4:2:2 and 4:1:1 formats indicate that the Cr and Cb channels each horizontally boast 50% (4:2:2), and 25% (4:1:1), of Y's horizontal resolution.

While less common, a 4:2:1 format also exists, yielding 2:1 horizontal Cr resolution vs. Y, and 4:1 horizontal Cb resolution vs. Y. This exploits the lower sensitivity to blue than red definition in human vision.

Unfortunately, the Y:Cr:Cb notation often causes confusion because it was "hacked" after its introduction to express vertical subsampling levels in addition to horizontal ones. For example, while 4:2:0 seems to suggest 2:1 Cr vs. Y horizontally and the total lack of Cb, it actually indicates that both Cr and Cb are present, and that each is subsampled 2:1 vs. Y horizontally *and* vertically. The reason this "makes sense" is because, in vertical subsampling, only one of the two chroma subchannels is sampled per horizontal line (co-siting). Therefore, scanline 1 contains only Cr at 50% of Y's horizontal resolution (4:2:0), while scanline 2 contains only Cb at 50% of Y's horizontal resolution (4:0:2), etc. Upon decoding, Cr and Cb are vertically interpolated to form a vertical average. But in encoded form, it's literally "4:2:0".

The 4:1:0 format uses the same technique, whereby Cr and Cb are alternately present (2:1 averaged vertical chroma resolution), and each boasts 25% (4:1) of the horizontal resolution of Y.

Summary:
- 4:4:4 (PSP 1x1 1x1 1x1) - CrH 100% - CbH 100% - CrV 100% - CbV 100%
- 4:2:2 (PSP 2x1 1x1 1x1) - CrH 50% - CbH 50% - CrV 100% - CbV 100%
- 4:1:1 (PSP 4x1 1x1 1x1) - CrH 25% - CbH 25% - CrV 100% - CbV 100%
- 4:2:0 (PSP 2x2 1x1 1x1) - CrH 50% - CbH 50% - CrV 50% - CbV 50%
- 4:1:0 (PSP 4x2 1x1 1x1) - CrH 25% - CbH 25% - CrV 50% - CbV 50% 

Uses :
- 4:4:4 - High-end imaging/cinematography processing, high quality JPEG (used by Photoshop's higher quality levels)
- 4:2:2 - D-1, D-5, D-9 (a.k.a. Digital-S), Ampex DCT, Digital Betacam, Betacam SX, DVCPRO-50, studio/high profile MPEG-2, good quality JPEG
- 4:2:0 - PAL DV, PAL DVCAM, main profile MPEG-2 (e.g. DVD, American SDTV and HDTV, SD and HD DVB), average quality JPEG (used by Photoshop's lower quality levels)
- 4:1:1 - D-7, NTSC DV, NTSC DVCAM, DVCPRO, low quality JPEG
- 4:1:0 - Some low-bandwidth video/image codecs (worse than VHS chroma)
Comment 9 Jean-Marc Liotier 2007-09-07 15:52:22 UTC
Canon cameras (and many other manufacturers) use 2:1 chroma subsampling when converting in-camera from RAW to JPEG, so the default setting should not be higher than 4:2:2 since a higher setting would merely increase file size with no gain of quality.

Additionally, 4:2:2 is widely considered as a medium quality JPEG so that could be a good default.
Comment 10 Jean-Marc Liotier 2007-09-07 15:55:54 UTC
I second Gille's suggestion of three quality levels :

"High chroma subsampling" ==> 2x2 1x1 1x1
"Medium chroma subsampling" ==> 2x1 1x1 1x1 (4:2:2)
"Low chroma subsampling" ==> 1x1 1x1 1x1 

But I would choose "Medium chroma subsampling" as the default since a lower subsampling is overkill for most digital photographers, including the immense majority of DSLR users and an even higher proportion of Digikam users.
Comment 11 Jean-Marc Liotier 2007-09-07 16:11:23 UTC
Gimp documentation bug submitted at http://bugzilla.gnome.org/show_bug.cgi?id=474564
Comment 12 caulier.gilles 2007-09-07 16:11:29 UTC
SVN commit 709439 by cgilles:

digiKam from KDE3 branch : provide settings to adjust JPEG subsampling factor when new JPEG file is created by editor.
TODO: KDE4PORT
CCBUGS: 149578



 M  +44 -6     libs/dimg/loaders/jpegloader.cpp  
 M  +37 -4     libs/dimg/loaders/jpegsettings.cpp  
 M  +3 -0      libs/dimg/loaders/jpegsettings.h  
 M  +2 -1      libs/widgets/common/filesaveoptionsbox.cpp  
 M  +10 -10    project/project.kdevelop  
 M  +5 -2      utilities/imageeditor/canvas/dimginterface.cpp  
 M  +4 -0      utilities/imageeditor/canvas/iofilesettingscontainer.h  
 M  +2 -0      utilities/imageeditor/editor/editorwindow.cpp  
 M  +2 -0      utilities/setup/setupiofiles.cpp  


--- branches/extragear/kde3/graphics/digikam/libs/dimg/loaders/jpegloader.cpp #709438:709439
@@ -503,19 +503,57 @@
 
     QVariant qualityAttr = imageGetAttribute("quality");
     int quality = qualityAttr.isValid() ? qualityAttr.toInt() : 90;
-    
+
     if (quality < 0)
         quality = 90;
     if (quality > 100)
         quality = 100;
-    
+
+    QVariant subSamplingAttr = imageGetAttribute("subsampling");
+    int subsampling = subSamplingAttr.isValid() ? subSamplingAttr.toInt() : 1;  // Medium
+   
     jpeg_set_defaults(&cinfo);
 
-    // B.K.O #130996: set horizontal and vertical Subsampling factor to 1 for a best 
-    // quality of color picture compression. 
-    cinfo.comp_info[0].h_samp_factor = 1;
-    cinfo.comp_info[0].v_samp_factor = 1; 
+    // B.K.O #149578: set horizontal and vertical chroma subsampling factor to encoder.
+    // See this page for details: http://en.wikipedia.org/wiki/Chroma_subsampling
 
+    switch (subsampling)
+    {
+        case 1:  // 2x1, 1x1, 1x1 (4:2:2) : Medium
+        {
+            DDebug() << "Using LibJPEG medium chroma-subsampling (4:2:2)" << endl;
+            cinfo.comp_info[0].h_samp_factor = 2;
+            cinfo.comp_info[0].v_samp_factor = 1;
+            cinfo.comp_info[1].h_samp_factor = 1;
+            cinfo.comp_info[1].v_samp_factor = 1;
+            cinfo.comp_info[2].h_samp_factor = 1;
+            cinfo.comp_info[2].v_samp_factor = 1;
+            break;
+        }
+        case 2:  // 2x2, 1x1, 1x1 (4:1:1) : High
+        {
+            DDebug() << "Using LibJPEG high chroma-subsampling (4:1:1)" << endl;
+            cinfo.comp_info[0].h_samp_factor = 2;
+            cinfo.comp_info[0].v_samp_factor = 2;
+            cinfo.comp_info[1].h_samp_factor = 1;
+            cinfo.comp_info[1].v_samp_factor = 1;
+            cinfo.comp_info[2].h_samp_factor = 1;
+            cinfo.comp_info[2].v_samp_factor = 1;
+            break;
+        }
+        default:  // 1x1 1x1 1x1 (4:4:4) : None
+        {
+            DDebug() << "Using LibJPEG none chroma-subsampling (4:4:4)" << endl;
+            cinfo.comp_info[0].h_samp_factor = 1;
+            cinfo.comp_info[0].v_samp_factor = 1;
+            cinfo.comp_info[1].h_samp_factor = 1;
+            cinfo.comp_info[1].v_samp_factor = 1;
+            cinfo.comp_info[2].h_samp_factor = 1;
+            cinfo.comp_info[2].v_samp_factor = 1;
+            break;
+        }
+    }
+
     jpeg_set_quality(&cinfo, quality, true);
     jpeg_start_compress(&cinfo, true);
 
--- branches/extragear/kde3/graphics/digikam/libs/dimg/loaders/jpegsettings.cpp #709438:709439
@@ -27,6 +27,7 @@
 #include <qlabel.h>
 #include <qlayout.h>
 #include <qwhatsthis.h>
+#include <qcombobox.h>
 
 // KDE includes.
 
@@ -54,12 +55,16 @@
         labelJPEGcompression = 0;
         JPEGcompression      = 0;
         labelWarning         = 0;
+        subSamplingCB        = 0;
     }
 
     QGridLayout  *JPEGGrid;
 
     QLabel       *labelJPEGcompression;
+    QLabel       *labelSubSampling;
 
+    QComboBox    *subSamplingCB;
+
     KActiveLabel *labelWarning;
 
     KIntNumInput *JPEGcompression;
@@ -94,11 +99,30 @@
     d->labelWarning->setLineWidth(1);
     d->labelWarning->setFrameShape(QFrame::Box);
 
+    d->labelSubSampling = new QLabel(i18n("Chroma subsampling:"), this);
+
+    d->subSamplingCB = new QComboBox(false, this);
+    d->subSamplingCB->insertItem(i18n("None"));    // 1x1, 1x1, 1x1 (4:4:4)
+    d->subSamplingCB->insertItem(i18n("Medium"));  // 2x1, 1x1, 1x1 (4:2:2)
+    d->subSamplingCB->insertItem(i18n("High"));    // 2x2, 1x1, 1x1 (4:1:1)
+    QWhatsThis::add(d->subSamplingCB, i18n("<p>The level of chroma subsampling for JPEG images:<p>"
+                                           "<b>None</b>: use 4:4:4 ratio. It does not have any chroma "
+                                           "subsampling at all. This preserves borders and contrasting "
+                                           "colors, but compression is less<p>"
+                                           "<b>Medium</b>: use 4:2:2 ratio. Medium compression; reduces "
+                                           "the bandwidth for the picture by one-third with little to "
+                                           "no visual difference<p>"
+                                           "<b>High</b>: use 4:1:1 ratio. Important compression; suits "
+                                           "images with weak borders but tends to denature colors<p>"
+                                           "<b>Note: JPEG use a lossy compression image algorithm.</b>"));
+
     d->JPEGGrid->addMultiCellWidget(d->labelJPEGcompression, 0, 0, 0, 0);
-    d->JPEGGrid->addMultiCellWidget(d->JPEGcompression, 0, 0, 1, 1);
-    d->JPEGGrid->addMultiCellWidget(d->labelWarning, 0, 0, 2, 2);    
+    d->JPEGGrid->addMultiCellWidget(d->JPEGcompression,      0, 0, 1, 1);
+    d->JPEGGrid->addMultiCellWidget(d->labelSubSampling,     1, 1, 0, 0);    
+    d->JPEGGrid->addMultiCellWidget(d->subSamplingCB,        1, 1, 1, 1);    
+    d->JPEGGrid->addMultiCellWidget(d->labelWarning,         0, 1, 2, 2);    
     d->JPEGGrid->setColStretch(1, 10);
-    d->JPEGGrid->setRowStretch(1, 10);
+    d->JPEGGrid->setRowStretch(2, 10);
 }
 
 JPEGSettings::~JPEGSettings()
@@ -116,5 +140,14 @@
     return d->JPEGcompression->value();
 }
 
+void JPEGSettings::setSubSamplingValue(int val)
+{
+    d->subSamplingCB->setCurrentItem(val);
+}
+
+int JPEGSettings::getSubSamplingValue()
+{
+    return d->subSamplingCB->currentItem();
+}
+
 }  // namespace Digikam
-
--- branches/extragear/kde3/graphics/digikam/libs/dimg/loaders/jpegsettings.h #709438:709439
@@ -49,6 +49,9 @@
     void setCompressionValue(int val);
     int  getCompressionValue();
 
+    void setSubSamplingValue(int val);
+    int  getSubSamplingValue();
+
 private:
 
     JPEGSettingsPriv* d;
--- branches/extragear/kde3/graphics/digikam/libs/widgets/common/filesaveoptionsbox.cpp #709438:709439
@@ -159,6 +159,7 @@
     KConfig* config = kapp->config();
     config->setGroup("ImageViewer Settings");
     config->writeEntry("JPEGCompression", d->JPEGOptions->getCompressionValue());
+    config->writeEntry("JPEGSubSampling", d->JPEGOptions->getSubSamplingValue());
     config->writeEntry("PNGCompression", d->PNGOptions->getCompressionValue());
     config->writeEntry("TIFFCompression", d->TIFFOptions->getCompression());
     config->writeEntry("JPEG2000Compression", d->JPEG2000Options->getCompressionValue());
@@ -171,6 +172,7 @@
     KConfig* config = kapp->config();
     config->setGroup("ImageViewer Settings");
     d->JPEGOptions->setCompressionValue( config->readNumEntry("JPEGCompression", 75) );
+    d->JPEGOptions->setCompressionValue( config->readNumEntry("JPEGSubSampling", 1) );  // Medium subsampling
     d->PNGOptions->setCompressionValue( config->readNumEntry("PNGCompression", 9) );
     d->TIFFOptions->setCompression(config->readBoolEntry("TIFFCompression", false));
     d->JPEG2000Options->setCompressionValue( config->readNumEntry("JPEG2000Compression", 75) );
@@ -178,4 +180,3 @@
 }
 
 }  // namespace Digikam
-
--- branches/extragear/kde3/graphics/digikam/project/project.kdevelop #709438:709439
@@ -12,10 +12,10 @@
     </keywords>
     <projectdirectory>./</projectdirectory>
     <absoluteprojectpath>false</absoluteprojectpath>
-    <description/>
+    <description></description>
     <ignoreparts/>
     <projectname>digikam</projectname>
-    <defaultencoding/>
+    <defaultencoding></defaultencoding>
   </general>
   <kdevfileview>
     <groups>
@@ -75,10 +75,10 @@
     <general>
       <dbgshell>libtool</dbgshell>
       <programargs/>
-      <gdbpath/>
-      <configGdbScript/>
-      <runShellScript/>
-      <runGdbScript/>
+      <gdbpath></gdbpath>
+      <configGdbScript></configGdbScript>
+      <runShellScript></runShellScript>
+      <runGdbScript></runGdbScript>
       <breakonloadinglibs>true</breakonloadinglibs>
       <separatetty>false</separatetty>
       <floatingtoolbar>false</floatingtoolbar>
@@ -113,12 +113,12 @@
     <run>
       <directoryradio>build</directoryradio>
       <customdirectory>/</customdirectory>
-      <mainprogram>digikam/digikam/digikam</mainprogram>
-      <programargs/>
+      <mainprogram>/home/gilles/Documents/Devel/SVN/branches/graphics/digikam/digikam/digikam</mainprogram>
+      <programargs></programargs>
       <terminal>false</terminal>
       <autocompile>false</autocompile>
       <envvars/>
-      <globaldebugarguments/>
+      <globaldebugarguments></globaldebugarguments>
       <globalcwd>../digikam</globalcwd>
       <useglobalprogram>true</useglobalprogram>
       <autoinstall>false</autoinstall>
@@ -192,7 +192,7 @@
       <includePaths>.;</includePaths>
     </codecompletion>
     <creategettersetter>
-      <prefixGet/>
+      <prefixGet></prefixGet>
       <prefixSet>set</prefixSet>
       <prefixVariable>m_,_</prefixVariable>
       <parameterName>theValue</parameterName>
--- branches/extragear/kde3/graphics/digikam/utilities/imageeditor/canvas/dimginterface.cpp #709438:709439
@@ -549,8 +549,11 @@
 
     // JPEG file format.
     if ( mimeType.upper() == QString("JPG") || mimeType.upper() == QString("JPEG") || 
-         mimeType.upper() == QString("JPE")) 
-       d->image.setAttribute("quality", iofileSettings->JPEGCompression);
+         mimeType.upper() == QString("JPE"))
+    {
+       d->image.setAttribute("quality",     iofileSettings->JPEGCompression);
+       d->image.setAttribute("subsampling", iofileSettings->JPEGSubSampling);
+    }
 
     // PNG file format.
     if ( mimeType.upper() == QString("PNG") ) 
--- branches/extragear/kde3/graphics/digikam/utilities/imageeditor/canvas/iofilesettingscontainer.h #709438:709439
@@ -43,6 +43,7 @@
     IOFileSettingsContainer()
     {
         JPEGCompression     = 75;
+        JPEGSubSampling     = 1;    // Medium subsampling
         PNGCompression      = 9;
         TIFFCompression     = false;
         JPEG2000Compression = 75;
@@ -56,6 +57,9 @@
     // JPEG quality value.
     int  JPEGCompression;
 
+    // JPEG chroma subsampling value.
+    int  JPEGSubSampling;
+
     // PNG compression value.
     int  PNGCompression;
 
--- branches/extragear/kde3/graphics/digikam/utilities/imageeditor/editor/editorwindow.cpp #709438:709439
@@ -858,6 +858,8 @@
                                                  (float)config->readNumEntry("JPEGCompression", 75)
                                                  + 26.0 - (75.0/100.0));
 
+    m_IOFileSettings->JPEGSubSampling     = config->readNumEntry("JPEGSubSampling", 1);  // Medium subsampling
+
     // PNG compression slider settings : 1 - 9 ==> libpng settings : 100 - 1.
     m_IOFileSettings->PNGCompression      = (int)(((1.0-100.0)/8.0)*
                                                  (float)config->readNumEntry("PNGCompression", 1)
--- branches/extragear/kde3/graphics/digikam/utilities/setup/setupiofiles.cpp #709438:709439
@@ -114,6 +114,7 @@
     KConfig* config = kapp->config();
     config->setGroup("ImageViewer Settings");
     config->writeEntry("JPEGCompression", d->JPEGOptions->getCompressionValue());
+    config->writeEntry("JPEGSubSampling", d->JPEGOptions->getSubSamplingValue());
     config->writeEntry("PNGCompression", d->PNGOptions->getCompressionValue());
     config->writeEntry("TIFFCompression", d->TIFFOptions->getCompression());
     config->writeEntry("JPEG2000Compression", d->JPEG2000Options->getCompressionValue());
@@ -126,6 +127,7 @@
     KConfig* config = kapp->config();
     config->setGroup("ImageViewer Settings");
     d->JPEGOptions->setCompressionValue(config->readNumEntry("JPEGCompression", 75) );
+    d->JPEGOptions->setCompressionValue( config->readNumEntry("JPEGSubSampling", 1) ); // Medium subsampling
     d->PNGOptions->setCompressionValue(config->readNumEntry("PNGCompression", 9) );
     d->TIFFOptions->setCompression(config->readBoolEntry("TIFFCompression", false));
     d->JPEG2000Options->setCompressionValue( config->readNumEntry("JPEG2000Compression", 75) );
Comment 13 Jean-Marc Liotier 2007-09-07 16:15:28 UTC
The inverse relationship between the amount of chroma subsampling and the resulting quality level could be confusing to some users. Maybe we should try to find an alternative wording to the high/medium/low options.
Comment 14 Jean-Marc Liotier 2007-09-07 16:18:17 UTC
Thanks for the patch. And the "QWhatsThis" comments probably provide enough explanations to reduce the confusion of novice users - though this will have to be tested on real novice users...
Comment 15 Arnd Baecker 2007-09-07 16:18:51 UTC
There is another interesting point in
 the http://photo.net/learn/jpeg/ article:

"Trying to improve the appearance of a Jpeg image by re-compressing at a
higher quality setting achieves very little (except an increase in
file-size)."

Therefore one should use the same quality setting on save
with which the image was created.
This is discussed further in the section "Jpegdump";
this tools allows to estimate the IJG
quality settings for any given Jpeg.

So shouldn't this be used as setting for saving?

Best, Arnd
Comment 16 Arnd Baecker 2007-09-07 16:21:00 UTC
Ad #c13: what about #c6 ?
(I hate those mid-air collisions with own postings ;-)
Comment 17 Jean-Marc Liotier 2007-09-07 16:29:28 UTC
Good point Arnd. Indeed using the input image's own subsampling setting as a default for saving it would be quite sensible : from a user's point of view, the application would just do the right thing and that would be one less option for 95% of users to worry about. I guess that this is the kind of simplification that Digikam strives for. Now from a developper's point of view this is of course qui a bit more work...
Comment 18 caulier.gilles 2007-09-07 16:36:40 UTC
SVN commit 709455 by cgilles:

digiKam from trunk (KDE4) : provide settings to adjust JPEG subsampling factor when new JPEG file is created by editor.
BUG: 149578 


 M  +42 -4     libs/dimg/loaders/jpegloader.cpp  
 M  +39 -4     libs/dimg/loaders/jpegsettings.cpp  
 M  +3 -0      libs/dimg/loaders/jpegsettings.h  
 M  +2 -1      libs/widgets/common/filesaveoptionsbox.cpp  
 M  +2 -0      project/project.kdevelop  
 M  +5 -2      utilities/imageeditor/canvas/dimginterface.cpp  
 M  +4 -0      utilities/imageeditor/canvas/iofilesettingscontainer.h  
 M  +2 -0      utilities/imageeditor/editor/editorwindow.cpp  
 M  +2 -0      utilities/setup/setupiofiles.cpp  


--- trunk/extragear/graphics/digikam/libs/dimg/loaders/jpegloader.cpp #709454:709455
@@ -532,13 +532,51 @@
     if (quality > 100)
         quality = 100;
     
+    QVariant subSamplingAttr = imageGetAttribute("subsampling");
+    int subsampling = subSamplingAttr.isValid() ? subSamplingAttr.toInt() : 1;  // Medium    
+
     jpeg_set_defaults(&cinfo);
 
-    // B.K.O #130996: set horizontal and vertical Subsampling factor to 1 for a best 
-    // quality of color picture compression. 
-    cinfo.comp_info[0].h_samp_factor = 1;
-    cinfo.comp_info[0].v_samp_factor = 1; 
+    // B.K.O #149578: set horizontal and vertical chroma subsampling factor to encoder.
+    // See this page for details: http://en.wikipedia.org/wiki/Chroma_subsampling
 
+    switch (subsampling)
+    {
+        case 1:  // 2x1, 1x1, 1x1 (4:2:2) : Medium
+        {
+            DDebug() << "Using LibJPEG medium chroma-subsampling (4:2:2)" << endl;
+            cinfo.comp_info[0].h_samp_factor = 2;
+            cinfo.comp_info[0].v_samp_factor = 1;
+            cinfo.comp_info[1].h_samp_factor = 1;
+            cinfo.comp_info[1].v_samp_factor = 1;
+            cinfo.comp_info[2].h_samp_factor = 1;
+            cinfo.comp_info[2].v_samp_factor = 1;
+            break;
+        }
+        case 2:  // 2x2, 1x1, 1x1 (4:1:1) : High
+        {
+            DDebug() << "Using LibJPEG high chroma-subsampling (4:1:1)" << endl;
+            cinfo.comp_info[0].h_samp_factor = 2;
+            cinfo.comp_info[0].v_samp_factor = 2;
+            cinfo.comp_info[1].h_samp_factor = 1;
+            cinfo.comp_info[1].v_samp_factor = 1;
+            cinfo.comp_info[2].h_samp_factor = 1;
+            cinfo.comp_info[2].v_samp_factor = 1;
+            break;
+        }
+        default:  // 1x1 1x1 1x1 (4:4:4) : None
+        {
+            DDebug() << "Using LibJPEG none chroma-subsampling (4:4:4)" << endl;
+            cinfo.comp_info[0].h_samp_factor = 1;
+            cinfo.comp_info[0].v_samp_factor = 1;
+            cinfo.comp_info[1].h_samp_factor = 1;
+            cinfo.comp_info[1].v_samp_factor = 1;
+            cinfo.comp_info[2].h_samp_factor = 1;
+            cinfo.comp_info[2].v_samp_factor = 1;
+            break;
+        }
+    }
+
     jpeg_set_quality(&cinfo, quality, true);
     jpeg_start_compress(&cinfo, true);
 
--- trunk/extragear/graphics/digikam/libs/dimg/loaders/jpegsettings.cpp #709454:709455
@@ -28,6 +28,7 @@
 #include <QLayout>
 #include <QFrame>
 #include <QGridLayout>
+#include <QComboBox>
 
 // KDE includes.
 
@@ -54,14 +55,18 @@
         labelJPEGcompression = 0;
         JPEGcompression      = 0;
         labelWarning         = 0;
+        labelSubSampling     = 0;
+        subSamplingCB        = 0;
     }
 
     QGridLayout   *JPEGGrid;
 
     QLabel        *labelJPEGcompression;
-
     QLabel        *labelWarning;
+    QLabel        *labelSubSampling;
 
+    QComboBox     *subSamplingCB;
+
     KIntNumInput  *JPEGcompression;
 };
 
@@ -96,11 +101,31 @@
     d->labelWarning->setLineWidth(1);
     d->labelWarning->setFrameShape(QFrame::Box);
 
+    d->labelSubSampling = new QLabel(i18n("Chroma subsampling:"), this);
+
+    d->subSamplingCB = new QComboBox(this);
+    d->subSamplingCB->insertItem(0, i18n("None"));    // 1x1, 1x1, 1x1 (4:4:4)
+    d->subSamplingCB->insertItem(1, i18n("Medium"));  // 2x1, 1x1, 1x1 (4:2:2)
+    d->subSamplingCB->insertItem(2, i18n("High"));    // 2x2, 1x1, 1x1 (4:1:1)
+    d->subSamplingCB->setWhatsThis( i18n("<p>The level of chroma subsampling for JPEG images:<p>"
+                                         "<b>None</b>: use 4:4:4 ratio. It does not have any chroma "
+                                         "subsampling at all. This preserves borders and contrasting "
+                                         "colors, but compression is less<p>"
+                                         "<b>Medium</b>: use 4:2:2 ratio. Medium compression; reduces "
+                                         "the bandwidth for the picture by one-third with little to "
+                                         "no visual difference<p>"
+                                         "<b>High</b>: use 4:1:1 ratio. Important compression; suits "
+                                         "images with weak borders but tends to denature colors<p>"
+                                         "<b>Note: JPEG use a lossy compression image algorithm.</b>"));
+
+
     d->JPEGGrid->addWidget(d->labelJPEGcompression, 0, 0, 1, 1);
-    d->JPEGGrid->addWidget(d->JPEGcompression, 0, 1, 1, 1);
-    d->JPEGGrid->addWidget(d->labelWarning, 0, 2, 1, 1);    
+    d->JPEGGrid->addWidget(d->JPEGcompression,      0, 1, 1, 1);
+    d->JPEGGrid->addWidget(d->labelSubSampling,     1, 0, 1, 1);    
+    d->JPEGGrid->addWidget(d->subSamplingCB,        1, 1, 1, 1);    
+    d->JPEGGrid->addWidget(d->labelWarning,         0, 2, 1, 1);    
     d->JPEGGrid->setColumnStretch(1, 10);
-    d->JPEGGrid->setRowStretch(1, 10);
+    d->JPEGGrid->setRowStretch(2, 10);
     d->JPEGGrid->setAlignment(d->JPEGcompression, Qt::AlignCenter);
     d->JPEGGrid->setMargin(KDialog::spacingHint());
     d->JPEGGrid->setSpacing(KDialog::spacingHint());
@@ -121,4 +146,14 @@
     return d->JPEGcompression->value();
 }
 
+void JPEGSettings::setSubSamplingValue(int val)
+{
+    d->subSamplingCB->setCurrentIndex(val);
+}
+
+int JPEGSettings::getSubSamplingValue()
+{
+    return d->subSamplingCB->currentIndex();
+}
+
 }  // namespace Digikam
--- trunk/extragear/graphics/digikam/libs/dimg/loaders/jpegsettings.h #709454:709455
@@ -49,6 +49,9 @@
     void setCompressionValue(int val);
     int  getCompressionValue();
 
+    void setSubSamplingValue(int val);
+    int  getSubSamplingValue();
+
 private:
 
     JPEGSettingsPriv* d;
--- trunk/extragear/graphics/digikam/libs/widgets/common/filesaveoptionsbox.cpp #709454:709455
@@ -164,6 +164,7 @@
     KSharedConfig::Ptr config = KGlobal::config();
     KConfigGroup group = config->group("ImageViewer Settings");
     group.writeEntry("JPEGCompression", d->JPEGOptions->getCompressionValue());
+    group.writeEntry("JPEGSubSampling", d->JPEGOptions->getSubSamplingValue());
     group.writeEntry("PNGCompression", d->PNGOptions->getCompressionValue());
     group.writeEntry("TIFFCompression", d->TIFFOptions->getCompression());
     group.writeEntry("JPEG2000Compression", d->JPEG2000Options->getCompressionValue());
@@ -176,6 +177,7 @@
     KSharedConfig::Ptr config = KGlobal::config();
     KConfigGroup group = config->group("ImageViewer Settings");
     d->JPEGOptions->setCompressionValue( group.readEntry("JPEGCompression", 75) );
+    d->JPEGOptions->setCompressionValue( group.readEntry("JPEGSubSampling", 1) );  // Medium subsampling
     d->PNGOptions->setCompressionValue( group.readEntry("PNGCompression", 9) );
     d->TIFFOptions->setCompression( group.readEntry("TIFFCompression", false) );
     d->JPEG2000Options->setCompressionValue( group.readEntry("JPEG2000Compression", 75) );
@@ -183,4 +185,3 @@
 }
 
 }  // namespace Digikam
-
--- trunk/extragear/graphics/digikam/project/project.kdevelop #709454:709455
@@ -158,6 +158,8 @@
     <projectname>project</projectname>
     <projectname>project</projectname>
     <projectname>project</projectname>
+    <projectname>project</projectname>
+    <projectname>project</projectname>
   </general>
   <kdevfileview>
     <groups>
--- trunk/extragear/graphics/digikam/utilities/imageeditor/canvas/dimginterface.cpp #709454:709455
@@ -3,7 +3,7 @@
  * This file is a part of digiKam project
  * http://www.digikam.org
  *
- * Date   : 2003-01-15
+ * Date        : 2003-01-15
  * Description : DImg interface for image editor
  *
  * Copyright (C) 2004-2005 by Renchi Raju <renchi@pooh.tam.uiuc.edu>
@@ -552,7 +552,10 @@
     // JPEG file format.
     if ( mimeType.toUpper() == QString("JPG") || mimeType.toUpper() == QString("JPEG") || 
          mimeType.toUpper() == QString("JPE")) 
-       d->image.setAttribute("quality", iofileSettings->JPEGCompression);
+    {
+       d->image.setAttribute("quality",     iofileSettings->JPEGCompression);
+       d->image.setAttribute("subsampling", iofileSettings->JPEGSubSampling);
+    }
 
     // PNG file format.
     if ( mimeType.toUpper() == QString("PNG") ) 
--- trunk/extragear/graphics/digikam/utilities/imageeditor/canvas/iofilesettingscontainer.h #709454:709455
@@ -43,6 +43,7 @@
     IOFileSettingsContainer()
     {
         JPEGCompression     = 75;
+        JPEGSubSampling     = 1;    // Medium subsampling
         PNGCompression      = 9;
         TIFFCompression     = false;
         JPEG2000Compression = 75;
@@ -56,6 +57,9 @@
     // JPEG quality value.
     int  JPEGCompression;
 
+    // JPEG chroma subsampling value.
+    int  JPEGSubSampling;
+
     // PNG compression value.
     int  PNGCompression;
 
--- trunk/extragear/graphics/digikam/utilities/imageeditor/editor/editorwindow.cpp #709454:709455
@@ -818,6 +818,8 @@
                                                  (float)group.readEntry("JPEGCompression", 75)
                                                  + 26.0 - (75.0/100.0));
 
+    m_IOFileSettings->JPEGSubSampling     = group.readEntry("JPEGSubSampling", 1);  // Medium subsampling
+
     // PNG compression slider settings : 1 - 9 ==> libpng settings : 100 - 1.
     m_IOFileSettings->PNGCompression      = (int)(((1.0-100.0)/8.0)*
                                                  (float)group.readEntry("PNGCompression", 1)
--- trunk/extragear/graphics/digikam/utilities/setup/setupiofiles.cpp #709454:709455
@@ -118,6 +118,7 @@
     KSharedConfig::Ptr config = KGlobal::config();
     KConfigGroup group = config->group(QString("ImageViewer Settings"));
     group.writeEntry("JPEGCompression", d->JPEGOptions->getCompressionValue());
+    group.writeEntry("JPEGSubSampling", d->JPEGOptions->getSubSamplingValue());
     group.writeEntry("PNGCompression", d->PNGOptions->getCompressionValue());
     group.writeEntry("TIFFCompression", d->TIFFOptions->getCompression());
     group.writeEntry("JPEG2000Compression", d->JPEG2000Options->getCompressionValue());
@@ -130,6 +131,7 @@
     KSharedConfig::Ptr config = KGlobal::config();
     KConfigGroup group = config->group(QString("ImageViewer Settings"));
     d->JPEGOptions->setCompressionValue(group.readEntry("JPEGCompression", 75) );
+    d->JPEGOptions->setCompressionValue(group.readEntry("JPEGSubSampling", 1) ); // Medium subsampling
     d->PNGOptions->setCompressionValue(group.readEntry("PNGCompression", 9) );
     d->TIFFOptions->setCompression(group.readEntry("TIFFCompression", false));
     d->JPEG2000Options->setCompressionValue( group.readEntry("JPEG2000Compression", 75) );
Comment 19 Arnd Baecker 2007-09-07 16:40:10 UTC
Ad #c15 and #c17: just in case, it seems that the
code downloadable at http://www.programmersheaven.com/download/17054/download.aspx
could be used as basis for the detection of the jpeg compression level.
Comment 20 caulier.gilles 2007-09-08 17:49:14 UTC
SVN commit 709928 by cgilles:

kipi-plugins RAW converter : set JPEG subsampling factor to Medium.
CCBUGS: 149578


 M  +11 -4     rawdecodingiface.cpp  


--- branches/extragear/kde3/libs/kipi-plugins/rawconverter/rawdecodingiface.cpp #709927:709928
@@ -223,10 +223,17 @@
             cinfo.input_components = 3;
             cinfo.in_color_space   = JCS_RGB;
             jpeg_set_defaults(&cinfo);
-            // B.K.O #130996: set horizontal and vertical Subsampling factor 
-            // to 1 for a best quality of color picture compression. 
-            cinfo.comp_info[0].h_samp_factor = 1;
-            cinfo.comp_info[0].v_samp_factor = 1; 
+
+            // B.K.O #149578: set encoder horizontal and vertical chroma subsampling 
+            // factor to 2x1, 1x1, 1x1 (4:2:2) : Medium subsampling.
+            // See this page for details: http://en.wikipedia.org/wiki/Chroma_subsampling 
+            cinfo.comp_info[0].h_samp_factor = 2;
+            cinfo.comp_info[0].v_samp_factor = 1;
+            cinfo.comp_info[1].h_samp_factor = 1;
+            cinfo.comp_info[1].v_samp_factor = 1;
+            cinfo.comp_info[2].h_samp_factor = 1;
+            cinfo.comp_info[2].v_samp_factor = 1;
+
             jpeg_set_quality(&cinfo, 100, true);
             jpeg_start_compress(&cinfo, true);
 
Comment 21 caulier.gilles 2007-09-08 17:52:21 UTC
Jean marc,

Do you know another important settings to adjust image quality with JPEG encoder which is missing in digiKam ? If yes, please open a new file in this room...

Thanks in advance

Gilles